翻译|《JavaScript Everywhere》第4章咱们的第一个GraphQL API(^_^)

翻译 | 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用户界面访问APIapi

将咱们的服务器转换为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自动建立的文档。

image-20201120062343873

4-1GraphQL Playground

注意

GraphQL Playground具备深色的默认语法主题。在整本书中,我将使用“浅色”主题以提升对比度。

这能够在GraphQL Playground的设置中进行配置,能够经过单击齿轮图标进行访问。如今,咱们能够针对咱们的GraphQL API编写查询。为此,在GraphQL Playground中键入如下内容:

query {
   hello
 }

当你单击“播放”按钮时,查询应返回如下内容(图4-2):

{
   "data": {
     "hello": "Hello world!"
   }
 }

image-20201120062638814

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)。

image-20201120062650769

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 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 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的坚实基础。在下一章中,咱们将探讨使用数据库持久化数据的能力。

若是有理解不到位的地方,欢迎你们纠错。若是以为还能够,麻烦您点赞收藏或者分享一下,但愿能够帮到更多人。

相关文章
相关标签/搜索