什么是 GraphQL 以及如何使用它
什么是 GraphQL?
先决条件
如何在 Node.js 中使用 GraphQL
代码
测试应用程序
恭喜😃
这篇文章最初发表于adityasridhar.com
什么是 GraphQL?
它基本上是一种 API 的查询语言
GraphQL 显示服务器提供的不同类型数据,然后客户端可以准确地选择它想要的数据。
另外,在 GraphQL 中,您可以在一次调用中获取多个服务器资源,而不必进行多个 REST API 调用。
您可以查看https://graphql.org/以获取完整的好处列表。
问题是,除非你亲眼看到 GraphQL 的实际应用,否则很难理解它的好处。所以,让我们开始使用 GraphQL 吧。
在本文中,我们将使用 GraphQL 和 NodeJS。
先决条件
从https://nodejs.org/en/安装 NodeJS
如何在 Node.js 中使用 GraphQL
GraphQL 可以与多种语言一起使用。本文我们将重点介绍如何在 NodeJS 中使用 GraphQL 和 JavaScript。
创建一个名为graphql-with-nodejs 的文件夹。进入项目文件夹并运行npm init
以创建 NodeJS 项目。相关命令如下。
cd graphql-with-nodejs
npm init
安装依赖项
使用以下命令安装 Express
npm install express
使用以下命令安装 GraphQL。我们将安装 graphql 和 express 版 graphql。
npm install express-graphql graphql
NodeJS 代码
在项目内创建一个名为server.js 的文件,并将以下代码复制到其中
const express = require('express');
const port = 5000;
const app = express();
app.get('/hello', (req,res) => {
res.send("hello");
}
);
app.listen(port);
console.log(`Server Running at localhost:${port}`);
上面的代码有一个名为/hello 的http get 端点。
终点是使用 express 创建的。
现在让我们修改此代码以启用 GraphQL。
在代码中启用 GraphQL
GraphQL 将有一个名为/graphql 的单个 url 端点,它将处理所有请求。
将以下代码复制到server.js中
//get all the libraries needed
const express = require('express');
const graphqlHTTP = require('express-graphql');
const {GraphQLSchema} = require('graphql');
const {queryType} = require('./query.js');
//setting up the port number and express app
const port = 5000;
const app = express();
// Define the Schema
const schema = new GraphQLSchema({ query: queryType });
//Setup the nodejs GraphQL server
app.use('/graphql', graphqlHTTP({
schema: schema,
graphiql: true,
}));
app.listen(port);
console.log(`GraphQL Server Running at localhost:${port}`);
现在让我们来看一下这段代码
graphqlHTTP使我们能够在/graphql url 上设置一个 GraphQL 服务器。它基本上知道如何处理传入的请求。
此设置通过以下代码行完成
app.use('/graphql', graphqlHTTP({
schema: schema,
graphiql: true,
}));
现在让我们探索 graphqlHTTP 内部的参数
graphiql
graphiql 是一个 Web UI,你可以用它来测试 graphql 端点。我们将它设置为 true,以便更轻松地测试我们创建的各种 graphql 端点。
模式
虽然 graphql 只有一个外部端点/graphql,但它可以有多个其他端点来执行各种操作。这些端点将在 schema 中指定。
该模式将执行以下操作:
- 指定端点
- 指示端点的输入和输出字段
- 指示当到达端点时应该采取什么行动等等。
Schema 在代码中定义如下
const schema = new GraphQLSchema({ query: queryType });
模式可以包含查询类型和突变类型。本文仅关注查询类型。
询问
从模式中可以看出,查询已设置为queryType。
我们使用以下命令从query.js文件导入 queryType
const {queryType} = require('./query.js');
query.js是一个自定义文件,我们将很快创建它。
查询是我们在模式中指定只读端点的地方。
在项目中创建一个名为query.js的文件,并将以下代码复制到其中。
const { GraphQLObjectType,
GraphQLString
} = require('graphql');
//Define the Query
const queryType = new GraphQLObjectType({
name: 'Query',
fields: {
hello: {
type: GraphQLString,
resolve: function () {
return "Hello World";
}
}
}
});
exports.queryType = queryType;
查询解释
queryType 被创建为GraphQLObjectType并被赋予名称Query。
字段是我们指定各种端点的地方。
因此,我们在这里添加一个名为hello的端点
hello的类型是GraphQLString,这意味着此端点的返回类型为 String。由于这是一个 graphql schema,因此返回类型是GraphQLString而不是String。因此直接使用 String 是行不通的。
resolve函数表示调用端点时要执行的操作。此处的操作是返回一个字符串“Hello World”。
最后,我们使用 导出查询类型exports.queryType = queryType
。这是为了确保我们可以在server.js中导入它。
运行应用程序
使用以下命令运行应用程序
node server.js
该应用程序在localhost:5000/graphql上运行。
您可以通过访问 localhost:5000/graphql 来测试该应用程序。
此 URL 运行 Graphiql Web UI,如下面的屏幕所示。
左侧给出输入,右侧显示输出。
给出以下输入
{
hello
}
这将给出以下输出
{
"data": {
"hello": "Hello World"
}
}
恭喜😃
您已创建第一个 GraphQL 端点。
添加更多端点
我们将创建 2 个新端点:
- movie:此端点将返回一部电影,给定电影 ID
- 导演:此端点将根据导演 ID 返回导演。它还将返回该导演执导的所有电影。
添加数据
通常,应用程序会从数据库读取数据。但在本教程中,为了简单起见,我们将在代码中对数据进行硬编码。
创建一个名为data.js的文件并添加以下代码。
//Hardcode some data for movies and directors
let movies = [{
id: 1,
name: "Movie 1",
year: 2018,
directorId: 1
},
{
id: 2,
name: "Movie 2",
year: 2017,
directorId: 1
},
{
id: 3,
name: "Movie 3",
year: 2016,
directorId: 3
}
];
let directors = [{
id: 1,
name: "Director 1",
age: 20
},
{
id: 2,
name: "Director 2",
age: 30
},
{
id: 3,
name: "Director 3",
age: 40
}
];
exports.movies = movies;
exports.directors = directors;
此文件包含电影和导演数据。我们将使用此文件中的数据作为我们的端点。
将电影端点添加到查询中
新的端点将被添加到 query.js 文件中的 queryType
电影端点的代码如下所示
movie: {
type: movieType,
args: {
id: { type: GraphQLInt }
},
resolve: function (source, args) {
return _.find(movies, { id: args.id });
}
}
此端点的返回类型是movieType,我们将很快定义它。
args参数用于指示影片端点的输入。此端点的输入是id,其类型为
GraphQLInt。
resolve函数从电影列表中返回与 id 对应的电影。find是lodash库中的一个函数,用于在列表中查找元素。
query.js的完整代码如下所示
const { GraphQLObjectType,
GraphQLString,
GraphQLInt
} = require('graphql');
const _ = require('lodash');
const {movieType} = require('./types.js');
let {movies} = require('./data.js');
//Define the Query
const queryType = new GraphQLObjectType({
name: 'Query',
fields: {
hello: {
type: GraphQLString,
resolve: function () {
return "Hello World";
}
},
movie: {
type: movieType,
args: {
id: { type: GraphQLInt }
},
resolve: function (source, args) {
return _.find(movies, { id: args.id });
}
}
}
});
exports.queryType = queryType;
从上面的代码可以看出,movieType实际上是在types.js中定义的
添加自定义类型 movieType
创建一个名为types.js的文件。
在 types.js 中添加以下代码
const {
GraphQLObjectType,
GraphQLID,
GraphQLString,
GraphQLInt
} = require('graphql');
// Define Movie Type
movieType = new GraphQLObjectType({
name: 'Movie',
fields: {
id: { type: GraphQLID },
name: { type: GraphQLString },
year: { type: GraphQLInt },
directorId: { type: GraphQLID }
}
});
exports.movieType = movieType;
可以看出,movieType被创建为GraphQLObjectType
它有 4 个字段:id、name、year 和 directorId。添加每个字段时,也会指定它们的类型。
这些字段直接来自数据。在本例中,它来自电影列表。
添加主管端点的查询和类型
与电影类似,甚至可以添加导演端点。
在query.js中,可以按如下方式添加 director 端点
director: {
type: directorType,
args: {
id: { type: GraphQLInt }
},
resolve: function (source, args) {
return _.find(directors, { id: args.id });
}
}
directorType可以在types.js中添加如下内容
//Define Director Type
directorType = new GraphQLObjectType({
name: 'Director',
fields: {
id: { type: GraphQLID },
name: { type: GraphQLString },
age: { type: GraphQLInt },
movies: {
type: new GraphQLList(movieType),
resolve(source, args) {
return _.filter(movies, { directorId: source.id });
}
}
}
});
等一下。directorType和 movieType有点不同。这是为什么呢?
为什么directorType中有一个 resolve 函数?之前我们看到 resolve 函数只存在于查询中。
导演类型的特殊性
当调用导演端点时,我们必须返回导演的详细信息,以及该导演执导的所有电影。
directorType中的前 3 个字段id、name、age很简单,直接来自数据(导演列表)
第四个字段movies需要包含该导演的电影列表。
为此,我们要提到电影字段的类型是
movieType 的 GraphQLList(电影列表)
但究竟如何才能找到这位导演执导的所有电影呢?
为此,我们在 movies 字段中设置了一个resolve
函数。 该 resolve 函数的输入是source和args。
源将具有父对象详细信息。
假设导演的字段id = 1,name = "Random",age = 20。那么source.id = 1,source.name = "Random",source.age = 20
因此在这个例子中,resolve 函数找出所有 directorId 与所需导演的 Id 匹配的电影。
代码
此应用程序的完整代码可在此GitHub 仓库中找到
测试应用程序
现在让我们针对不同的场景测试该应用程序。
使用运行应用程序node server.js
转到localhost:5000/graphql并尝试以下输入。
电影
输入:
{
movie(id: 1) {
name
}
}
输出:
{
"data": {
"movie": {
"name": "Movie 1"
}
}
}
从上面可以看出,客户端可以精确地请求它想要的内容,GraphQL 将确保只返回这些参数。这里只请求了name字段,服务器也只返回了该字段。
在 中movie(id: 1)
,id 是输入参数。我们要求服务器返回 id 为 1 的电影。
输入:
{
movie(id: 3) {
name
id
year
}
}
输出:
{
"data": {
"movie": {
"name": "Movie 3",
"id": "3",
"year": 2016
}
}
}
在上面的例子中,请求了name、id 和 year字段。因此服务器返回了所有这些字段。
导演
输入:
{
director(id: 1) {
name
id,
age
}
}
输出:
{
"data": {
"director": {
"name": "Director 1",
"id": "1",
"age": 20
}
}
}
输入:
{
director(id: 1) {
name
id,
age,
movies{
name,
year
}
}
}
输出:
{
"data": {
"director": {
"name": "Director 1",
"id": "1",
"age": 20,
"movies": [
{
"name": "Movie 1",
"year": 2018
},
{
"name": "Movie 2",
"year": 2017
}
]
}
}
}
在上面的例子中,我们展示了 GraphQL 的强大功能。我们指定了 id 为 1 的导演,并指定了这位导演的所有电影。导演和电影字段都是完全可自定义的,客户端可以精确地请求所需的内容。
类似地,这可以扩展到其他字段和类型。例如,我们可以运行类似“查找 ID 为 1 的导演”的查询。查找该导演的所有电影。查找每部电影的演员。查找每位演员的评分最高的 5 部电影,等等。对于此查询,我们需要指定类型之间的关系。完成此操作后,客户端可以查询所需的任何关系。
恭喜😃
现在您已经了解了 GraphQL 的基本概念。
您可以查看文档以了解有关 GraphQL 的更多信息