使用Node构建GraphQL APIs

使用Node构建GraphQL APIs

GraphQL是API开发中的新流行语。虽然RESTful APIs仍然是暴露应用程序数据的最流行方式,但它们有许多限制,而GraphQL旨在解决这些限制。

GraphQL是由Facebook创建的一种查询语言,在2015年变成了一个开源项目。它为描述和访问API中的数据提供了一个直观和灵活的语法。

本指南将探讨如何建立一个GraphQL Node.js项目。我们将使用GraphQL在Node的Express.js网络框架中建立一个Todo应用程序。

  1. 什么是GraphQL?
  2. GraphQL术语
  3. GraphQL如何与Node.js和Express.js一起工作?
  4. 用Express.js设置GraphQL

什么是GraphQL?

来自官方文档:”GraphQL是一种用于API的查询语言,也是用现有数据完成这些查询的运行时间。GraphQL为你的API中的数据提供了一个完整的、可理解的描述,使客户有能力准确地询问他们所需要的东西,而不是更多,使API更容易随着时间的推移而发展,并启用强大的开发者工具。”

GraphQL是一个服务器端的运行时间,用于使用你为你的数据定义的类型系统执行查询。此外,GraphQL不与任何特定的数据库或存储引擎相联系。相反,它是由你现有的代码和数据存储支持的。你可以通过GraphQL与RESTful API指南获得这些技术的详细比较。

要创建一个GraphQL服务,你首先要定义模式类型,并使用这些类型创建字段。接下来,你提供一个函数解析器,在客户端请求数据的时候,在每个字段和类型上执行。

GraphQL术语

GraphQL类型系统用于描述哪些数据可以被查询,哪些数据可以被操作。它是GraphQL的核心。让我们讨论一下我们在GraphQ中描述和操作数据的不同方式。

对象类型

GraphQL对象类型是包含强类型字段的数据模型。在你的模型和GraphQL类型之间应该有一个1对1的映射。下面是一个GraphQL类型的例子:

type User {
id: ID! # The "!" means required
firstname: String
lastname: String
email: String
username: String
todos: [Todo] # Todo is another GraphQL type
}

查询

GraphQL 查询定义了客户端可以在 GraphQL API 上运行的所有查询。你应该定义一个 RootQuery ,按照惯例,它将包含所有现有的查询。

下面我们定义并将查询映射到相应的RESTful API:

type RootQuery {
user(id: ID): User           # Corresponds to GET /api/users/:id
users: [User]                # Corresponds to GET /api/users
todo(id: ID!): Todo    # Corresponds to GET /api/todos/:id
todos: [Todo]          # Corresponds to GET /api/todos
}

突变

如果GraphQL查询是 GET 请求,那么突变就是操作GraphQL API的 POSTPUTPATCH, 和 DELETE 请求。

我们将把所有的突变放在一个单一的 RootMutation 中来演示:

type RootMutation {
createUser(input: UserInput!): User             # Corresponds to POST /api/users
updateUser(id: ID!, input: UserInput!): User    # Corresponds to PATCH /api/users
removeUser(id: ID!): User                       # Corresponds to DELETE /api/users
createTodo(input: TodoInput!): Todo
updateTodo(id: ID!, input: TodoInput!): Todo
removeTodo(id: ID!): Todo
}

你注意到在突变中使用了 -input 类型,如 UserInputTodoInput。在创建和更新你的资源时,总是定义输入类型是最好的做法。

你可以像下面这样定义输入类型:

input UserInput {
firstname: String!
lastname: String
email: String!
username: String!
}

解析器

解析器告诉GraphQL在每个查询或突变被请求时要做什么。它是一个基本的函数,它完成了打入数据库层进行CRUD(创建、读取、更新、删除)操作、打入内部RESTful API端点或调用微服务来完成客户的请求的艰苦工作。

你可以创建一个新的resolvers.js文件并添加以下代码:

