GraphQL 初学者完整指南

2025-05-24

GraphQL 初学者完整指南

2024 年 6 月 27 日:这篇博文使用 Amplify Gen 1,如果您要启动新的 Amplify 应用程序,我建议您尝试Gen 2

我第一次使用 GraphQL 是在 2015 年 GraphQL 发布后不久,当时我在一个项目中,说实话,我当时并不明白为什么要用它。多年来,我越来越喜欢 GraphQL——你可以使用 AWS AppSync 和 Hasura 等托管服务快速创建 API,而且它还能减少前端和后端开发之间的摩擦。在这篇文章中,我们将讨论什么是 GraphQL,为什么要使用它,以及什么时候它可能不是最佳选择,然后使用 GraphQL API 创建一个完整的食谱应用程序。

请注意,我是 AWS Amplify 团队的开发倡导者,如果您对此有任何反馈或疑问,请联系我或在我们的 discord 上提问 - discord.gg/amplify!

如果您是 API 新手,我建议您先阅读这篇关于 API 的博客文章!如果您对 REST 的概念还不熟悉,我也建议您先阅读这篇文章;这篇文章经常会将 GraphQL 与 REST 进行比较。我还会使用React来处理前端的一些部分——建议您在阅读这篇文章之前先浏览一下相关的教程。

此外,我们将在本文中使用一些词汇:

  • 模式:这是数据结构方式的表示。
  • 字段:这些是与数据相关的属性。

什么是 GraphQL?

根据其文档,“GraphQL 是一种用于 API 的查询语言,也是一个服务器端运行时,用于使用您为数据定义的类型系统执行查询。” GraphQL 本身是一种规范,这意味着有一个文档概述了 GraphQL 查询的样子以及客户端-服务器交互如何使用它;但是,它可以与您应用程序的任何编程语言或数据层一起使用。

实际上,这允许前端开发人员向后端发送查询,请求他们需要的数据(包括嵌套数据)。这使得后端开发人员只需创建一个端点,而无需像 REST API 那样创建多个端点。您可以将更改数据的操作和检索数据的查询全部发送到一个地方。

为什么要使用 GraphQL?

GraphQL 受欢迎的原因有很多。首先,它简化了前端和后端开发人员之间的沟通——前端开发人员无需在需求发生变化时请求新的端点,只需更新其 GraphQL 查询即可。如果您有多个前端需要相同的后端数据,这将变得更加有用。前端开发人员可以准确获取所需的数据——不会出现字段或项获取不足或过度的情况。

由于前端开发人员可以使用一个查询来请求嵌套数据,因此网络请求也得到了最小化——例如,如果您查询一篇博客文章,那么您可以在一个查询中同时获取该文章的评论,而无需再次发出请求。这也可以减少所需的前端代码量,并使代码更易于理解。

GraphQL 还强制使用类型化的数据模式,因此每个条目的字段必须与这些类型匹配。这使得数据更加一致且易于管理——无需循环遍历博客文章并确定每个标题是字符串还是布尔值,GraphQL 会强制每个标题都是字符串。

GraphQL 什么时候不那么好?

与软件工程中的任何事物一样,使用 GraphQL 也有缺点。首先,我在 2015 年左右 GraphQL 发布时就开始使用它了,但我讨厌它。我当时是一个小团队的全栈工程师,构建后端的工作量很大,而前端则需要更冗长。GraphQL 查询通常很长,而对于许多 REST API,您只需提供一个 url 即可。此外,与 REST 相比,许多后端框架和语言对 GraphQL API 的支持不太成熟。您可能需要做更多的工作并浏览较少使用的库才能获取 GraphQL Api。如果您是创建和使用端点的人,那么构建 REST API 可能会更快——尤其是当您使用的编程语言或框架对 GraphQL 支持不太成熟时。

GraphQL 在大型团队中大放异彩,这些团队由前端团队开发客户端,并由单独的团队开发服务器端。此外,越来越多的托管 GraphQL 服务出现,例如 Hasura 和 AWS AppSync。这些服务允许你使用它们的服务生成 GraphQL 后端,然后在前端使用它——与从头编写 GraphQL 服务器相比,这通常可以显著加快后端开发的速度。

