常规情况下的GraphQL
GraphQL 是一个用于API的查询语言与运行时环境,它使得客户端可以精确地请求数据,并仅仅获取需要的内容,从而提高数据请求效率。通常情况下,在前端开发中,GraphQL 会配合 Apollo 客户端使用,可以快速开发出强大而又灵活的应用程序。
在使用GraphQL时,我们通常会定义自己的Schema、Resolver以及Types等一系列相关的类型。其中,Schema 定义的是数据的结构,而 Types(包括Query、Mutation等)则是针对这个结构的操作。在对这些类型进行定义时,我们需要在其中添加对应的数据类型和变量。
GraphQL 与 TypeScript 结合
虽然GraphQL和Apollo的配合在开发中已经显得越来越流行,不过当应用切换到 TypeScript 开发时,我们会发现在这种架构下使用这种组件变得不那么直观、不那么明显,需要更多的类型定义和较多的类型检查等等。因此,开发者也提出了自己独特的解决方案,那就是 TypeScript 与 GraphQL 深度结合,让我们来具体了解一下。
首先,让我们来看一下GraphQL的一个Query:
query GET_USER_INFO($id: ID!) { user(id: $id) { name age } }
上述代码表示我们希望从服务端获取用户信息,需要给出该用户的 ID,从而得到对应的名字和年龄等信息。但是在写这样的Query时,我们会发现变量的类型并没有被声明,只有在构建使用它的代码时才会发现。
如果我们再使用 TypeScript 的话,则可以通过声明类型的方式,不仅使得变量的代码层面更加强健,还能够将GraphQL的定义和现实代码结合更加紧密。下面就让我们来看下面的 TypeScript 代码实现这个Query:
// javascriptcn.com 代码示例 import { gql } from 'apollo-server'; interface User { name: string; age: number; } interface GetUserQuery { user: User; } async function getUserInfo(id: number): GetUserQuery { const response = await fetch('/graphql', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: gql` query GET_USER_INFO($id: ID!) { user(id: $id) { name age } } `, variables: { id } }) }); const result = await response.json(); return result; }
上面的代码中,我们使用了 interface 来声明类型,通过这种方式来对Query中所需的数据类型进行声明。在接下来的函数 getUserInfo 的实现中,还需要额外处理哪些字段是必要的、哪些是可选的等问题,这样做也可以有效避免由于开发疏忽所产生的不必要错误。
案例
下面让我们来看一个具体的案例吧。
在这个案例中,我们需要开发一个支持登录和注册的前后端分离的应用程序。前端通过GraphQL来请求后端数据,并将相关数据展示给用户。其中,前端应用采用React进行开发,使用TypeScript来支持GraphQL服务。启动方式为,进入 src
目录后,运行 npm run start
即可。
后端服务
提供GraphQL API,代码如下:
// javascriptcn.com 代码示例 import { ApolloServer, gql } from 'apollo-server'; import bcrypt from 'bcryptjs'; import { createToken } from './utils/token'; interface User { id: number; username: string; password: string; } interface LoginMutationReturn { token: string; user: Pick<User, 'id' | 'username'>; } interface RegisterMutationReturn { user: Pick<User, 'id' | 'username'>; } let idCount = 0; let users: User[] = [ { id: ++idCount, username: 'admin', password: bcrypt.hashSync('admin', 10) } ]; const typeDefs = gql` type Query { userInfo(id: ID!): User } type Mutation { login(username: String!, password: String!): LoginMutationReturn register(username: String!, password: String!): RegisterMutationReturn } type User { id: ID! username: String! } type Token { token: String! } type LoginMutationReturn { token: String! user: User } type RegisterMutationReturn { user: User } `; const resolvers = { Query: { userInfo: (parent: any, args: any) => { const { id } = args; const user = users.find(user => user.id === id); return user; } }, Mutation: { login: (parent: any, args: any) => { const { username, password } = args; const user = users.find(user => user.username === username); if (!user) { throw new Error('No user found'); } const isCorrectPassword = bcrypt.compareSync(password, user.password); if (!isCorrectPassword) { throw new Error('Incorrect password'); } const token = createToken(user.id); return { token, user }; }, register: (parent: any, args: any) => { const { username, password } = args; const user = users.find(user => user.username === username); if (user) { throw new Error('User already exists'); } const hashedPassword = bcrypt.hashSync(password, 10); const newUser = { id: ++idCount, username, password: hashedPassword }; users.push(newUser); return { user: newUser }; } } }; const server = new ApolloServer({ typeDefs, resolvers }); server.listen().then(({ url }) => { console.log(`Server ready at ${url}`); });
前端代码
然后,我们需要为前端代码添加几个必要的包。可以通过以下命令来添加:
npm install --save graphql apollo-boost apollo-link-http apollo-client graphql-tag
该程序需要我们在 React 项目中启动 Apollo Client 来使得前端可以访问 GraphQL API。而在使用 Apollo Client 时,我们需要先创建这个客户端,然后将其传递给其中的其他组件,使得这些组件可以通过这个 API 来请求数据。
代码如下:
// javascriptcn.com 代码示例 import React, { useState } from 'react'; import gql from 'graphql-tag'; import { useMutation, useQuery } from '@apollo/react-hooks'; interface LoginMutationVariables { username: string; password: string; } interface LoginMutationResult { login: { token: string; user: { id: number; username: string; } } } interface UserInfoQueryVariables { id: number; } interface UserInfoQueryResult { userInfo: { username: string; } } const LOGIN = gql` mutation LoginMutation($username: String!, $password: String!) { login(username: $username, password: $password) { token user { id username } } } `; const USER_INFO = gql` query UserInfoQuery($id: ID!) { userInfo(id: $id) { username } } `; function Login() { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [login] = useMutation<LoginMutationResult, LoginMutationVariables>(LOGIN, { onCompleted: data => { localStorage.setItem('token', data.login.token); localStorage.setItem('userId', data.login.user.id.toString()); window.location.href = '/'; } }); const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => { event.preventDefault(); login({ variables: { username, password } }); }; return ( <form onSubmit={handleSubmit}> <label> Username: <input type="text" value={username} onChange={e => setUsername(e.target.value)} /> </label> <br /> <label> Password: <input type="password" value={password} onChange={e => setPassword(e.target.value)} /> </label> <br /> <button type="submit">Submit</button> </form> ); } function UserInfo() { const userId = localStorage.getItem('userId'); const { loading, error, data } = useQuery<UserInfoQueryResult, UserInfoQueryVariables>(USER_INFO, { variables: { id: Number.parseInt(userId || '') } }); if (loading) return <p>Loading...</p>; if (error) return <p>Error :(</p>; return <p>Hello, {data?.userInfo?.username}</p>; } function Home() { const token = localStorage.getItem('token'); return token ? <UserInfo /> : <Login />; } export default Home;
运行结果
启动后端服务,并运行前端应用。首先,我们需要在浏览器中输入 http://localhost:3000
,然后就可以通过登录或者注册操作来获取一个Token。这个 Token 可以用于后续的持久化登录操作。
接着,在获取到Token后,通过在 http header 中设置该 Token 的值来实现持久化登录,这样,用户在进行资料查询时,可以实现自动登录的效果。
总结
通过对以上代码理解和实践,相信你已经了解了 GraphQL 和 TypeScript 结合的过程。如果你正在开发一个大型的应用程序,这些实践将非常有用,因为它们使得您能够更好地管理和维护代码,并确保您的代码不会产生意外的错误。
当然,本文所给出的示例只是冰山一角,更多详细信息可以上官网获取:https://www.graphql.com/learn/type-system/。
希望本文能够帮助到您!
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/654df0a67d4982a6eb74c332