写在最后面

大家好呀,我是毛小悠,是一位前端开发工程师。正在翻译一本英文技术书籍。

为了进步大家的浏览体验,对语句的构造和内容略有调整。如果发现本文中有存在瑕疵的中央,或者你有任何意见或者倡议,能够在评论区留言,或者加我的微信:code_maomao,欢送互相沟通交流学习。

(゚∀゚)..:*☆哎哟不错哦

第4章 咱们的第一个GraphQL API

依据揣测,如果你正在浏览本文,那么你就是一个人。作为人类,你有很多趣味和喜好,你也有家人、敌人、熟人、同学和共事。这些人也有本人的社会关系、趣味和喜好。这些关系和趣味中有些重叠,有些没有。总而言之,咱们每个人都有咱们生存中人的关系图。这些类型的互连数据正是GraphQL最后提出要在API开发中解决的挑战。

通过编写GraphQL API,咱们可能高效地连贯数据,从而升高了复杂性和申请数量,同时使咱们可能精确地为客户提供所需的数据。听起来对Notes应用程序来说有点过了,是吗?兴许是吧,然而正如你将看到的,GraphQL JavaScript生态系统提供的工具和技术都能够启用和简化所有类型的API开发。

在本章中,咱们将应用apollo-server-express包来构建GraphQL API。为此,咱们将探索GraphQL的根本主题,编写GraphQL模式,编写代码以解析模式性能,并应用GraphQL Playground用户界面拜访API

将咱们的服务器转换为API(排序)

让咱们通过应用以下命令将Express服务器变成GraphQL服务器来开始API开发。

apollo-server-express软件包。

Apollo Server是一个开源GraphQL服务器库,可与大量Node.js服务器框架一起应用,包含ExpressConnectHapiKoa

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-1GraphQL 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的变量中。数组中的每个笔记将是一个具备三个属性(idcontentauthor)的对象:

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/apiGraphQL Playground,咱们能够测试笔记查问。

为此,请键入以下查问:

query {  notes {    id    content    author  }}

而后,当你单击“播放”按钮时,应该看到返回的数据对象,其中蕴含数据数组(图4-3)。

4-3。笔记查问。

在尝试GraphQL最酷的方面之一是咱们能够删除任何申请的字段,例如idauthor。当咱们这样做时,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/apiGraphQL 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的函数,该函数具备parentargs参数。在此函数中,咱们将应用参数content并创立一个具备idcontentauthor键的对象。

你可能曾经留神到,这与笔记的以后模式匹配。而后,咱们将该对象退出到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 4000const 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 languageconst 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 fieldsconst 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 setupconst server = new ApolloServer({ typeDefs, resolvers });// Apply the Apollo GraphQL middleware and set the path to /apiserver.applyMiddleware({ app, path: '/api' });app.listen({ port }, () =>  console.log(    `GraphQL Server running at http://localhost:${port}${server.graphqlPath}`  ));

更新构造和解析器以承受批改后,让咱们在GraphQL Playgroundhttp://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的坚实基础。在下一章中,咱们将探讨应用数据库长久化数据的能力。

如果有了解不到位的中央,欢送大家纠错。如果感觉还能够,麻烦您点赞珍藏或者分享一下,心愿能够帮到更多人。