import sequelize from '../models';
export default function resolvers () {
const models = sequelize.models;
return {
// Resolvers for Queries
RootQuery: {
user (root, { id }, context) {
return models.User.findById(id, context);
},
users (root, args, context) {
return models.User.findAll({}, context);
}
},
User: {
todos (user) {
return user.getTodos();
}
},
}
// Resolvers for Mutations
RootMutation: {
createUser (root, { input }, context) {
return models.User.create(input, context);    
},
updateUser (root, { id, input }, context) {
return models.User.update(input, { ...context, where: { id } });
},
removeUser (root, { id }, context) {
return models.User.destroy(input, { ...context, where: { id } });
},
// ... Resolvers for Todos go here
}
}

模式

GraphQL模式是GraphQL暴露给世界的东西。因此,类型、查询和突变将被包含在模式中,以暴露给世界。

下面是如何将类型、查询和突变暴露给世界的:

schema {
query: RootQuery
mutation: RootMutation
}

在上面的脚本中,我们包含了我们之前创建的 RootQuery 和 RootMutation ,以暴露在世界面前。

GraphQL如何与Node.js和Express.js一起工作?

GraphQL为所有主要的编程语言提供了一个实现,Node.js也不例外。在GraphQL官方网站上,有一个支持JavaScript的部分,同时,还有GraphQL的其他实现,使编写和编码变得简单。

GraphQL Apollo为Node.js和Express.js提供了一个实现,并使GraphQL容易入门。

你将在下一节中学习如何使用GraphQL Apollo在Node.js和Express.js后端框架中创建和开发你的第一个GraphQL应用程序。

用Express.js设置GraphQL

用Express.js构建GraphQL API服务器是很简单的,可以直接上手。在本节中,我们将探讨如何建立一个GraphQL服务器。

用Express初始化项目

首先,你需要安装和设置一个新的Express.js项目。为你的项目创建一个文件夹,用这个命令安装Express.js:

cd <project-name> && npm init -y
npm install express

上面的命令创建了一个新的package.json文件并将Express.js库安装到你的项目中。

接下来,我们将按照下图所示,构建我们的项目。它将包含项目功能的不同模块,如用户、todos等。

graphql-todo的文件

graphql-todo的文件

初始化GraphQL

让我们从安装GraphQL Express.js的依赖项开始。运行以下命令进行安装:

npm install apollo-server-express graphql @graphql-tools/schema --save

创建模式和类型

接下来,我们要在modules文件夹内创建一个index.js文件,并添加以下代码片段:

const { gql } = require('apollo-server-express');
const users = require('./users');
const todos = require('./todos');
const { GraphQLScalarType } = require('graphql');
const { makeExecutableSchema } = require('@graphql-tools/schema');
const typeDefs = gql`
scalar Time
type Query {
getVersion: String!
}
type Mutation {
version: String!
}
`;
const timeScalar = new GraphQLScalarType({
name: 'Time',
description: 'Time custom scalar type',
serialize: (value) => value,
});
const resolvers = {
Time: timeScalar,
Query: {
getVersion: () => `v1`,
},
};
const schema = makeExecutableSchema({
typeDefs: [typeDefs, users.typeDefs, todos.typeDefs],
resolvers: [resolvers, users.resolvers, todos.resolvers],
});
module.exports = schema;

代码演练

让我们通过代码片断进行工作,并将其分解:

第1步

首先,我们导入了所需的库,并创建了默认的查询和变异类型。查询和突变目前只设置了GraphQL API的版本。然而,随着我们的进展,我们将扩展查询和突变以包括其他模式。

导入GraphQL和扩展

导入GraphQL和扩展

第2步:

然后我们为时间创建了一个新的标量类型,并为上面创建的查询和突变创建了我们的第一个解析器。此外,我们还使用 makeExecutableEchema 函数生成了一个模式。

生成的模式包括我们导入的所有其他模式,当我们创建和导入这些模式时,也将包括更多的模式。

为时间创建一个标量类型

为时间创建一个标量类型,以及我们的第一个解析器。

