GraphQL服务器的结构和实现(第二部分)
在上一篇文章中,我们通过学习GraphQL schema及其在执行查询和变异时的基本作用,了解了很多基础的GraphQL服务器内部工作原理。
在我们了解GraphQL服务器如何使用GraphQL引擎执行这些操作的同时,我们还没有涉及实际的客户端与服务器之间的通信方面:有关如何通过网络传输查询及其响应的问题。 这就是本文的主题!
GraphQL服务器可以用您喜欢的任何编程语言来实现。 本文重点介绍JavaScript和一些可用的库,这些库可以帮助您构建服务器,尤其是
Express-graphql,apollo-server和graphql-yoga。
通过HTTP服务GraphQL
GraphQL与传输层无关
了解GraphQL的关键在于,它实际上与通过网络传输数据的方式无关。 这意味着GraphQL服务器可能可以基于HTTP以外的协议(例如WebSockets或较低级别的TCP)工作。 但是,本文重点介绍当今实现GraphQL服务器的最常见方法,该方法确实是基于HTTP。
Express.js作为强大而灵活的基础
以下部分主要介绍Express.js及其用于GraphQL库(例如
express-graphql和apollo-server)的中间件的概念。 如果您已经熟悉Express,可以跳到下一部分。
【译注】上图截止时间为2020年1月31日。
npm趋势上 express, hapi, koa and sail的比较
Express.js是迄今为止最受欢迎的JavaScript Web框架。它的简洁性,灵活性和性能使其十分耀眼。
开始体验你自己的服务器只需要如下代码:
const express = require('express')
const app = express()
// respond with "hello world" when a GET request is received
app.get('/', function(req, res) {
res.send('<h1>Hello World</h1>')
})
app.listen(3000)
使用Node.js执行此脚本后,您可以在浏览器地址栏中输入http://localhost:3000访问网站:
您可以轻松地向服务器API添加更多终结点(也称为路由):
app.get('/goodbye', function(req, res) {
res.send('<h1>Goodbye</h1>')
})
或使用其他HTTP方法,例如POST而不是GET:
app.post('/', function(req, res) {
res.send('<h1>You just made a POST request</h1>')
})
Express在实现服务器方面提供了极大的灵活性,使您可以使用中间件的概念轻松添加功能。
Express灵活性和模块化的关键:中间件
中间件允许在处理请求时或在返回响应之前,拦截传入的请求并执行特定的任务。
本质上,中间件不过是带有三个参数的函数:
req:客户端的传入请求res:返回给客户端的响应next:调用下一个中间件的函数
由于中间件函数对传入的请求对象和传出的响应对象有(写入)访问权限,因此它是一个非常强大的概念,可以根据特定目的调整请求和响应。
中间件的用例有许多,例如身份验证,缓存,数据转换和验证,自定义业务逻辑的执行等等。 这里有一个简单的日志记录示例,它将打印接收请求的时间:
function loggingMiddleware(req, res, next) {
console.log(`Received a request at: ${Date.now()}`)
next()
}
app.use(loggingMiddleware)
通过这种中间件方式获得的灵活性被诸如graphql-express,apollo-server或graphql-yoga之类的框架所利用,这些框架均基于Express!
Express 与 GraphQL
通过上篇文章中我们学习到的有关graphql函数和GraphQL执行引擎的所有内容,我们已经可以预期基于Express的GraphQL服务器如何工作。
Express提供了处理HTTP请求所需的一切,而GraphQL.js提供了解决查询的功能,我们仍然需要的是它们之间的粘合剂。
这种胶水是由Express-graphql和apollo-server之类的库提供的,这些库不过是Express的中间件函数!
GraphQL中间件将HTTP和GraphQL.js粘合在一起
express-graphql:Facebook的GraphQL中间件版本
express-graphql是Facebook的GraphQL中间件版本,可与Express和GraphQL.js一起使用。 如果看一下它的源代码,您会注意到它的核心功能仅在几行代码中实现。
真的,它的主要职责有两部分:
- 确保传入的POST请求正文中包含的GraphQL查询(或变异)可以由GraphQL.js执行。 因此,它需要解析查询并将其转发到graphql函数以执行。
- 将执行结果附加到响应对象,以便可以将其返回给客户端。
使用express-graphql,您可以按如下快速开始GraphQL服务器开发:
const express = require('express')
const graphqlHTTP = require('express-graphql')
const { GraphQLSchema, GraphQLObjectType, GraphQLString } = require('graphql')
const app = express()
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
hello: {
type: GraphQLString,
resolve: (root, args, context, info) => {
return 'Hello World'
}
}
}
})
app.use('/graphql', graphqlHTTP({
schema,
graphiql: true // enable GraphiQL
}))
app.listen(4000)
使用Node.js执行这段代码会在
http://localhost:4000/graphql启动一个GraphQL服务器。
如果您已经阅读了有关GraphQL schema的上一篇文章,那么您将很好地理解第7至18行的用途:我们构建了一个GraphQLSchema,可以执行以下查询:
query {
hello
} # responds: { "data": { "hello": "Hello World" } }
不过,这段代码段新的部分是集成了网络层。 这次我们不是设置内联查询并直接使用GraphQL.js(如此处所示)直接执行查询,而是设置服务器等待传入的查询,然后再针对GraphQLSchema执行该查询。
您真的不需要很多的准备工作就可以开始在服务端使用GraphQL。
apollo-server: 在Express生态系统之外具有更好的兼容性
从本质上讲,apollo-server与express-graphql非常相似,只是有一些细微的差异。 两者之间的主要区别在于,apollo-server还允许与许多其他框架(例如koa和hapi)以及AWS Lambda或Azure Functions等FaaS提供集成。可以通过在软件包名称后附加相应的后缀来安装各个集成,例如: apollo-server-express, apollo-server-koa 或 apollo-server-lambda.
然而,从根本上讲,它仍然是一个中间件,将HTTP层与GraphQL.js提供的GraphQL引擎桥接在一起。 这是上述基于express-graphql的示例的等效实现在apollo-server-express中的样子:
const express = require('express')
const bodyParser = require('body-parser')
const { graphqlExpress, graphiqlExpress } = require('apollo-server-express')
const { GraphQLSchema, GraphQLObjectType, GraphQLString } = require('graphql')
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
hello: {
type: GraphQLString,
resolve: (root, args, context, info) => {
return 'Hello World'
},
},
},
}),
})
const app = express()
app.use('/graphql', bodyParser.json(), graphqlExpress({ schema }))
app.get('/graphiql', graphiqlExpress({ endpointURL: '/graphql' })) // enable GraphiQL
app.listen(4000)
graphql-yoga: 构建GraphQL服务器的最简单方法
消除构建GraphQL服务器时的阻力
即使在使用express-graphql或apollo-server时,也存在许多阻碍:
- 需要安装多个依赖
- 假设具备Express的先验知识
- 使用GraphQL订阅时需要复杂的设置
可以通过graphql-yoga(一个用于构建GraphQL服务器的简单库)消除这种阻力。 它本质上是Express,apollo-server和其他一些库之上的便利层,以提供创建GraphQL服务器的快速方法。 (可以将其视为类似于GraphQL服务器的create-react-app。)
下面是我们在express-graphql和apollo-server中看到的同样的GraphQL服务器代码:
const { GraphQLServer } = require('graphql-yoga')
const typeDefs = `
type Query {
hello: String!
}
`
const resolvers = {
Query: {
hello: (root, args, context, info) => 'Hello World',
},
}
const server = new GraphQLServer({ typeDefs, resolvers })
server.start() // defaults to port 4000
注意这里既可以使用GraphQLSchema的现成实例来实例化GraphQLServer,也可以使用快捷的接口(基于来自graphql-tools的makeExecutableSchema)来实例化GraphQLServer,如上面的代码段所示。
内置对GraphQL游乐场,订阅和跟踪的支持
graphql-yoga具有对graphql-playground的内置支持。 使用上面的代码,您可以在http://localhost:4000打开Playground:
graphql-yoga还具有针对现成的GraphQL订阅的简单API,该API建立在graphql-subscriptions和ws-subscriptions-transport软件包之上。 您可以在这个简单直接的示例中查看其工作方式。
为了对使用graphql-yoga执行的GraphQL操作启用字段级分析,还内置了对Apollo Tracing的支持。
总结
在上一篇文章中讨论了基于GraphQLSchema的GraphQL执行过程和GraphQL引擎(例如GraphQL.js)的概念之后,这次我们重点关注网络层。特别的是,GraphQL服务器如何通过使用执行引擎处理查询(或变异)来响应HTTP请求。
在Node生态系统中,由于Express的简洁性和灵活性,它是迄今为止最受欢迎的构建Web服务器的框架。 因此,用于GraphQL服务器的最常见实现是基于Express的,最著名的是express-graphql和apollo-server。 这两个库都非常相似,只是有一些细微的差别,最重要的是apollo-server还与其他Web框架(例如koa和hapi)兼容。
graphql-yoga是许多其他库(例如graphql-tools,express,graphql-subscriptions和graphql-playground)之上的便利层,并且是构建GraphQL服务器的最简单方法。
在下一篇文章中,我们将讨论传递给GraphQL分解器的info参数的内部。