两篇文章学完了GraphQL(基础篇, 原理篇),接下去即是实践的过程,这个实践咱们使用了以下技术栈去实现一套任务管理系统,源码就不公开了, 等稳定后再发布。效果以下:css
使用的技术栈有:前端
以下图:react
分为两大部分:client
和server
,其中client
的目录结构以下:git
各个目录的解释在图中已经体现。github
server
端的目录结构以下:web
各个目录的含义的解释在图中已经体现。typescript
由于咱们主要是讲graphql的应用,因此其他的细节忽略不说。至于Nest框架的使用,请参考文档Nest.js数据库
实践GraphQL咱们不会直接用graphql-js,而是使用功能更加丰富、社区支持更多apollo-graphql
。其文档编写的也是很详尽,基本上全部的问题均可以在文档上找到答案。推荐新手能够先按照Get started来入手编程
由于咱们使用了apollo-boost
,因此在前端入口文件上,要拿这个包进行一些初始化,获得apolloClient的实例(无关的代码已经去掉):json
import React from 'react';
import ReactDOM from 'react-dom';
import { ApolloProvider } from 'react-apollo';
import ApolloClient from 'apollo-boost'
... ...
const GW_BASE_URL = process.env.NODE_ENV === 'production' ? '/graphql' : 'http://127.0.0.1:8888/graphql'
const client = new ApolloClient({
uri: GW_BASE_URL,
// 须要设置这个,这样每次请求的时候认证信息才会带上
fetchOptions: {
credentials: 'include',
},
// 缓存读取配置
clientState: {
typeDefs,
resolvers,
},
// 设置这个是为了配合jwt
request: async (operation) => {
// get the authentication token from local storage if it exists
const token = localStorage.getItem('token');
operation.setContext({
headers: {
authorization: token ? `Bearer ${token}` : '',
Origin: location.href,
},
});
},
// 设置全局错误处理信息,这样就不用每一个请求都进行error处理
onError: (errObj) => {
if (errObj.graphQLErrors) {
const errorMsg = errObj.graphQLErrors[0].message;
if (errorMsg === '身份信息已过时,请从新登陆') {
... ...
message.info(errorMsg, 3, () => location.hash = '#/user/login');
} else if (errorMsg && (errorMsg as any).statusCode === 403) {
message.error('权限不足,请不要重试');
} else {
message.error(errorMsg);
}
}
},
});
ReactDOM.render(
<ApolloProvider client={client}>
<LocaleProvider>
<App />
</LocaleProvider>
</ApolloProvider>,
document.getElementById('root'),
);
复制代码
每一个页面都会新建三个文件:
graphql.ts
index.scss
index.tsx
复制代码
其中graphql.ts
定义了客户端的请求,好比:
import gql from 'graphql-tag';
// 用来查询全部的用户
export const QUERY_USERS = gql`
query {
userList {
id
roles
team
mobile
staffCode
email
username
}
}
`;
复制代码
然后在index.tsx
文件中就可使用这个查询语句,以下:
整个流程是很清晰的,由于使用了typescript,因此在客户端能够引用到服务端定义的返回类型,从而提升了代码编写的速度。
react-apollo
目前发现了个bug,若是我返回的数据的层级太深,好比达到了4层以上,数据更新到缓存的时候便会出错。由于服务端使用了Nest.js,因此没有直接用apollo提供的服务器,而是用了Nest框架封装出来适用于Nest框架的graphql包graphql。该包仍是提供了不少功能的。
在app.modules.ts
中,咱们要去初始化graphql模块(无关代码已忽略):
@Module({
imports: [
GraphQLModule.forRoot({
// 指定服务端schema存放的位置
typePaths: ['graphql/schema.graphql'],
// 配置了该选项,能够自动根据代码生成schema
autoSchemaFile: path.join(__dirname, 'graphql/schema.graphql'),
buildSchemaOptions: {
},
// 能够自动生成types文件
// definitions: {
// path: path.join(__dirname, 'types/graphql.ts'),
// },
debug: true,
playground: true,
context: ({ req }) => ({ req }), // 必定要这里设置req到上下文中,不然在guard中是拿不到这个req参数的
}),
],
controllers: [],
providers: []
})
复制代码
每一个业务目录都会存在这么些文件:
咱们在dto
目录下定义三种类型文件:xx.args.ts
/xx.input.ts
/xx.model.ts
,分别定义下面三种状况
然后在xx.resolver.ts
中实现resolve函数,借助于修饰器,好比:
import { Query, Resolver, Args, Mutation } from '@nestjs/graphql'
@Resolver('User')
export class UserResolver {
@Query(returns => [UserItem])
... ...
async userList(): Promise<UserItem[]>{
return this.userService.getUserList();
}
}
复制代码
UserItem
在这里(user.model.ts
)定义:
import { ObjectType, Field, ID, registerEnumType, Int } from 'type-graphql'
... ...
@ObjectType()
export class UserItem {
@Field(type => ID, {nullable: true})
_id?: number;
@Field({nullable: true})
username?: string;
@Field({nullable: true})
email?: string;
... ...
}
复制代码
如此便完成了整个服务端数据流的过程。看着是否是很easy啊?
至此,三篇关于GraphQL的文章到此结束了,花了不少时间断断续续地学习,但愿能够给你们呈现一份不同地文章,供你们思考。后续我所在的公司网关团队会持续实践GraphQL,争取贡献出更多的解决方案。