翻译
|
《JavaScript Everywhere
》第4
章
咱们的第一个GraphQL API
(^_^)前端
写在最前面
你们好呀,我是毛小悠,是一位前端开发工程师。正在翻译一本英文技术书籍。node
为了提升你们的阅读体验,对语句的结构和内容略有调整。若是发现本文中有存在瑕疵的地方,或者你有任何意见或者建议,能够在评论区留言,或者加个人微信:code
_maomao
,欢迎相互沟通交流学习。数据库
(σ゚∀゚)σ..:
*☆哎哟不错哦express
第4章 咱们的第一个GraphQL API
根据推测,若是你正在阅读本文,那么你就是一我的。做为人类,你有不少兴趣和爱好,你也有家人、朋友、熟人、同窗和同事。这些人也有本身的社会关系、兴趣和爱好。这些关系和兴趣中有些重叠,有些没有。总而言之,咱们每一个人都有咱们生活中人的关系图。这些类型的互连数据正是GraphQL
最初提出要在API
开发中解决的挑战。npm
经过编写GraphQL API
,咱们可以高效地链接数据,从而下降了复杂性和请求数量,同时使咱们可以准确地为客户提供所需的数据。听起来对Notes
应用程序来讲有点过了,是吗?也许是吧,可是正如你将看到的,GraphQL JavaScript
生态系统提供的工具和技术均可以启用和简化全部类型的API
开发。json
在本章中,咱们将使用apollo-server-express
包来构建GraphQL API
。为此,咱们将探究GraphQL
的基本主题,编写GraphQL
模式,编写代码以解析模式功能,并使用GraphQL Playground
用户界面访问API
。api
将咱们的服务器转换为API(排序)
让咱们经过使用如下命令将Express
服务器变成GraphQL
服务器来开始API
开发。数组
apollo-server-express
软件包。浏览器
Apollo Server
是一个开源GraphQL
服务器库,可与大量Node.js
服务器框架一块儿使用,包括Express
,Connect
,Hapi
和Koa
。服务器
GraphQL API
,可从Node.js
应用程序中获取数据,还提供了有用的工具,例如GraphQL Playground
,一个可视化的辅助器,帮助咱们在开发中查看API
。
要编写咱们的API
,咱们将修改上一章中编写的Web
应用程序的代码。让咱们首先开始于apollo-server-express
软件包。
将如下内容添加到src/index.js
文件的顶部:
const { ApolloServer, gql } = require('apollo-server-express');
如今咱们已经导入了apollo
服务器,咱们将创建一个基本的GraphQL
应用程序。
GraphQL
应用程序由两个主要组件组成:类型定义和解析器,用于解析针对数据执行的查询和修改。
这听起来像是胡话,但不要紧。
咱们将实现“ Hello World
” API
响应,并将在咱们API
的整个开发过程当中进一步探讨这些GraphQL
主题。
首先,让咱们构建一个基本模式,将其存储在一个名为typeDefs
的变量中。
该模式将描述一个名为hello
的查询,该查询将返回一个字符串:
// Construct a schema, using GraphQL schema language const typeDefs = gql` type Query { hello: String } `;
如今咱们已经设置告终构,咱们能够添加一个解析器,该解析器将向用户返回一个值。这只是一个简单的函数,返回字符串“ Hello world
!”:
// Provide resolver functions for our schema fields const resolvers = { Query: { hello: () => 'Hello world!' } };
最后,咱们将集成Apollo Server
以提供GraphQL API
。
为此,咱们将添加一些特定于Apollo Server
的设置和中间件,并更新咱们的应用程序。
监听代码:
// Apollo Server setup const server = new ApolloServer({ typeDefs, resolvers }); // Apply the Apollo GraphQL middleware and set the path to /api server.applyMiddleware({ app, path: '/api' }); app.listen({ port }, () => console.log( `GraphQL Server running at http://localhost:${port}${server.graphqlPath}` ) );
综上,咱们的src/index.js
文件如今应以下所示:
const express = require('express'); const { ApolloServer, gql } = require('apollo-server-express'); // Run the server on a port specified in our .env file or port 4000 const port = process.env.PORT || 4000; // Construct a schema, using GraphQL's schema language const typeDefs = gql` type Query { hello: String } `; // Provide resolver functions for our schema fields const resolvers = { Query: { hello: () => 'Hello world!' } }; const app = express(); // Apollo Server setup const server = new ApolloServer({ typeDefs, resolvers }); // Apply the Apollo GraphQL middleware and set the path to /api server.applyMiddleware({ app, path: '/api' }); app.listen({ port }, () => console.log( `GraphQL Server running at http://localhost:${port}${server.graphqlPath}` ) );
若是你没有运行nodemon
进程,则能够直接进入浏览器。不然,你必须在终端应用程序中输入npm
运行dev
以启动服务器。而后访问http://localhost:4000/api
,在这里你会看到GraphQL Playground
(图4-1
)。
该Web
应用程序与Apollo Server
捆绑在一块儿,是使用GraphQL
的巨大好处之一。从这里,你能够运行GraphQL
查询和修改并查看结果。
你也能够单击“Schema
”选项卡来访问为API
自动建立的文档。
图4-1
。GraphQL Playground
注意
GraphQL Playground
具备深色的默认语法主题。在整本书中,我将使用“浅色”主题以提升对比度。
这能够在GraphQL Playground
的设置中进行配置,能够经过单击齿轮图标进行访问。如今,咱们能够针对咱们的GraphQL API
编写查询。为此,在GraphQL Playground
中键入如下内容:
query { hello }
当你单击“播放”按钮时,查询应返回如下内容(图4-2
):
{ "data": { "hello": "Hello world!" } }
图4-2
。你好查询
就是这样!如今,咱们已经能够经过GraphQL Playground
访问有效的GraphQL API
。咱们的API
会查询hello
,并返回字符串Hello world
!。
更重要的是,咱们如今具备了功能构建齐全的API
的结构。
😙
GraphQL基础
在上一节中,咱们深刻探讨并开发了咱们的第一个API
,先让咱们花一些时间后退一步来看看GraphQL API
的不一样部分。
GraphQL API
的两个主要构建模块是模式和解析器。
经过理解这两个组件,能够更有效地将它们应用于API
设计和开发。
Schemas
模式是咱们的数据和交互的书面表示。
经过请求一个模式,GraphQL
规范了咱们的API
请求。
这是由于你的API
只能返回数据并执行架构中定义的交互。
GraphQL
模式的基本组成部分是对象类型。
在前面的示例中,咱们建立了一个GraphQL
对象类型的Query
,带有一个hello
字段,该对象返回了标准的String
类型。
GraphQL
包含五种内置标量类型:
-
串
:String
具备
UTF-8
字符编码的字符串 -
布尔型
Boolean
正确或错误的值
-
整数
Int
32
位整数 -
浮点型
Flaot
浮点值
-
ID
惟一标识符
使用这些基本组件,咱们能够为API
构建一个模式。
咱们首先定义类型。假设咱们正在为披萨菜单建立一个API
。
这时,咱们能够定义披萨的GraphQL
模式类型,以下所示:
type Pizza { }
如今,每一个披萨饼都有惟一的ID
,大小(例如小,中或大),切片数和可选的浇头。
Pizza
模式可能看起来像这样:
type Pizza { id: ID size: String slices: Int toppings: [String] }
在此架构中,某些字段值是必需的(例如ID
,大小和切片),而其余字段值则是可选的(例如配料)。咱们可使用感叹号来表示字段必须包含一个值。
让咱们更新结构以表示所需的值:
type Pizza { id: ID! size: String! slices: Int! toppings: [String] }
在本书中,咱们将编写一个基本模式,这将使咱们可以执行常见API
中的绝大多数操做。若是你想探索全部的GraphQL
模式选项,建议你阅读GraphQL
模式文档。
解析器Resolvers
GraphQL API
的第二部分是解析器。
解析程序彻底执行其名称所暗示的操做;他们解析API
而后获取用户已请求的数据。
咱们将首先在结构中定义这些解析器,而后在JavaScript
代码中实现逻辑,以编写这些解析器。
咱们的API
将包含两种类型的解析器:查询和修改。
查询
查询从API
请求中获取所需格式的特定数据。
在咱们假设的披萨API
中,咱们能够编写一个查询,该查询将返回菜单上披萨的完整列表,而另外一个查询将返回有关单个披萨的详细信息。而后查询将返回一个对象,其中包含API
中用户请求的数据。查询从不修改数据,仅访问数据。
修改
当咱们想要修改API
中的数据时,咱们使用一个修改。
在咱们的披萨示例中,咱们可能编写了一个修改,该修改能够更改给定披萨的配料,而另外一个修改则能够调整切片数。
与查询相似,也指望修改以对象的形式返回结果,一般是所执行操做的最终结果。
调整咱们的API
如今你已经对GraphQL
的组件有了很好的了解,让咱们为Notes
应用程序调整咱们的初始API
代码。
首先,咱们将编写一些代码来阅读和建立笔记。咱们须要作的第一件事是为API
提供一些数据。让咱们建立一个“笔记”对象数组,将其用做API
提供的基本数据。随着项目的发展,咱们将用数据库替换这个数据。
如今,咱们将数据存储在一个名为notes
的变量中。数组中的每一个笔记将是一个具备三个属性(id
,content
和author
)的对象:
let notes = [ { id: '1', content: 'This is a note', author: 'Adam Scott' }, { id: '2', content: 'This is another note', author: 'Harlow Everly' }, { id: '3', content: 'Oh hey look, another note!', author: 'Riley Harrison' } ];
如今咱们有了一些数据,咱们将调整咱们的GraphQL API
来使用它。
让咱们从关注咱们的模式开始。咱们的模式是GraphQL
对咱们的数据以及如何与之交互的表示。已知咱们会有笔记,这些笔记将被查询和修改。这些笔记目前将包含ID
、内容和做者3
个字段。
让咱们在typeDefs GraphQL
模式中建立一个相应的笔记类型。
如下表示咱们API
中笔记的属性:
type Note { id: ID! content: String! author: String! }
如今,让咱们添加一个查询,该查询将容许咱们检索全部笔记的列表。
让咱们更新一个笔记查询,这将返回笔记对象的数组:
type Query { hello: String! notes: [Note!]! }
如今,咱们能够更新解析器代码以执行返回数据数组的工做。
让咱们更新包括如下笔记解析器的查询代码,该解析器返回原始数据对象:
Query: { hello: () => 'Hello world!', notes: () => notes },
若是如今切换到运行在http://localhost:4000/api
的GraphQL Playground
,咱们能够测试笔记查询。
为此,请键入如下查询:
query { notes { id content author } }
而后,当你单击“播放”按钮时,应该看到返回的数据对象,其中包含数据数组(图4-3
)。
图4-3
。笔记查询。
在尝试GraphQL
最酷的方面之一是咱们能够删除任何请求的字段,例如id
或author
。当咱们这样作时,API
精确地返回咱们所请求的数据。
这样,使用数据的客户端能够控制每一个请求中发送的数据量,并将该数据限制在所需的范围内(图4-4
)。
图4-4
。笔记查询,仅请求内容数据。
如今咱们能够查询完整的笔记列表,让咱们编写一些代码,使咱们能够查询单个笔记。
从用户界面的角度,你能够想象这样作的用处,能够显示包含单个特定笔记的视图。为此,咱们但愿请求带有特定id
值的笔记。这要求咱们使用GraphQL
模式中的参数。参数容许API
使用者将特定的值传递给解析器函数,从而提供解析所需的信息。让咱们添加一个笔记查询,该查询将使用id
类型的ID
做为参数。
咱们将typeDefs
中的Query
对象更新为如下内容,其中包括新的笔记查询:
type Query { hello: String notes: [Note!]! note(id: ID!): Note! }
更新结构后,咱们已经能够编写在查询解析器后返回的笔记了。如今咱们须要可以读取API
用户的参数值。
有用信息
Apollo Server
将如下有用的参数传递给咱们的解析器功能:
-
parent
父查询的结果,在嵌套查询时颇有用。
-
args
这些是用户在查询中传递的参数。
-
context
信息从服务器应用程序传递到解析器功能。
这可能包括诸如当前用户或数据库信息之类的信息。
-
info
有关查询自己的信息。
咱们将根据须要在代码中进一步探索这些内容。若是你感到好奇,能够在Apollo
服务器的文档中了解有关这些参数的更多信息。
如今,咱们仅须要第二个参数args
中包含的信息。
笔记查询将笔记ID
做为参数,在咱们的笔记对象数组中找到它。将如下内容添加到查询解析器代码中:
note: (parent, args) => { return notes.find(note => note.id === args.id); }
解析器代码如今应以下所示:
const resolvers = { Query: { hello: () => 'Hello world!', notes: () => notes, note: (parent, args) => { return notes.find(note => note.id === args.id); } } };
运行咱们的查询,让咱们返回到Web
浏览器并访问位于http://localhost:4000/api
的GraphQL Playground
。
如今,咱们能够查询具备特定ID
的笔记,以下所示:
query { note(id: "1") { id content author } }
运行此查询时,你应该收到带有请求的id
值的笔记的结果。若是你尝试查询不存在的笔记,则应收到结果为null
的结果。要对此进行测试,请尝试更改id
值以返回不一样的结果。
让咱们经过使用GraphQL
修改引入建立新笔记的功能来强化咱们的初始API
代码。在这种修改中,用户将传递笔记的内容。
如今,咱们将对笔记的做者进行编码。
让咱们首先使用Mutation
类型更新咱们的typeDefs
模式,咱们将其称为newNote
:
type Mutation { newNote(content: String!): Note! }
如今,咱们将编写一个修改解析器,它将笔记内容做为参数,将笔记存储为对象,而后将其添加到notes
数组中。咱们将Mutation
对象添加到解析器。
在Mutation
对象中,咱们将添加一个名为newNote
的函数,该函数具备parent
和args
参数。在此函数中,咱们将使用参数content
并建立一个具备id
,content
和author
键的对象。
你可能已经注意到,这与笔记的当前模式匹配。而后,咱们将该对象加入到notes
数组并返回该对象。返回的对象容许GraphQL
修改接收预期格式的响应。
继续并编写如下代码:
Mutation: { newNote: (parent, args) => { let noteValue = { id: String(notes.length + 1), content: args.content, author: 'Adam Scott' }; notes.push(noteValue); return noteValue; } }
咱们的src/index.js
文件如今将以下所示:
const express = require('express'); const { ApolloServer, gql } = require('apollo-server-express'); // Run our server on a port specified in our .env file or port 4000 const port = process.env.PORT || 4000; let notes = [ { id: '1', content: 'This is a note', author: 'Adam Scott' }, { id: '2', content: 'This is another note', author: 'Harlow Everly' }, { id: '3', content: 'Oh hey look, another note!', author: 'Riley Harrison' } ]; // Construct a schema, using GraphQL's schema language const typeDefs = gql` type Note { id: ID! content: String! author: String! } type Query { hello: String notes: [Note!]! note(id: ID!): Note! } type Mutation { newNote(content: String!): Note! } `; // Provide resolver functions for our schema fields const resolvers = { Query: { hello: () => 'Hello world!', notes: () => notes, note: (parent, args) => { return notes.find(note => note.id === args.id); } }, Mutation: { newNote: (parent, args) => { let noteValue = { id: String(notes.length + 1), content: args.content, author: 'Adam Scott' }; notes.push(noteValue); return noteValue; } } }; const app = express(); // Apollo Server setup const server = new ApolloServer({ typeDefs, resolvers }); // Apply the Apollo GraphQL middleware and set the path to /api server.applyMiddleware({ app, path: '/api' }); app.listen({ port }, () => console.log( `GraphQL Server running at http://localhost:${port}${server.graphqlPath}` ) );
更新结构和解析器以接受修改后,让咱们在GraphQL Playground
的http://localhost:4000/api
上进行尝试。
在Playground
上,单击+号以建立一个新选项卡,并按以下所示编写变量:
mutation { newNote (content: "This is a mutant note!") { content id author } }
当你单击“播放”按钮时,你应该会收到包含新笔记的内容、ID
和做者的结果。
你还能够经过从新运行notes
查询来查看该修改是否起做用。
为此,请切换回包含该查询的GraphQL Playground
选项卡,或键入如下内容:
query { notes { content id author } }
运行此查询时,你如今应该看到四个笔记,包括最近添加的笔记。
数据存储
咱们将数据存储在内存中。这意味着,只要咱们从新启动服务器,就会丢失该数据。在下一章中,咱们将使用数据库来持久化数据。
如今,咱们已经成功实现了查询和修改解析器,并在GraphQL Playground
用户界面中对其进行了测试。
结论
在本章中,咱们已经使用apollo-server-express
模块成功构建了GraphQL API
。如今,咱们能够对内存中的数据对象运行查询和修改。此设置为咱们提供了构建任何API
的坚实基础。在下一章中,咱们将探讨使用数据库持久化数据的能力。
若是有理解不到位的地方,欢迎你们纠错。若是以为还能够,麻烦您点赞收藏或者分享一下,但愿能够帮到更多人。