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