第十期基于 ApolloKoa 搭建 GraphQL 服务端
Posted 水煮前端
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第十期基于 ApolloKoa 搭建 GraphQL 服务端相关的知识,希望对你有一定的参考价值。
本文预期读者对 NodeJS、Koa 有一定的了解
GraphQL 解决了什么问题
过去,服务端的研发人员设计一个数据接口,通常并不会要求使用接口的客户端研发人员知道接口内部的数据结构,而是只提供一份 api 文档(使用说明书),文档内容介绍如何调用 API,返回什么数据,文档和功能都实现后,就算完成服务端工作了。
我们使用这个工作方式工作,慢慢地发现了一些问题:
•专门写 API 文档成了一种负担•API 文档和 API 服务经常部署在不同的域名,我们需要记住文档在哪•我们总是发现 API 的实际行为和文档不一致•API 内部数据的枚举值,总是泄露到客户端•API 参数校验工作在客户端和服务器重复进行•我们很难看到整个应用数据的结构图•我们不得不维护多个 API 版本
慢慢地,我们发现,在服务端和客户端之间,需要共享一份数据描述规范:
•这份数据描述就是功能的一部分(注意,它不是注释),它参与实现 API 功能。•这份数据描述本身就是文档,我们不在需要专门写文档,更不需要专门去部署文档服务。•当我们修改了数据描述细节,API 功能就会发生变化,我们无需担心文档和行为不一致的问题。•数据描述本身支持枚举类型,限制枚举值泄露的问题。•数据描述本身有类型系统,我们无需在客户端和服务器重复做参数校验工作。•数据描述本身就是整个应用数据的结构图。•数据描述能屏蔽版本维护的问题。
GraphQL 就是这么一种数据描述规范。
什么是 GraphQL
官网的介绍如下:
GraphQL 是一个用于 API 的查询语言,是一个使用基于类型系统来执行查询的服务端运行时(类型系统由你的数据定义)。
下面是一个基于 GraphQL 的数据描述:
type Query {
book: Book
}
enum BookStatus {
DELETED
NORMAL
}
type Book {
id: ID
name: String
price: Float
status: BookStatus
}
为了不和特定的平台绑定,且更容易理解服务端的功能,GraphQL 实现了一个易读的 schema 语法: Schema Definition Language
(SDL
)
SDL
用于表示 schema 中可用的类型,以及这些类型之间的关系
SDL
必须以字符串的形式存储
SDL
规定了三类入口,分别为:
•Query
用于定义读
操作 (可以理解为 CURD
中的 R
)•Mutation
用于定义写
操作 (可以理解为 CURD
中的 CUD
)•Subscription
用于定义长链接(基于事件的、创建和维持与服务端实时连接的方式)
通过上述代码,我们声明了一个查询,名为 book
,类型为 Book
类型 Book
拥有四个字段,分别为:
•id, 代表每本书的唯一id, 类型为 ID•name, 代表每本书的名字, 类型为字符串•price, 代表每本书的价格, 类型为浮点数•status, 代表每本书的状态, 类型为 BookStatus
BookStatus
是一个枚举类型,包含:
•DELETED
代表书已经下架,它的值为 0
•NORMAL
代表书在正常销售中, 它的值为 1
除了可以定义自己的数据类型外,GraphQL 还内置了一下几种基础类型(标量):
•Int
: 有符号 32 位整型•Float
: 有符号双精度浮点型•String
: UTF-8 字符序列•Boolean
: true 或 false•ID
: 一个唯一的标识,经常用于重新获取一个对象或作为缓存的 key
这里需要注意,GraphQL 要求端点字段的类型必须是标量类型。(这里的端点可以理解为叶子节点)
关于 GraphQL
的更多信息,请参考:https://graphql.cn/learn/
什么是 Apollo
官网的介绍如下:
Apollo 是 GraphQL 的一个实现,可帮助您管理从云到 UI 的数据。它可以逐步采用,并在现有服务上进行分层,包括 REST API 和数据库。Apollo 包括两组用于客户端和服务器的开源库,以及开发人员工具,它提供了在生产中可靠地运行 GraphQL API 所需的一切。
我们可以将 Apollo
看作是一组工具,它分为两大类,一类面向服务端,另一类面向客户端。
其中,面向客户端的 Apollo Client
涵盖了以下工具和平台:
•React + React Native•Angular•Vue•Meteor•Ember•ios (Swift)•android (Java)•...
面向服务端的 Apollo Server
涵盖了以下平台:
•Java•Scala•Ruby•Elixir•NodeJS•...
我们在本文中会使用 Apollo
中针对 NodeJS
服务端 koa
框架的 apollo-server-koa
库
关于 apollo server 和 apollo-server-koa 的更多信息请参考:
•https://www.apollographql.com/docs/apollo-server/•https://github.com/apollographql/apollo-server/tree/master/packages/apollo-server-koa
搭建 GraphQL 后端 api 服务
快速搭建
step1:
新建一个文件夹,我这里新建了 graphql-server-demo 文件夹
mkdir graphql-server-dem
在文件夹内初始化项目:
cd graphql-server-demo && yarn init
安装依赖:
yarn add koa graphql apollo-server-koa
step2:
新建 index.js 文件,并在其中撰写如下代码:
const path = require('path')
const Koa = require('koa')
const app = new Koa()
const { ApolloServer, gql } = require('apollo-server-koa')
/**
* 在 typeDefs 里定义 GraphQL Schema
*
* 例如:我们定义了一个查询,名为 book,类型是字符串
*/
const typeDefs = gql`
type Query {
book: Book
hello: String
}
enum BookStatus {
DELETED
NORMAL
}
type Book {
id: ID
name: String
price: Float
status: BookStatus
}
`;
const BookStatus = {
DELETED: 0,
NORMAL: 1
}
/**
* 在这里定义对应的解析器
*
* 例如:
* 针对查询 hello, 定义同名的解析器函数,返回字符串 "hello world!"
* 针对查询 book,定义同名的解析器函数,返回预先定义好的对象(实际场景可能返回来自数据库或其他接口的数据)
*/
const resolvers = {
// Apollo Server 允许我们将实际的枚举映射挂载到 resolvers 中(这些映射关系通常维护在服务端的配置文件或数据库中)
// 任何对于此枚举的数据交换,都会自动将枚举值替换为枚举名,避免了枚举值泄露到客户端的问题
BookStatus,
Query: {
hello: () => 'hello world!',
book: (parent, args, context, info) => ({
name:'地球往事',
price: 66.3,
status: BookStatus.NORMAL
})
}
};
// 通过 schema、解析器、 Apollo Server 的构造函数,创建一个 server 实例
const server = new ApolloServer({ typeDefs, resolvers })
// 将 server 实例以中间件的形式挂载到 app 上
server.applyMiddleware({ app })
// 启动 web 服务
app.listen({ port: 4000 }, () =>
console.log(`
以上是关于第十期基于 ApolloKoa 搭建 GraphQL 服务端的主要内容,如果未能解决你的问题,请参考以下文章
华为云PB级数据库GaussDB(for Redis)揭秘第十期:GaussDB(for Redis)迁移系列(上)