【AICon】 如何构建高效的 RAG 系统?RAG 技术在实际应用中遇到的挑战及应对策略?>>> 了解详情
写点什么

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

  • 2019-01-26
  • 本文字数:4797 字

    阅读完需:约 16 分钟

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) => { 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 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 "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 中导入它。

运行应用程序

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


node server.js
复制代码


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


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


Graphiql Web UI 如下图所示。



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


给定以下输入:


{  hello}
复制代码


将给出以下输出:


{  "data": {    "hello": "Hello World"  }}
复制代码

添加更多端点

我们将创建 2 个新端点:


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

  • director:根据给定的导演 ID 返回导演的信息,它还将返回该导演指导的所有电影信息。

添加数据

通常,应用程序将从数据库中读取数据。但在本文中,我们只是简单地在代码中硬编码一些数据。


创建一个叫作 data.js 的文件并添加以下代码。


//Hardcode some data for movies and directorslet 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 Queryconst 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 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  }}
复制代码


输出:


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


2019-01-26 07:0019255
用户头像

发布了 731 篇内容, 共 430.5 次阅读, 收获喜欢 1995 次。

关注

评论 3 条评论

发布
用户头像
请问iOS客户端该如何使用graphql啊
2021-05-03 16:02
回复
用户头像
[types.js]
movieType = ....
这里应该少了个const吧,还是只是演示代码而已。
2019-01-27 17:20
回复
用户头像
最近infoQ狂推graphQL啊
2019-01-25 21:16
回复
没有更多了
发现更多内容

企评家 | 梅花生物科技集团股份有限公司成长性评价简介

企评家

Vue数据响应Object.defineProperty

空城机

vue.js 5月月更

一文读懂Move2Earn项目——MOVE

小哈区块

JavaWeb JDBC

Emperor_LawD

javaWeb JDBC 5月月更

一文读懂Move2Earn项目——MOVE

西柚子

总工程师直播解析:隐私计算是什么,都有哪些核心技术和典型应用场景? | 第17 期

OpenAnolis小助手

隐私计算 龙蜥社区 sig 龙蜥大讲堂 海泰方圆

看透说破:客户服务首解率(FCR)的迷思

龙国富

客户体验 首解率

前端学习笔记(一):css学习笔记

恒山其若陋兮

5月月更

国内首批|观测云获得信通院 “可观测性平台技术能力”最高级认证!

观测云

可观测性 可观测

TiDB 冷热存储分离解决方案

TiDB 社区干货传送门

企评家 | 浙江永贵电器股份有限公司性评价简介

企评家

企评家企业大数据平台,助力企业刻画企业成长性画像

企评家

【LeetCode】验证外星语词典Java题解

Albert

LeetCode 5月月更

基于SVN的CICD调研方案

ZuccRoger

5月月更

【刷题第 11 天】26. 删除有序数组中的重复项

白日梦

5月月更

蚂蚁集团 Service Mesh 进展回顾与展望

SOFAStack

架构 开发者 API Service Mesh 服务网格

[数据分析实践]-Image Matching-2DTo3D-1

浩波的笔记

人工智能 机器学习 数据分析

Redis「6」实现消息队列

Samson

学习笔记 Redis 核心技术与实战 5月月更

Java 8 开始新增的 Optional 类 - 创建 Optional 对象

HoneyMoose

【活动预告】云上数字工厂与中小企业数字化转型创新论坛

阿里云弹性计算

数字化转型 中小企业 云盒

ArrayList源码分析-迭代器

zarmnosaj

5月月更

拆分电商系统为微服务

哈喽

「架构实战营」

BSC币安NFT链游系统DAPP开发技术

薇電13242772558

智能合约 NFT

PolarDB-X 源码解读:DDL的一生(上)

阿里云数据库开源

数据库 阿里云 开源 分布式 polarDB

焱融看|2022 年存储自动化的 5 大趋势

焱融科技

云计算 分布式 云原生 高性能 文件存储

在线文本列表交集计算工具

入门小站

工具

在线TSV某一列提取下载

入门小站

工具

企评家 | 如何评价企评家企业成长性评价系统?

企评家

难对齐、难保障、难管理?一文了解字节跳动如何解决数据SLA治理难题

字节跳动数据平台

字节跳动 数据治理 SLA 数据研发

linux之sudo使用技巧汇总

入门小站

Linux

Java 8 开始新增的 Optional 类 - 检查 Optional 的值

HoneyMoose

GraphQL到底怎么用?看看这个例子就知道了_大前端_Aditya Sridhar_InfoQ精选文章