本文共 8469 字,大约阅读时间需要 28 分钟。
GraphQL究竟是什么东西?
它实际上是一种API查询语言。
GraphQL显示了服务器可以提供的不同类型的数据,然后客户端就可以明确选择它们想要哪些内容。
在使用GraphQL时,你可以在一个调用中获取多个服务器的资源,而不是像REST API那样需要调用多个API。
理论说得再多也没用,例子才是最直观的。所以,让我们开始使用GraphQL吧。
我们将在本文中使用GraphQL和NodeJS。
下载和安装NodeJS:
GraphQL可以与多种语言一起使用。在这里,我们将重点介绍如何在NodeJS中使用GraphQL。
创建一个叫作graphql-with-nodejs的文件夹。进入这个文件夹,并运行npm init来创建NodeJS项目。
cd graphql-with-nodejsnpm init
使用以下命令安装Express。
npm install express
使用以下命令安装GraphQL。我们将安装graphql和express-graphql。
npm install express-graphql graphql
在项目中创建一个叫作server.js的文件,并将下面的代码复制到文件中。
const express = require('express');const port = 5000;const app = express();app.get('/hello', (req,res) =\u0026gt; { res.send(\u0026quot;hello\u0026quot;); });app.listen(port);console.log(`Server Running at localhost:${port}`);
上面的代码提供了一个叫作/hello的HTTP端点。
这个端点是使用express创建的。
现在让我们修改代码,启用GraphQL。
GraphQL将提供一个叫作/graphql的端点,负责处理所有的请求。
将下面的代码复制到server.js文件中。
//get all the libraries neededconst express = require('express');const graphqlHTTP = require('express-graphql');const {GraphQLSchema} = require('graphql');const {queryType} = require('./query.js');//setting up the port number and express appconst port = 5000;const app = express(); // Define the Schemaconst schema = new GraphQLSchema({ query: queryType });//Setup the nodejs GraphQL serverapp.use('/graphql', graphqlHTTP({ schema: schema, graphiql: true,}));app.listen(port);console.log(`GraphQL Server Running at localhost:${port}`);
我们在/graphql端点上建立了一个GraphQL服务器,它知道如何处理收到的请求。
GraphQL服务器是通过下面的代码建立起来的。
app.use('/graphql', graphqlHTTP({ schema: schema, graphiql: true,}));
现在让我们来看一下graphqlHTTP的参数。
graphiql是一个Web UI,你可以用它来测试graphql端点。我们将其设置为true,这样就可以很容易测试我们创建的各种graphql端点。
虽然graphql只提供了一个外部端点/graphql,但它可以拥有多个其他端点,用于执行其他各种操作。这些端点可以在schema中指定。
schema将执行以下操作:
指定端点;
指定端点的输入和输出字段;
指定在端点被调用时应该执行哪些操作,等等。
schema的定义如下。
const schema = new GraphQLSchema({ query: queryType });
schema可以包含查询和可变类型,不过本文只关注查询类型。
在这个定义中可以看到,query已被设置为queryType。
我们使用以下命令从query.js文件导入queryType。
const {queryType} = require('./query.js');
query.js是一个自定义文件,我们稍后会创建它。
在项目中创建一个叫作query.js的文件,并将下面的代码复制到文件中。
const { GraphQLObjectType, GraphQLString} = require('graphql');//Define the Queryconst queryType = new GraphQLObjectType({ name: 'Query', fields: { hello: { type: GraphQLString, resolve: function () { return \u0026quot;Hello World\u0026quot;; } } }});exports.queryType = queryType;
queryType是一个GraphQLObjectType对象,并指定了名称Query。
我们在fields中指定各种端点,我们在这里添加一个叫作hello的端点。
hello的type是GraphQLString,这意味着这个端点的返回类型为字符串。因为这是graphql schema,所以字符串类型是GraphQLString而不是String。如果直接使用String是不行的。
resolve函数在调用端点时会被执行。这里的操作是返回字符串“Hello World”。
最后,我们使用exports.queryType = queryType导出queryType。这样我们就可以在server.js中导入它。
使用以下命令运行这个应用程序。
node server.js
应用程序将运行在localhost:5000/graphql上。
你可以通过访问localhost:5000/graphql来测试应用程序。
Graphiql Web UI如下图所示。
左侧是输入,右侧是输出。
给定以下输入:
{ hello}
将给出以下输出:
{ \u0026quot;data\u0026quot;: { \u0026quot;hello\u0026quot;: \u0026quot;Hello World\u0026quot; }}
我们将创建2个新端点:
movie:根据给定的电影ID返回一部电影的信息。
director:根据给定的导演ID返回导演的信息,它还将返回该导演指导的所有电影信息。
通常,应用程序将从数据库中读取数据。但在本文中,我们只是简单地在代码中硬编码一些数据。
创建一个叫作data.js的文件并添加以下代码。
//Hardcode some data for movies and directorslet movies = [{ id: 1, name: \u0026quot;Movie 1\u0026quot;, year: 2018, directorId: 1},{ id: 2, name: \u0026quot;Movie 2\u0026quot;, year: 2017, directorId: 1},{ id: 3, name: \u0026quot;Movie 3\u0026quot;, year: 2016, directorId: 3}];let directors = [{ id: 1, name: \u0026quot;Director 1\u0026quot;, age: 20},{ id: 2, name: \u0026quot;Director 2\u0026quot;, age: 30},{ id: 3, name: \u0026quot;Director 3\u0026quot;, 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参数用于指定movie端点的输入。这个端点的输入是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 Queryconst queryType = new GraphQLObjectType({ name: 'Query', fields: { hello: { type: GraphQLString, resolve: function () { return \u0026quot;Hello World\u0026quot;; } }, movie: { type: movieType, args: { id: { type: GraphQLInt } }, resolve: function (source, args) { return _.find(movies, { id: args.id }); } } }});exports.queryType = queryType;
从上面的代码可以看出,movieType实际上是在types.js中定义的。
创建一个叫作types.js的文件,将下面的代码添加到types.js文件中。
const { GraphQLObjectType, GraphQLID, GraphQLString, GraphQLInt} = require('graphql');// Define Movie TypemovieType = 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。在添加这些字段时同时也指定每个字段的类型。
这些字段直接来自之前定义的数据,也就是电影列表。
与movie端点类似,我们也可以添加director端点。
director: { type: directorType, args: { id: { type: GraphQLInt } }, resolve: function (source, args) { return _.find(directors, { id: args.id }); } }
在types.js中添加directorType。
//Define Director TypedirectorType = 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函数?之前我们只在query中看到过这个函数。
当director端点被调用时,我们必须返回导演以及导演所指导的所有电影的信息。
directorType中的前3个字段id、name、age直接来自之前定义的数据(导演列表)。
第四个字段movies需要包含这位导演所指导的电影列表。
为此,movies字段的类型是GraphQLList。
但究竟如何才能找到这位导演指导的所有电影?
为此,我们在movies字段中指定了resolve函数。这个函数的输入是source和args。
source将持有父对象的详细信息。
假设某个导演的字段id = 1、name = “Random”、age = 20, = 1
= “Random”、source.age = 20。因此,在这个示例中,resolve函数将找出directorId与给定导演ID相匹配的所有影片。
这个应用程序的完整代码可以在GitHub()上找到。
现在让我们根据不同的场景来测试这个应用程序。
使用node server.js运行应用程序.
访问localhost:5000/graphql,尝试以下输入。
输入:
{ movie(id: 1) { name }}
输出:
{ \u0026quot;data\u0026quot;: { \u0026quot;movie\u0026quot;: { \u0026quot;name\u0026quot;: \u0026quot;Movie 1\u0026quot; } }}
从上面可以看出,客户端可以明确地请求它想要的东西,GraphQL确保只返回需要的参数。这里只请求name字段,所以服务器只返回这个字段的内容。
在movie(id: 1)中,id是输入参数。我们要求服务器发回id为1的电影。
输入:
{ movie(id: 3) { name id year }}
输出:
{ \u0026quot;data\u0026quot;: { \u0026quot;movie\u0026quot;: { \u0026quot;name\u0026quot;: \u0026quot;Movie 3\u0026quot;, \u0026quot;id\u0026quot;: \u0026quot;3\u0026quot;, \u0026quot;year\u0026quot;: 2016 } }}
在上面的示例中,请求了name、id和year字段,所以服务器返回所有这些字段。
输入:
{ director(id: 1) { name id, age }}
输出:
{ \u0026quot;data\u0026quot;: { \u0026quot;director\u0026quot;: { \u0026quot;name\u0026quot;: \u0026quot;Director 1\u0026quot;, \u0026quot;id\u0026quot;: \u0026quot;1\u0026quot;, \u0026quot;age\u0026quot;: 20 } }}
输入:
{ director(id: 1) { name id, age, movies{ name, year } }}
输出:
{ \u0026quot;data\u0026quot;: { \u0026quot;director\u0026quot;: { \u0026quot;name\u0026quot;: \u0026quot;Director 1\u0026quot;, \u0026quot;id\u0026quot;: \u0026quot;1\u0026quot;, \u0026quot;age\u0026quot;: 20, \u0026quot;movies\u0026quot;: [ { \u0026quot;name\u0026quot;: \u0026quot;Movie 1\u0026quot;, \u0026quot;year\u0026quot;: 2018 }, { \u0026quot;name\u0026quot;: \u0026quot;Movie 2\u0026quot;, \u0026quot;year\u0026quot;: 2017 } ] } }}
英文原文:
转载地址:http://xpasx.baihongyu.com/