最后,许多开发人员在职业生涯早期就学习了如何使用和创建 REST API,并且可能对 GraphQL 缺乏系统性的了解。让整个团队快速上手或许是一项值得考虑的投资。

创建 GraphQL API

现在到了最有趣的部分,让我们开始写代码吧!我们将使用 AWS Amplify 来创建一个 GraphQL 后端——这将加快流程,让我们只专注于 GraphQL,而不是后端开发的其他部分。

首先,我将创建一个 React 应用程序——这里没有太多的 React 代码,但设置比使用捆绑器创建 Vanilla JS 应用程序更快。

在你的终端中运行:

npx create-react-app graphql-playground
cd graphql-playground
Enter fullscreen mode Exit fullscreen mode

注意:您需要安装Node才能执行此步骤。

接下来,我们将在我们的项目中初始化 Amplify。

amplify init
Enter fullscreen mode Exit fullscreen mode

注意:您需要安装Amplify才能执行此步骤。

然后,系统会提示您回答几个问题。您可以输入“y”以获取默认的 React 配置,然后选择您的 AWS 配置文件(如果您没有,请参阅上面的教程!)

Project information
| Name: graphqldemo
| Environment: dev
| Default editor: Visual Studio Code
| App type: javascript
| Javascript @framework: react
| Source Directory Path: src
| Distribution Directory Path: dist
| Build Command: npm run-script build
| Start Command: npm run-script start

? Initialize the project with the above configuration? Yes
Using default provider awscloudformation
? Select the authentication method you want to use: AWS profile

For more information on AWS Profiles, see:
https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html

? Please choose the profile you want to use default
Enter fullscreen mode Exit fullscreen mode

现在,我们将创建一个 GraphQL API。运行:

amplify add api
Enter fullscreen mode Exit fullscreen mode

系统会再次询问您几个问题!首先,选择 GraphQL,然后为您的 API 命名,例如 graphql demo。然后,您可以按两次 Enter 键接受 API 密钥的默认值。然后,您可以选择 GraphQL API 和 GraphQL 架构的“否”。选择“一对多关系”模板,然后单击“是”以立即编辑架构。

? Please select from one of the below mentioned services: GraphQL
? Provide API name: graphqldemo
? Choose the default authorization type for the API API key
? Enter a description for the API key:
? After how many days from now the API key should expire (1-365): 7
? Do you want to configure advanced settings for the GraphQL API No, I am done.
? Do you have an annotated GraphQL schema? No
? Choose a schema template: One-to-many relationship (e.g., “Blogs” with “Posts” and “Comments”)
? Do you want to edit the schema now? Yes
? Choose your default editor: Visual Studio Code
Enter fullscreen mode Exit fullscreen mode

您会看到一个预先生成的模式弹出,让我们来讨论一下博客模型。

type Blog @model {
  id: ID!
  name: String!
  posts: [Post] @connection(keyName: "byBlog", fields: ["id"])
}
Enter fullscreen mode Exit fullscreen mode

TODO:添加标量类型列表

type- 该词type用于表示您可以从 API 中获取的一种对象类型 - 在本例中是博客!

Blog- 这是类型的名称

@model- @GraphQl 中的符号定义了一个指令,这意味着某个字段或类型具有与其关联的自定义逻辑。Amplify提供了许多这样的指令供您使用。该@model指令使得博客的数据能够存储在我们的数据库中。

id、、nameposts- 这些是每个博客都会有的字段或数据片段

ID并且String- 这些是类型,它们定义了id将是 类型id,而name将是字符串。这些字段是,scalar这意味着它们是单独的数据——一个 ID 和一个名称,而不是每个博客文章都有一个名称集合。

!- 类型后面的感叹号表示该字段不可为空,或者您始终需要为该字段提供一个值。在这种情况下,每个博客都必须有一个 ID 和名称!

[Post]- 首先,[]将其设置为数组字段。每个博客都可以关联一个帖子数组。您也可以使用标量类型来实现这一点,这样[String]就可以使用字符串数组。在本例中,我们引用的Post模型也在此文件中声明,因此这两种数据类型是相互关联的。

