GraphQL到底怎么用?看看这个例子就知道了

GraphQL究竟是什么东西?

它实际上是一种API查询语言。

GraphQL显示了服务器可以提供的不同类型的数据,然后客户端就可以明确选择它们想要哪些内容。

在使用GraphQL时,你可以在一个调用中获取多个服务器的资源,而不是像REST API那样需要调用多个API。

理论说得再多也没用,例子才是最直观的。所以,让我们开始使用GraphQL吧。

我们将在本文中使用GraphQL和NodeJS。

先决条件

下载和安装NodeJS:https://nodejs.org/en/

如何在NodeJS中使用GraphQL

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

NodeJS代码

在项目中创建一个叫作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将提供一个叫作/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

graphiql是一个Web UI,你可以用它来测试graphql端点。我们将其设置为true,这样就可以很容易测试我们创建的各种graphql端点。

schema

虽然graphql只提供了一个外部端点/graphql,但它可以拥有多个其他端点,用于执行其他各种操作。这些端点可以在schema中指定。

schema将执行以下操作:

  • 指定端点;

  • 指定端点的输入和输出字段;

  • 指定在端点被调用时应该执行哪些操作,等等。

schema的定义如下。

const schema = new GraphQLSchema({ query: queryType });

schema可以包含查询和可变类型,不过本文只关注查询类型。

query

在这个定义中可以看到,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;

有关这个query的说明

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;

这个文件包含电影和导演的数据。我们将使用这个文件中的数据作为端点的数据来源。

将movie端点添加到query中

新端点将被添加到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中定义的。

添加自定义类型movieType

创建一个叫作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。在添加这些字段时同时也指定每个字段的类型。

这些字段直接来自之前定义的数据,也就是电影列表。

为director端点添加查询和类型

与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中看到过这个函数。

directorType的不同之处

当director端点被调用时,我们必须返回导演以及导演所指导的所有电影的信息。

directorType中的前3个字段id、name、age直接来自之前定义的数据(导演列表)。

第四个字段movies需要包含这位导演所指导的电影列表。

为此,movies字段的类型是GraphQLList。

但究竟如何才能找到这位导演指导的所有电影?

为此,我们在movies字段中指定了resolve函数。这个函数的输入是source和args。

source将持有父对象的详细信息。

假设某个导演的字段id = 1、name = “Random”、age = 20,那么source.id = 1
source.name = “Random”、source.age = 20。

因此,在这个示例中,resolve函数将找出directorId与给定导演ID相匹配的所有影片。

代码

这个应用程序的完整代码可以在GitHub(https://github.com/aditya-sridhar/graphql-with-nodejs)上找到。

测试应用程序

现在让我们根据不同的场景来测试这个应用程序。

使用node server.js运行应用程序.

访问localhost:5000/graphql,尝试以下输入。

movie

输入:

{  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

输入:

{  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        }      ]    }  }}

英文原文:https://dev.to/adityasridhar/what-is-graphql-and-how-to-use-it-1f58

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值