graphql愈来愈流行,一直想把个人凝胶项目除了支持restful api外,也能同时支持graphql。因为该项目的特色是结合关系数据库的优势,尽可能少写重复或雷同的代码。对于rest api,在作完数据库设计后,百分之六十到八十的接口就已经完成了,但还须要配置上api文档。而基于数据库表自动实现graphql,感受仍是有难度的,但若能作好,连文档也就同时提供了。node
不久前又看到了一句让我深觉得然的话:No program is perfect, even the most talented engineers will write a bug or two (or three). By far the best design pattern available is simply writing less code. That’s the opportunity we have today, to accomplish our goals by doing less. git
so, ready go...github
我须要在koa2上接入graphql,通过查阅资料,最后聚焦在下面两个库上:数据库
开始是考虑简单为上,试着用kao-graphql,做为中间件能够方便的接入,我指定了/gql路由,能够测试效果,代码以下:api
import * as Router from 'koa-router' import BaseDao from '../db/baseDao' import { GraphQLString, GraphQLObjectType, GraphQLSchema, GraphQLList, GraphQLInt } from 'graphql' const graphqlHTTP = require('koa-graphql') let router = new Router() export default (() => { let authorType = new GraphQLObjectType({ name: 'Author', fields: { id: { type: GraphQLInt}, name: { type: GraphQLString} } }) let bookType = new GraphQLObjectType({ name: 'Book', fields: { id: { type: GraphQLInt}, title: { type: GraphQLString}, author: { type: authorType, resolve: async (book, args) => { let rs = await new BaseDao('author').retrieve({id: book.author_id}) return rs.data[0] } } } }) let queryType = new GraphQLObjectType({ name: 'Query', fields: { books: { type: new GraphQLList(bookType), args: { id: { type: GraphQLString }, search: { type: GraphQLString }, title: { type: GraphQLString }, }, resolve: async function (_, args) { let rs = await new BaseDao('book').retrieve(args) return rs.data } }, authors: { type: new GraphQLList(authorType), args: { id: { type: GraphQLString }, search: { type: GraphQLString }, name: { type: GraphQLString }, }, resolve: async function (_, args) { let rs = await new BaseDao('author').retrieve(args) return rs.data } } } }) let schema = new GraphQLSchema({ query: queryType }) return router.all('/gql', graphqlHTTP({ schema: schema, graphiql: true })) })()
这种方式有个问题,前面的变量对象中要引入后面定义的变量对象会出问题,所以投入了apollo-server。但apollo-server 2.0网上资料少,大可能是介绍1.0的,而2.0变更又比较大,所以折腾了一段时间,仍是要多看英文资料。
apollo-server 2.0集成不少东西到里面,包括cors,bodyParse,graphql-tools 等。
经过中间件加载,放到rest路由以前,加入顺序及方式请看app.ts,apollo-server-kao接入代码:promise
//自动生成数据库表的基础schema,并合并了手写的业务模块 import { getInfoFromSql } from './schema_generate' const { ApolloServer } = require('apollo-server-koa') export default async (app) => { //app是koa实例 let { typeDefs, resolvers } = await getInfoFromSql() //数据库查询是异步的,因此导出的是promise函数 if (!G.ApolloServer) { G.ApolloServer = new ApolloServer({ typeDefs, //已经不须要graphql-tools,ApolloServer构造函数已经集成其功能 resolvers, context: ({ ctx }) => ({ //传递ctx等信息,主要供认证、受权使用 ...ctx, ...app.context }) }) } G.ApolloServer.applyMiddleware({ app }) }
静态schema试验,schema_generate.ts浏览器
const typeDefs = ` type Author { id: Int! name: String books: [book] } type Book { id: Int! title: String author: Author } # the schema allows the following query: type Query { books: [Post] author(id: Int!): Author } ` const resolvers = { Query: { books: async function (_, args) { let rs = await new BaseDao('book').retrieve(args) return rs.data }, author: async function (_, { id }) { let rs = await new BaseDao('author').retrieve({id}) return rs.data[0] }, }, Author: { books: async function (author) { let rs = await new BaseDao('book').retrieve({ author_id: author.id }) return rs.data }, }, Book: { author: async function (book) { let rs = await new BaseDao('author').retrieve({ id: book.author_id }) return rs.data[0] }, }, } export { typeDefs, resolvers }
https://github.com/zhoutk/gels
git clone https://github.com/zhoutk/gels cd gels yarn tsc -w nodemon dist/index.js
而后就能够用浏览器打开连接:http://localhost:5000/graphql 查看效果了。restful
这是第一部分,肯定需求,进行了技术选型,实现了接入静态手写schema试验,下篇将实现动态生成与合并特殊业务模型。app