24

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

 5 years ago
source link: https://www.infoq.cn/article/i5JMm54_aWrRZcem1VgH?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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-nodejs
npminit

安装依赖项

使用以下命令安装 Express。

复制代码

npminstallexpress

使用以下命令安装 GraphQL。我们将安装 graphql 和 express-graphql。

复制代码

npminstallexpress-graphql graphql

NodeJS 代码

在项目中创建一个叫作 server.js 的文件,并将下面的代码复制到文件中。

复制代码

constexpress =require('express');
constport =5000;
constapp = express();

app.get('/hello',(req,res) =>{
    res.send("hello");
   }
);

app.listen(port);
console.log(`Server Running at localhost:${port}`);

上面的代码提供了一个叫作 /hello 的 HTTP 端点。

这个端点是使用 express 创建的。

现在让我们修改代码,启用 GraphQL。

修改代码,启用 GraphQL

GraphQL 将提供一个叫作 /graphql 的端点,负责处理所有的请求。

将下面的代码复制到 server.js 文件中。

复制代码

//get all the libraries needed
constexpress =require('express');
constgraphqlHTTP =require('express-graphql');
const{GraphQLSchema} =require('graphql');

const{queryType} =require('./query.js');

//setting up the port number and express app
constport =5000;
constapp = express();

 // Define the Schema
constschema =newGraphQLSchema({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}`);

我们在 /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 =newGraphQLSchema({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 Query
constqueryType =newGraphQLObjectType({
   name:'Query',
   fields: {
       hello: {
           type: GraphQLString,

           resolve:function(){
               return"Hello World";
            }
        }
    }
});

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 中导入它。

运行应用程序

使用以下命令运行这个应用程序。

复制代码

nodeserver.js

应用程序将运行在 localhost:5000/graphql 上。

你可以通过访问 localhost:5000/graphql 来测试应用程序。

Graphiql Web UI 如下图所示。

eQZRZru.png!web

左侧是输入,右侧是输出。

给定以下输入:

复制代码

{
  hello
}

将给出以下输出:

复制代码

{
 "data": {
   "hello":"Hello World"
  }
}

添加更多端点

我们将创建 2 个新端点:

  • movie:根据给定的电影 ID 返回一部电影的信息。

  • director:根据给定的导演 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;

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

将 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 Query
constqueryType =newGraphQLObjectType({
   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 =newGraphQLObjectType({
    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 Type
directorType =newGraphQLObjectType({
    name:'Director',
    fields: {
        id: {type:GraphQLID},
        name: {type:GraphQLString},
        age: {type:GraphQLInt},
        movies: {
           type:newGraphQLList(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
  }
}

输出:

复制代码

{
 "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

输入:

复制代码

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK