- 原文地址:Creating a GraphQL server with NodeJS
- 原文做者:Aman Mittal
- 译文出自:掘金翻译计划
- 本文永久连接:github.com/xitu/gold-m…
- 译者:Raoul1996
- 校对者:KarthusLorin, weibinzhu,leviding
当谈到客户端和应用程序服务器之间的网络请求时,REST(表现层状态转换的表明)是链接两者最经常使用的选择之一。在 REST API 的世界中,一切都围绕着如何把资源做为可访问的 URL。而后咱们会进行 CURD 操做(新建、读取、更新、删除),这些操做是 HTTP 的基本方法,如 GET、POST、PUT 和 DELETE,来与数据进行交互。html
这是一个典型的 REST 请求的例子:前端
// 请求示例
https://swapi.co/api/people/
// 上面请求的 JSON 格式响应
{
"results": [
{
"name": "Luke Skywalker",
"gender": "male",
"homeworld": "https://swapi.co/api/planets/1/",
"films": [
"https://swapi.co/api/films/2/",
"https://swapi.co/api/films/6/",
"https://swapi.co/api/films/3/",
"https://swapi.co/api/films/1/",
"https://swapi.co/api/films/7/"
],
}
{
"name": "C-3PO",
"gender": "n/a",
"homeworld": "https://swapi.co/api/planets/1/",
"films": [
"https://swapi.co/api/films/2/",
"https://swapi.co/api/films/5/",
"https://swapi.co/api/films/4/",
"https://swapi.co/api/films/6/",
"https://swapi.co/api/films/3/",
"https://swapi.co/api/films/1/"
],
}
]
}
复制代码
REST API 的响应格式未必会是 JSON,可是这是目前大多数 API 的首选方法。除了 REST,还出现了另外一种处理网络请求的方法:GraphQL。它于 2015 年开源,正在改变着开发人员在服务器端编写API以及在客户端处理API的方式。并由 Facebook 开发并积极维护。node
GraphQL 是一种用于开发 API 的查询语言。和 REST(一种架构或者“一种作事方式”)相比,GraphQL 的开发基于一个理念:客户端每次仅从服务端请求所须要的项目集合。react
在上面的例子中,使用了 REST 或者其余相似架构。咱们请求 Star Wars 系列电影中 Luke Skywalker 出现过的电影时,咱们获得了一系列的 电影
或者 homeworld
的名称,他们还包含了不一样的 API URL,引导咱们去了解不一样 JSON 数据集的详细信息。这确定是一个过分获取(over fetching)的例子。客户端为了去获取人物 Luke Skywalker 出如今电影中的详情以及他家乡星球的名称,只能去向服务端发起多个请求。android
使用 GraphQL,就能够将其解析为单个网络请求。转到 API 网址:https://graphql.github.io/swapi-graphql/
,查看运行如下查询(query)看看。ios
注意:在下面的例子中,你能够没必要理会 GraphQL API 幕后的工做方式。我将在本教程后面逐步构建你本身的(多是第一个)GraphQL API。git
{
allPeople {
edges {
node {
name
gender
homeworld {
name
}
filmConnection {
edges {
node {
title
}
}
}
}
}
}
}
复制代码
咱们将获取咱们须要的数据。例如角色的名称、他们的性别(gender
)、家园(homeworld
),以及他们出现的电影(films
)的标题。运行上述查询,你将得到如下结果:github
{
"data": {
"allPeople": {
"edges": [
{
"node": {
"name": "Luke Skywalker",
"gender": "male",
"homeworld": {
"name": "Tatooine"
},
"filmConnection": {
"edges": [
{
"node": {
"title": "A New Hope"
}
},
{
"node": {
"title": "The Empire Strikes Back"
}
},
{
"node": {
"title": "Return of the Jedi"
}
},
{
"node": {
"title": "Revenge of the Sith"
}
},
{
"node": {
"title": "The Force Awakens"
}
}
]
}
}
},
{
"node": {
"name": "C-3PO",
"gender": "n/a",
"homeworld": {
"name": "Tatooine"
},
"filmConnection": {
"edges": [
{
"node": {
"title": "A New Hope"
}
},
{
"node": {
"title": "The Empire Strikes Back"
}
},
{
"node": {
"title": "Return of the Jedi"
}
},
{
"node": {
"title": "The Phantom Menace"
}
},
{
"node": {
"title": "Attack of the Clones"
}
},
{
"node": {
"title": "Revenge of the Sith"
}
}
]
}
}
}
]
}
}
}
复制代码
若是应用程序的客户端正在触发上述 GraphQL URL,它只须要在网络上发一个请求就能够获得所需结果。从而消除了任何会致使过分获取或发送多个请求的可能性。web
要学习本课程,你只须要在本地计算机上安装 nodejs
和 npm
便可。数据库
^8.12.0
^6.4.1
简而言之,GraphQL 是一种用于阐述如何请求 data 的语法,一般用于从客户端检索数据(也称为 query)或者对其进行更改(也称为 mutation)。
GraphQL 几乎没有什么定义特征:
GraphQL API 有四个构建模块:
Schema 以对象的形式在服务器上定义。每一个对象对应于数据类型,以便于去查询他们。例如:
type User {
id: ID!
name: String
age: Int
}
复制代码
上面的 schema 定义了一个用户对象的样子。其中必需的字段 id
用 !
符号标识。还包含其余字段,例如 string 类型的 name
和 integer 类型的 age
。这也会在查询数据的时候对 schema
进行验证。
Queries 是你用来向 GraphQL API 发出请求的方法。例如,在咱们上面的示例中,就像咱们获取 Star Wars 相关的数据时那样。让咱们简化一下,若是在 GraphQL 中查询,就是在查询对象的特定字段。例如,使用上面相同的 API,咱们能获取 Star Wars 中全部角色的名称。下面你能够看到差别,在图片的左侧是查询,右侧是结果。(译者注:原文是 on the right-hand side is the image,译者认为不是很合适)
使用 GraphQL 查询的好处是它们能够嵌套到你想要的深度。这在 REST API 中很难作到。(在 REST API 中)操做变得复杂得多。
下面是一个更复杂的嵌套查询示例:
Mutations: 在 REST 架构中,要修改数据,咱们要么使用 POST
来添加数据,要么使用 PUT
来更新现有字段的数据。在 GraphQL 中,总体的概念是相似的。你能够发送一个 query 来在服务端执行写入操做。可是。这种形式的查询称为 Mutation。
Resolvers 是 schema 和 data 之间的纽带。它们提供可用于经过不一样操做与数据库交互的功能。
在这个教程中,你将学习用咱们刚刚学到的构件,来使用 Nodejs 构建 GraphQL 服务器。
如今咱们来写咱们第一个 GraphQL 服务器。本教程中,咱们将使用 Apollo Server。咱们须要为 Apollo Server 安装三个包才能使用现有的 Express 应用程序做为中间件。Apollo Server 的优势在于它能够与 Node.js 的几个流行框架一块儿使用:Express、Koa 和 Hapi。Apollo 自己和库无关,所以在客户端和服务器应用程序中,它能够和许多第三方库链接。
打开你的终端安装如下依赖:
# 首先新建一个空文件夹
mkdir apollo-express-demo
# 而后初始化
npm init -y
# 安装须要的依赖
npm install --save graphql apollo-server-express express
复制代码
让咱们简要了解下这些依赖的做用。
graphql
是一个支持库,而且在咱们这里是一个必要的模块apollp-server-express
是相应的 HTTP 服务器支持包express
是 Nodejs 的 web 框架你能够在下面的图中看到我安装了所有的依赖,没有出现任何错误。
在你项目的根路径下,新建一个名字为 index.js
,包含如下代码的文件。
const express = require('express');
const { ApolloServer, gql } = require('apollo-server-express');
const typeDefs = gql` type Query { hello: String } `;
const resolvers = {
Query: {
hello: () => 'Hello world!'
}
};
const server = new ApolloServer({ typeDefs, resolvers });
const app = express();
server.applyMiddleware({ app });
app.listen({ port: 4000 }, () =>
console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`)
);
复制代码
这是咱们服务器文件的起点。开始咱们仅仅只须要 express
模块。gql
是一个模板文字标记,用于将 GraphQL schema 编写为类型。schema 由类型定义组成,而且强制包含一个用于读取数据的 Query 类型,用于读取数据。它还能够包含表示其余数据字段的字段和嵌套字段。在咱们上面的例子中,咱们定义了 typeDefs
来编写 graphQL 的 schema。
而后 resolvers
映入眼帘。Resolver 用于从 schema 中返回字段的数据。在咱们的示例中,咱们定义了一个 resolver,它将函数 hello()
映射到咱们的 schema 上的实现。接下来,咱们建立一个 server
,它使用 ApolloServer
类来实例化并启动服务器。因为咱们使用了 Express,因此咱们须要集成 ApolloServer
类。经过 applyMiddleware()
做为 app
来传递它,来添加 Apollo Server 的中间件。这里的 app
是 Express 的一个实例,表明了现有的应用程序。
最后,咱们使用 Express 模块提供的 app.listen()
来引导服务器。要运行服务器,只须要打开 terminal 并运行命令 node index.js
。如今,从浏览器窗口访问 url:http://localhost:4000/graphql
来看看它的操做。
Apollo Server 为你设置了 GraphQL Playground,供你快速开始运行 query,探索 schema,以下所示。
要运行一个 query,在左侧编辑空白部分,输入如下 query。而后按中间的 ▶ (play)按钮。
右侧的 schema 卡描述了咱们查询 hello
的数据类型。这直接来自咱们服务器中定义的 typeDefs
。
瞧!你刚建立了第一个 GraphQL 服务器。如今让咱们拓展下咱们对现实世界的认知。
目前为止咱们整理了全部必要的模块以及随附的必要术语。在这一节,咱们将用 Apollo Server 为咱们的演示去建立一个小的 Star Wars API。你可能已经猜到了 Apollo server 是一个库,能够帮助你使用 Nodejs 将 GraphQL schema 链接到 HTTP server。它不局限于特定的 Node 框架。例如上一节中咱们使用了 ExpressJS。Apollo Server 支持 Koa,Restify,Hapi 和 Lambda。对于咱们的 API,咱们继续使用 Express。
若是想从头开始,请继续。从 Hello World! With GraphQL
一节安装全部的库。这是咱们在前面一节中安装的全部依赖:
"dependencies": {
"apollo-server-express": "^2.1.0",
"express": "^4.16.4",
"graphql": "^14.0.2"
}
复制代码
我将使用相同的项目和相同的文件 index.js
去引导服务器启动。可是在咱们构建咱们的 API 以前,我想告诉你如何在咱们的演示项目中使用 ES6 modules。对于使用像 React 和 Angular 这样的前端库,他们已经支持了 ES6 特性。例如 import
和 export default
这样的语句。Nodejs 版本 8.x.x
解决了这个问题。咱们所须要的只是一个转换器(transpiler)让咱们使用 ES6 特性编写 JavaScript。你彻底能够跳过这个步骤使用旧的 require()
语句。
那么什么是转换器呢?
转换器(Transpiler)也被称做‘源到源的编译器’,从一种编程语言写的源码中读取代码转换成另外一种语言的等效代码。
在 Nodejs 的状况下,咱们不会切换编程语言,而是要使用哪些我目前使用的 LTS 版本的 Node 不支持的语言的新特性。我将安装 Babel 编译器,并经过接下来的配置过程在咱们的项目中启用它。
首先,你须要安装一些依赖,记得使用 -D
参数。由于咱们只会在开发环境中用到这些依赖。
npm install -D babel-cli babel-preset-env babel-watch
复制代码
只要你成功安装了他们,在项目的根目录下添加一个 .babelrc
文件而且添加如下配置:
{
"presets": [env]
}
复制代码
配置流程的最后一步是在 package.json
中添加一个 dev
脚本(script)
。一旦(项目文件)发生变化,babel 编译器将自动运行。这由 babel-watch
完成。同时它也负责从新启动 Nodejs 网络服务器。
"scripts": {
"dev": "babel-watch index.js"
}
复制代码
要查看它的操做,请将如下代码添加到 index.js
中,看看是否一切正常。
import express from 'express';
const app = express();
app.get('/', (req, res) => res.send('Babel Working!'));
app.listen({ port: 4000 }, () =>
console.log(`🚀 Server ready at http://localhost:4000`)
);
复制代码
在终端中输入 npm run dev
,不出意外,你能够看到下面的信息:
你也能够在浏览器中访问 http://localhost:4000/
去看看其操做。
咱们须要一个 schema 来启动咱们的 GraphQL API。让咱们在 api
目录下建立一个名字为 api/schema.js
的新文件。添加如下 schema。
import { gql } from 'apollo-server-express';
const typeDefs = gql` type Person { id: Int name: String gender: String homeworld: String } type Query { allPeople: [Person] person(id: Int!): Person } `;
export default typeDefs;
复制代码
咱们的 schema 一共包含两个 query。第一个是 allPeople
,经过它咱们能够列出到 API 中的全部的人物。第二个查询 person
是使用他们的 id 检索一我的。这两种查询类型都依赖于一个名为 Person
对象的自定义类型,该对象包含四个属性。
咱们已经了解了 resolver 的重要性。它基于一种简单的机制,去关联 schema 和 data。Resolver 是包含 query 或者 mutation 背后的逻辑和函数。而后使用它们来检索数据并在相关请求上返回。
若是在使用 Express 以前构建了服务器,则能够将 resolver 视为控制器,其中每个控制器都是针对特定路由构建。因为咱们不在服务器后面使用数据库,所以咱们必须提供一些虚拟数据来模拟咱们的 API。
建立一个名为 resolvers.js
的新文件并添加下面的文件。
const defaultData = [
{
id: 1,
name: 'Luke SkyWaler',
gender: 'male',
homeworld: 'Tattoine'
},
{
id: 2,
name: 'C-3PO',
gender: 'bot',
homeworld: 'Tattoine'
}
];
const resolvers = {
Query: {
allPeople: () => {
return defaultData;
},
person: (root, { id }) => {
return defaultData.filter(character => {
return (character.id = id);
})[0];
}
}
};
export default resolvers;
复制代码
首先,咱们定义 defaultData
数组,其中包含 Star Wars 中两我的物的详细信息。根据咱们的 schema,数组中的这两个对象都有四个属性。接下来是咱们的 resolvers
对象,它包含两个函数。这里可使用 allPeople()
来检索 defaultData
数组中的全部数据。person()
箭头函数使用参数 id
来检索具备请求 ID 的 person 对象。这个已经在咱们的查询中定义了。
你必须导出 resolver 和 schema 对象才能将它们与 Apollo Server 中间件一块儿使用。
如今咱们定义了 schema 和 resolver,咱们将要在 index.js
文件里边实现服务器。首先从 apollo-server-express
导入 Apollo-Server。咱们还须要从 api/
文件夹导入咱们的 schema 和 resolvers 对象。而后,使用 Apollo Server Express 库中的 GraphQL 中间件实例化 GraphQL API。
import express from 'express';
import { ApolloServer } from 'apollo-server-express';
import typeDefs from './api/schema';
import resolvers from './api/resolvers';
const app = express();
const PORT = 4000;
const SERVER = new ApolloServer({
typeDefs,
resolvers
});
SERVER.applyMiddleware({ app });
app.listen(PORT, () =>
console.log(`🚀 GraphQL playground is running at http://localhost:4000`)
);
复制代码
最后,咱们使用 app.listen()
来引导咱们的 Express 服务器。你如今能够从终端执行命令 npm run dev
来运行服务器。服务器节点启动后,将提示成功消息,指示服务器已经启动。
如今要测试咱们的 GraphQL API,在浏览器窗口中跳转 http://localhost:4000/graphql
URL 并运行如下 query。
{
allPeople {
id
name
gender
homeworld
}
}
复制代码
点击 play 按钮,你将在右侧部分看到熟悉的结果,以下所示。
一切正常,由于咱们的查询类型 allPeople
具备自定义的业务逻辑,可使用 resolver 检索全部数据(在咱们的例子中,咱们在 resolvers.js
中做为数据提供的模拟数据)。要获取单我的物对象,请尝试运行相似的其余 query。请记住,必须提供 ID。
{
person(id: 1) {
name
homeworld
}
}
复制代码
运行上面的查询,在结果中,你能够得到获得的每一个字段/属性的值以进行查询。你的结果将相似于如下内容。
完美!我相信你必定掌握了如何建立 GraphQL query 并运行它。Apollo Server 库功能很强大。它让咱们可以编辑 playground。假设咱们要编辑 playground 的主题?咱们要作的就是在建立 ApolloServer
实例时提供一个选项,在咱们的例子中是 SERVER
。
const SERVER = new ApolloServer({
typeDefs,
resolvers,
playground: {
settings: {
'editor.theme': 'light'
}
}
});
复制代码
playground
属性有不少功能,例如定义 playground 的默认端点(endpoint)以更改主题。你甚至能够在生产模式启用 playground。更多配置项能够在Apollo Server 的官方文档中找到,这里。
更改主题后咱们获取下面的结果。
若是你一步一步完成教程,那么祝贺你! 🎉
你已经学习了如何使用 Apollo 库配置 Express 服务器来设置您本身的 GraphQL API。Apollo Server 是一个开源项目,是为全栈应用程序建立 GraphQL API 的最稳定的解决方案之一。他还支持客户端开箱即用的 React、Vue、Angular、Meteor 和 Ember 以及使用 Swift 和 Java 的 Native 移动开发。有关这方面的更多信息能够在这里找到。
在此 Github 仓库中查看教程的完整代码 👇
Crowdbotics 帮助企业利用 Node 构建酷炫的东西(除此以外)。若是你有一个 Node 项目,你须要其余开发者资源,请给咱们留言。Crowbotics 能够帮助您估算给定产品的功能规格的构建时间,并根据您的须要提供专门的 Node 开发者。若是你使用 Node 构建,查看 Crowdbotics。
感谢 William Wickey 提供编辑方面的帮助。
若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。