@connection- 这是另一个指令,在本例中,它允许我们将一个模型与另一个模型关联起来。你需要向它提供一些数据,在本例中keyNamefields

keyName- 这是用于查询相关帖子的索引的名称。您会注意到,模型中Post有一个@key指令定义了一个名称。该键的名称将与此处的 匹配。每当您在 Amplify 中有一个一对多字段时,keyName您都需要定义一个,然后使用来引用它。@keykeyName

fields- 这是可以查询以获取连接对象的字段。

现在,让我们将其替换为我们的模式。我们将创建一本食谱。首先,让我们创建三个模型,RecipeIngredientInstruction

type Recipe @model {
}

type Ingredient @model {
}

type Instruction @model {
}
Enter fullscreen mode Exit fullscreen mode

现在,让我们为每个模型添加字段。每个模型都需要一个id,它将是必填ID字段。然后,我们将names 添加到RecipeIngredientIngredient也将包含数量,并且Instruction将包含info

type Recipe @model {
  id: ID!
  name: String!
}

type Ingredient @model {
  id: ID!
  name: String!
  quantity: String!
}

type Instruction @model{
  id: ID!
  info: String!
}
Enter fullscreen mode Exit fullscreen mode

现在,我们需要连接我们的模型。首先,我们将向@key两个子模型添加指令——Ingredient因为sInstructionRecipe同时包含这两个指令!我们希望能够通过它们所属的食谱来访问Ingredients 和s。每个模型都有一个,它指向它们所属的食谱。然后,我们将基于该 创建一个与模型的连接。最后,我们将在每个模型上设置,以便我们访问属于某个食谱的配料组或说明。InstructionrecipeIDReciperecipeID@key

type Ingredient @model @key(name: "byRecipe", fields: ["recipeID"]) {
  id: ID!
  name: String!
  quantity: String!
  recipeID: ID!
  recipe: Recipe @connection(fields: ["recipeID"])
}

type Instruction @model @key(name: "byRecipe", fields: ["recipeID"]) {
  id: ID!
  info: String!
  recipeID: ID!
  recipe: Recipe @connection(fields: ["recipeID"])
}
Enter fullscreen mode Exit fullscreen mode

最后,我们将添加从Recipe模型到每个成分和说明的连接。

type Recipe @model {
  id: ID!
  name: String!
  ingredients: [Ingredient] @connection(keyName: "byRecipe", fields: ["id"])
  instructions: [Instruction] @connection(keyName: "byRecipe", fields: ["id"])
}
Enter fullscreen mode Exit fullscreen mode

现在,我们需要部署数据!运行程序amplify push会在云端为我们创建一个 GraphQL API。

amplify push -y
Enter fullscreen mode Exit fullscreen mode

查询和变异!

好的,我们已经设置好了 GraphQL。现在让我们与它交互!我们将使用 创建数据mutations。我们也将使用 检索数据queries

从命令行运行:

amplify console api
Enter fullscreen mode Exit fullscreen mode

然后选择 graphql。AWS AppSync 的控制台将在浏览器中打开。AppSync 是我们用于创建 GraphQL API 的底层服务,使用其控制台,我们可以通过可视化界面测试查询。

进入 AppSync 界面后,Mutation在下拉菜单中选择,然后单击加号按钮。

界面显示突变下拉菜单和加号

在下面,你会看到一些可供选择的操作。选择“createRecipe”,然后点击name输入框旁边的复选框。

输入你的食谱名称。我选了mac n cheese

显示选项和复选框的界面屏幕截图

按下橙色的运行按钮,你就会得到一个食谱✨!如果你愿意,你可以创建几个不同的食谱——更改食谱名称,然后按下你想要制作的每个食谱对应的橙色按钮。

现在让我们看看我们创建的食谱。将下拉菜单切换回 ,Query而不是Mutation。然后选择listRecipes其下方的 。选择您想要查看的属性,例如name下方的items。另请注意,您可以

显示最终查询

重复创建食谱的步骤,Recipe创建一些食材和说明。使用食谱的 ID recipeID(提示:您可以使用查询获取listRecipes!),您还可以通过一次更改创建包含食材和说明的食谱,只需选择相应的字段并填充即可!

