Tags:NodeTypeScriptkoa2

Category: Back End

将 koa2/nodejs 项目 使用 typescript + koa + graphql 重构(历险记)

  • 为了尽快适应 typescript 开发, 所以决定将个人网站的后台使用 ts 重构

项目补充安装 koa ts graphql 相关依赖

  • devDependencies 都是一些 koa 的类型描述文件,方便 ts 类型解析
  "dependencies": {
    "@koa/cors": "^3.0.0",
    "@types/graphql": "^14.5.0",
    "axios": "^0.19.0",
    "graphql": "^14.6.0",
    "graphql-tools": "^4.0.7", // 这个很有用 合并多个graphql的schma
    "koa": "^2.10.0",
    "koa-bodyparser": "^4.2.1",
    "koa-graphql": "^0.8.0", // graph 在koa搭建的依赖
    "koa-mount": "^4.0.0", // 这个已经废了等一下就删除
    "koa-router": "^7.4.0",
    "mongoose": "^5.7.5"
  },
  "devDependencies": {
    "@types/koa": "^2.11.1",
    "@types/koa-bodyparser": "^4.3.0",
    "@types/koa-graphql": "^0.8.3",
    "@types/koa-mount": "^4.0.0",
    "@types/koa-router": "^7.4.0",
    "@types/koa__cors": "^3.0.1",
    "@types/mongoose": "^5.7.1",
    "@types/node": "^13.7.1",
    "ts-node": "^8.6.2", // 本地运行ts 代码的运行时依赖
    "typescript": "^3.7.5"
  }
  • 因为 nodemon 已经全局安装了 本项目就不安装了

typescript 项目初始化

tsc --init
// 会多出一个tsconfig.json

为什么不用 apollo-server-koa?

  • 因为迁移很多 restful api 到 graph 都没有测试
  • 目前服务大多数还是 restful, 所以不想让 graphql 主导整个后端服务所以将 graphql 当作一下子服务先把

开始

  • 修改所有.js 文件成 ts, 并且加上类型校验(略...) 很累
  • 加入 graphql 服务, 在启动项目入口文件加入
import Koa from 'koa'
import cors from '@koa/cors'
import bodyParser from 'koa-bodyparser'
import { router } from './router' // 其他restful 路由都在这里面 是一个koa-router 实例对象
import graphqlHTTP from 'koa-graphql' // graphql创建http服务
import { allSchema } from './gqlService/rootSchema' // 所有gql好的schema,参数注入gqlHttp服务
const app = new Koa()


// koa-router@7+ 使用router.all 方法确定可以获取到ctx.req.body
// 前端可以在post里面传参数
router.all(
  '/graphql',
  graphqlHTTP(ctx => ({
    schema: allSchema,
    context: ctx, //从koa获取到的上下文,当前端需要使用post body时从这里获取
    graphiql: true // 运行graphql 前端playground 图形化请求界面 生产环境关闭它
  }))
)

app.use(cors())
app.use(bodyParser())
app.use(router.routes())

实现服务端 gql Schema

  • Schema 是什么, 它定义了数据模型的解构、字段的类型, 模型间的关系
  • 在 graphqlHTTP 服务里面需要传入一个 schema, 是所有服务器上 gql 数据的关于 Query 和 Mutaion 属性的对象
const schema = new GraphQLSchema({
    query: // ..定义所有关于数据的请求的集合 类型为GraphQLObjectType
    mutation: //... 定义所有关于数据突变的集合 类型也是GraphQLObjectType
})
  • GraphQLObjectType gql 自定义创建类型, 有了他就可以轻松随便的定义你想要的数据结构以及对数据的处理
const HelloType = new GraphQLObjectType({
    name: 'Hello', // 名字
    description: 'hello gql', // 描述
    fields: () => ({ // 自定义类型下的字段
        // 客户端可以传入参数
        id: { type: GraphQLNonNull(GraphQLInt) }
        msg: {
            type: msgType, // msgType 是另一个自定义类型
            // args 客户端传递过来的参数对象
            resolve: (args) => {
                return await DBModel.find(msgModel => msgModel.id === args.id)
            }
        }
    })
})
  • 问题当有很多个不同的数据模型怎么样合并到一起,然后再传入 Schema 中?
  • 使用 graphql-tools 合并所有的自定义 Schema
  • graph 后端实现的方式有很多使用 graphqlHTTP 的方式, graphql-tools 合并 schema 是现在发现的最好方法
  • 另外的 graphql 后端实现方法是 定义 typeDefs 以及 resovlers + merge-graphql-schemas, 现在不要用力过猛,只用最简单的方式打到目的
import { mergeSchemas } from 'graphql-tools'
import { blogSchema } from './blogSchema'
import { commentSchema } from './commentSchema'
// 另一个合并所有schema的插件merge-graphql-schemas
const allSchema = mergeSchemas({
  schemas: [blogSchema, commentSchema]
})
export { allSchema }
// 每一个单一Schema 都是这样的结构
const schema = new GraphQLSchema({
    query: // ..定义所有关于数据的请求的集合 类型为GraphQLObjectType
    mutation: //... 定义所有关于数据突变的集合 类型也是GraphQLObjectType
})
  • 2020/02/23 待续