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

写在最前面

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

为了提高大家的阅读体验,对语句的结构和内容略有调整。如果发现本文中有存在瑕疵的地方,或者你有任何意见或者建议,可以在评论区留言,或者加我的微信: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自动创建的文档。

翻译 | 《JavaScript Everywhere》第4章 我们的第一个GraphQL API(^_^)_第1张图片

4-1GraphQL Playground

注意

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

这可以在GraphQL Playground的设置中进行配置,可以通过单击齿轮图标进行访问。现在,我们可以针对我们的GraphQL API编写查询。为此,在GraphQL Playground中键入以下内容:

 query {
   hello
 }

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

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

翻译 | 《JavaScript Everywhere》第4章 我们的第一个GraphQL API(^_^)_第2张图片

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

翻译 | 《JavaScript Everywhere》第4章 我们的第一个GraphQL API(^_^)_第3张图片

4-3。笔记查询。

在尝试GraphQL最酷的方面之一是我们可以删除任何请求的字段,例如idauthor。当我们这样做时,API精确地返回我们所请求的数据。

这样,使用数据的客户端可以控制每个请求中发送的数据量,并将该数据限制在所需的范围内(图4-4)。

翻译 | 《JavaScript Everywhere》第4章 我们的第一个GraphQL API(^_^)_第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的坚实基础。在下一章中,我们将探讨使用数据库持久化数据的能力。

如果有理解不到位的地方,欢迎大家纠错。如果觉得还可以,麻烦您点赞收藏或者分享一下,希望可以帮到更多人。

你可能感兴趣的:(前端,javascript)