上面的代码片段显示,我们在makeExecutableEchema函数中导入了不同的模式。这种方法有助于我们在结构化应用程序的复杂性。接下来,我们要创建我们导入的Todo和User模式。

创建Todo模式

Todo模式显示了应用程序的用户可以执行的简单CRUD操作。下面是实现Todo CRUD操作的模式。

const { gql } = require('apollo-server-express');
const createTodo = require('./mutations/create-todo');
const updateTodo = require('./mutations/update-todo');
const removeTodo = require('./mutations/delete-todo');
const todo = require('./queries/todo');
const todos = require('./queries/todos');
const typeDefs = gql`
type Todo {
id: ID!
title: String
description: String
user: User
}
input CreateTodoInput {
title: String!
description: String
isCompleted: Boolean
}
input UpdateTodoInput {
title: String
description: String
isCompleted: Boolean
}  extend type Query {
todo(id: ID): Todo!
todos: [Todo!]
}
extend type Mutation {
createTodo(input: CreateTodoInput!): Todo
updateTodo(id: ID!, input: UpdateTodoInput!): Todo
removeTodo(id: ID!): Todo
}
`;
// Provide resolver functions for your schema fields
const resolvers = {
// Resolvers for Queries
Query: {
todo,
todos,
},
// Resolvers for Mutations
Mutation: {
createTodo,
updateTodo,
removeTodo,
},
};
module.exports = { typeDefs, resolvers };

代码演练

让我们通过代码片断进行工作,并将其分解:

第1步:

首先,我们使用GraphQL typeinput, 和 extend 为我们的Todo创建一个模式。extend 关键字是用来继承和添加新的查询和突变到我们上面创建的现有根查询和突变。

为Todo创建模式

为Todo创建模式

第2步:

接下来,我们创建了一个解析器,用于在调用特定查询或变异时检索正确的数据。

创建解析器

创建解析器

使用解析器函数,我们可以为业务逻辑和数据库操作创建单独的方法,如create-todo.js示例所示。

./mutations 文件夹中创建一个create-user.js文件,并添加业务逻辑以在数据库中创建一个新的Todo。

const models = require('../../../models');
module.exports = async (root, { input }, context) => {
return models.todos.push({ ...input });
};

上面的代码片段是使用Sequelize ORM在我们的数据库中创建一个新Todo的简化方法。你可以了解更多关于Sequelize以及如何用Node.js设置它

你可以按照同样的步骤,根据你的应用程序创建许多模式,或者你可以从GitHub上克隆整个项目

接下来,我们将用Express.js设置服务器,并使用GraphQL和Node.js运行新创建的Todo应用程序。

设置和运行服务器

最后,我们将使用之前安装的 apollo-server-express 库设置我们的服务器,并对其进行配置。

apollo-server-express 是一个用于Express.js的Apollo服务器的简单包装,它被推荐是因为它已经被开发为适合Express.js的开发。

使用我们上面讨论的例子,让我们配置Express.js服务器,使其与新安装的 apollo-server-express一起工作。

在根目录下创建一个server.js文件并粘贴以下代码:

const express = require('express');
const { ApolloServer } = require('apollo-server-express');
const schema = require('./modules');
const app = express();
async function startServer() {
const server = new ApolloServer({ schema });
await server.start();
server.applyMiddleware({ app });
}
startServer();
app.listen({ port: 3000 }, () =>
console.log(`Server ready at http://localhost:3000`)
);

在上面的代码中,你已经为Todos和Users成功创建了你的第一个CRUD GraphQL服务器。你可以启动你的开发服务器并使用http://localhost:3000/graphql 来访问游乐场。如果一切成功,你应该看到下面的屏幕:

验证屏幕

验证屏幕

小结

GraphQL是由Facebook支持的现代技术,它简化了使用RESTful架构模式创建大规模API的繁琐工作。

本指南阐明了GraphQL,并演示了如何用Express.js开发你的第一个GraphQL API。

请在下面的评论中告诉我们你用GraphQL建立了什么。

评论留言