现在,listRecipes使用ingredientsand重新运行查询instructions,您将看到所有内容都已连接。这就是 GraphQL 的魅力所在——您无需更改端点即可获取所需的任何数据,只需更改与之交互的字段即可!

GraphQL 查询的剖析

我们已经使用这个可视化界面编写了 GraphQL 查询和变异,但我们也深入研究它们的语法,以便您可以从头开始编写和理解它们。

以下是我们可以在 API 上使用的示例查询。

query MyQuery {
  # This is a comment!
  listRecipes {
    items {
      name
      id
      createdAt
      instructions {
        items {
          id
          info
        }
      }
      ingredients {
        items {
          id
          name
          quantity
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

query- 这是我们针对数据执行的操作类型。query分别是检索数据、mutation更改数据以及subscription监听数据变化。在本教程的其余部分,我们将使用这三种操作!

MyQuery- 这是查询的名称,理想情况下,这些名称应该是描述性的,例如ListRecipes

listRecipes- AppSync 生成GraphQL 解析器,允许我们获取数据。

items- 从句法上讲,这表示我们得到了多个食谱

name,,id——createdAt我们想要获取有关我们数据的字段。createdAtupdatedAt自动为我们添加。

instructions并且ingredients——我们还想获取相关说明和成分的数据!然后,将它们的字段放入查询中即可获取这些数据。

您可以在查询中添加或删除您想要的任何字段!

某些查询也需要arguments。例如,如果您只想获取一个 Recipe,则可以提供所需 Recipe 的 ID。对于 Mutation 也是如此。

query GetRecipe($id: ID!) {
  getRecipe(id: $id) {
    id
    name
  }
}
Enter fullscreen mode Exit fullscreen mode

现在,让我们在应用程序中查询我们新创建的 API!

如何在前端运行这些查询

现在我们已经尝试了变更和查询,如何将它们集成到我们的应用中呢?首先,让我们先尝试一下不使用任何库。我们可以使用用于 REST API 调用的普通 Fetch 请求。

前往您的App.js组件。首先,从文件导入对象aws-exports.js。您可以直接查看该文件,但它实际上包含了前端所需的所有 Amplify 生成的后端配置信息。此外,还要useEffect从 React 导入。

import config from './aws-exports'
import { useEffect } from 'react'
Enter fullscreen mode Exit fullscreen mode

现在,我们将创建一个useEffect钩子,它将在页面加载时发出获取请求(如果您使用的是原始 JavaScript,则很可能会在页面加载事件中编写相同的代码,但不使用 useEffect)。

在获取请求中,我们需要指定端点,以便从aws-exports对象中获取。然后,我们需要通过添加请求方法来自定义请求POST。我们还将从aws-exports文件中提供 API 密钥。之后,请求正文将包含我们之前使用的查询!我们需要使用该JSON.stringify方法将对象转换为字符串。与其他获取请求一样,我们需要将数据转换为 JSON,然后才能查看!

function App() {
  useEffect(() => {
    const pullData = async () => {
      let data = await fetch(config.aws_appsync_graphqlEndpoint, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Accept: 'application/json',
          'X-Api-Key': config.aws_appsync_apiKey
        },
        body: JSON.stringify({
          query: `query MyQuery {
          listRecipes {
            items {
              name
              id
              createdAt
              instructions {
                items {
                  id
                  info
                }
              }
              ingredients {
                items {
                  id
                  name
                  quantity
                }
              }
            }
          }
        }
        `
        })
      })
      data = await data.json()
      console.log(data)
    }
    pullData()
  }, [])

  return <h1>Hello GraphQL!</h1>
}
Enter fullscreen mode Exit fullscreen mode

好的,现在我们可以从 API 中获取数据了,但这有点笨重,代码量也有点大。如果你进入graphql/Amplify 生成的目录,你会看到一些文件,其中包含所有常见操作的订阅、查询和变更!我们将导入这些文件并在代码中使用它们。此外,Amplify 还提供了辅助函数来抽象 HTTP 请求。

在项目的根目录中,运行:

npm i aws-amplify
Enter fullscreen mode Exit fullscreen mode

这将安装 Amplify 库,这将有助于使 GraphQL 查询更加简洁。

我们将在文件中配置 Amplify,将前端和后端绑定在一起index.js。在顶部添加以下内容:

// index.js
import { Amplify } from 'aws-amplify'
import config from './aws-exports'

Amplify.configure(config)
Enter fullscreen mode Exit fullscreen mode

现在,回到App.js文件。我们将从库中导入一些东西aws-amplify

import { API } from 'aws-amplify'
Enter fullscreen mode Exit fullscreen mode

我们还将listRecipes从 Amplify 生成的查询中导入查询。你可以在 graphql/queries.js 文件中查看它的代码。

import { listRecipes } from './graphql/queries'
Enter fullscreen mode Exit fullscreen mode

让我们修改一下useEffect代码。将pullData函数替换为以下内容:

useEffect(() => {
  const pullData = async () => {
    const data = await API.graphql({ query: listRecipes })
    console.log(data)
  }
  pullData()
}, [])
Enter fullscreen mode Exit fullscreen mode

API.graphql()方法会向我们应用已配置的 GraphQL API 发起 API 请求。我们将查询结果作为参数传入一个对象中。代码比以前少多了!

现在,我们将运行一个突变,以便在用户点击按钮时创建新的菜谱。我们还会提示用户输入菜谱的名称。将组件return中的语句替换App.js为以下内容:一个在点击时运行事件监听器的按钮。

return (
  <div className='App'>
    <button onClick={createNewRecipe}>create recipe</button>
  </div>
)
Enter fullscreen mode Exit fullscreen mode

确保导入我们需要的突变:

import { createRecipe } from './graphql/mutations'
Enter fullscreen mode Exit fullscreen mode

现在,我们将实现该createNewRecipe函数。将其添加到您的组件中。首先,我们将要求用户命名菜谱。然后,我们将运行一个 GraphQL 请求,这次会使用createRecipe突变。此突变也接受变量:在本例中是菜谱的名称。我们也将在一个对象中提供该变量!

const createNewRecipe = async () => {
  const name = prompt('what is the recipe name?')
  const newRecipe = await API.graphql({ query: createRecipe, variables: { input: { name } }}))
  console.log(newRecipe)
}
Enter fullscreen mode Exit fullscreen mode

如果你刷新页面,你会看到你的食谱数组里已经有你创建的新食谱了!但是,我们如何才能让查询在每次创建新食谱时自动重新运行呢?订阅!

订阅

订阅允许您通过 GraphQL “订阅” 事件,因此每当数据更新时,您都可以运行代码。在我们的例子中,我们将设置为每当创建新的配方时,都会重新获取所有配方。

首先,导入订阅:

import { onCreateRecipe } from './graphql/subscriptions'
Enter fullscreen mode Exit fullscreen mode

然后,我们将更新我们的useEffect。保留前几行从 API 中提取配方。在其下方创建一个订阅。这看起来与我们之前发出的其他 API 请求类似,但在本例中,我们将在其上添加方法。我们将传递一个带有和 的.subscribe对象。如果订阅出现错误, Error 将会运行。将在订阅触发后运行。在我们的例子中,我们希望重新运行nexterrorNextpullData

最后,确保通过返回清理订阅的函数来取消订阅更新。

useEffect(() => {
  const pullData = async () => {
    const data = await API.graphql(graphqlOperation(listRecipes))
    console.log(data)
  }
  pullData()

  const subscription = API.graphql(
    { query: onCreateRecipe }
  ).subscribe({
    next: (recipeData) => {
      pullData()
    },
    error: (err) => {
      console.log(err)
    }
  })

  return () => subscription.unsubscribe()
}, [])
Enter fullscreen mode Exit fullscreen mode

结论

在这篇文章中,我们了解了 GraphQL 及其优势,以及您可能不想使用它的原因!我们还创建了一个 API,并将其用于前端应用。如果您想删除 API,可以amplify delete从 CLI 运行,您的代码将保留在本地,但不会再部署到云端!

文章来源:https://dev.to/aws/a-complete-beginner-s-guide-to-graphql-32df
PREV
Next.js 初学者完整指南
NEXT
⚡️ 使用无服务器函数的 10 种方法