如何使用Prisma构建GraphQLAPI并部署到DigitalOcean的应用平台

来自菜鸟教程
跳转至:导航、​搜索

作者选择了 COVID-19 Relief Fund 作为 Write for DOnations 计划的一部分来接受捐赠。

介绍

GraphQL 是一种 API 查询语言,由模式定义语言和查询语言组成,允许 API 使用者仅获取支持灵活查询所需的数据。 GraphQL 使开发人员能够发展 API,同时满足多个客户端的不同需求,例如 iOS、Android 和应用程序的 Web 变体。 此外,GraphQL 模式为 API 增加了一定程度的类型安全性,同时也作为 API 的一种文档形式。

Prisma 是一个开源数据库工具包。 它由三个主要工具组成:

  • 棱镜客户端 :用于 Node.js 和 TypeScript 的自动生成和类型安全的查询构建器。
  • 棱镜迁移 :声明性数据建模和迁移系统。
  • Prisma Studio:用于查看和编辑数据库中数据的 GUI。

对于希望专注于实现增值功能而不是将时间花在复杂的数据库工作流程(例如模式迁移或编写复杂的 SQL 查询)上的应用程序开发人员,Prisma 有助于使用数据库。

在本教程中,您将结合使用 GraphQL 和 Prisma,因为它们的职责是相辅相成的。 GraphQL 为您的数据提供了一个灵活的接口,以便在客户端(例如前端和移动应用程序)中使用——GraphQL 不依赖于任何特定的数据库。 这就是 Prisma 处理与存储数据的数据库交互的地方。

DigitalOcean 的应用平台 提供了一种在云中部署应用程序和配置数据库的无缝方式,而无需担心基础设施。 这减少了在云中运行应用程序的运营开销; 特别是能够创建具有每日备份和自动故障转移的托管 PostgreSQL 数据库。 App Platform 具有原生 Node.js 支持,可简化部署。

您将使用 Node.js 在 JavaScript 中为博客应用程序构建 GraphQL API。 您将首先使用 Apollo Server 构建由内存数据结构支持的 GraphQL API。 然后,您将 API 部署到 DigitalOcean 应用平台。 最后,您将使用 Prisma 替换内存存储并将数据持久保存在 PostgreSQL 数据库中并再次部署应用程序。

在本教程结束时,您将拥有一个部署到 DigitalOcean 的 Node.js GraphQL API,它处理通过 HTTP 发送的 GraphQL 请求并对 PostgreSQL 数据库执行 CRUD 操作。

您可以在 DigitalOcean 社区资源库 中找到此项目的代码。

先决条件

在开始本指南之前,您需要以下内容:

基本熟悉 JavaScriptNode.js、GraphQL 和 PostgreSQL 很有帮助,但不是本教程的严格要求。

第 1 步——创建 Node.js 项目

在这一步中,您将使用 npm 建立一个 Node.js 项目并安装依赖项 apollo-servergraphql

该项目将成为您将在本教程中构建和部署的 GraphQL API 的基础。

首先,为您的项目创建一个新目录:

mkdir prisma-graphql

接下来,导航到目录并初始化一个空的 npm 项目:

cd prisma-graphql
npm init --yes

此命令创建一个最小的 package.json 文件,用作 npm 项目的配置文件。

您将收到以下输出:

OutputWrote to /Users/yourusaername/workspace/prisma-graphql/package.json:
{
  "name": "prisma-graphql",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

您现在已准备好在项目中配置 TypeScript。

执行以下命令安装必要的依赖项:

npm install apollo-server graphql --save

这会在您的项目中安装两个包作为依赖项:

  • apollo-server:用于定义如何解析 GraphQL 请求以及如何获取数据的 HTTP 库。
  • graphql:是您将用于构建 GraphQL 模式的库。

您已经创建了项目并安装了依赖项。 在下一步中,您将定义 GraphQL 模式。

第 2 步——定义 GraphQL 模式和解析器

在这一步中,您将定义 GraphQL schema 和相应的 解析器。 架构将定义 API 可以处理的操作。 解析器将使用内存数据结构定义处理这些请求的逻辑,您将在下一步中将其替换为数据库查询。

首先,创建一个名为 src 的新目录,其中将包含您的源文件:

mkdir src

然后运行以下命令为架构创建文件:

nano src/schema.js

现在将以下代码添加到文件中:

棱镜-graphql/src/schema.js

const { gql } = require('apollo-server')

const typeDefs = gql`
  type Post {
    content: String
    id: ID!
    published: Boolean!
    title: String!
  }

  type Query {
    feed: [Post!]!
    post(id: ID!): Post
  }

  type Mutation {
    createDraft(content: String, title: String!): Post!
    publish(id: ID!): Post
  }
`

在这里,您使用 gql 标记模板 定义 GraphQL 模式。 模式是类型定义的集合(因此是 typeDefs),它们共同定义可以针对您的 API 执行的查询的形状。 这会将 GraphQL 模式字符串转换为 Apollo 期望的格式。

该模式引入了三种类型:

  • Post:定义博客应用中帖子的类型,包含四个字段,每个字段后跟其类型,例如 String
  • Query:定义返回多个帖子的 feed 查询,如方括号和接受单个参数并返回单个 Postpost 查询.
  • Mutation:定义 createDraft 突变以创建草稿 Postpublish 突变,它接受 id 并返回 Post

请注意,每个 GraphQL API 都有一个 query 类型,并且可能有也可能没有 mutation 类型。 这些类型与常规对象类型相同,但它们是特殊的,因为它们定义了每个 GraphQL 查询的入口点。

接下来,将 posts 数组添加到 src/schema.js 文件中,在 typeDefs 变量下方:

棱镜-graphql/src/schema.js

...
const posts = [
  {
    id: 1,
    title: 'Subscribe to GraphQL Weekly for community news ',
    content: 'https://graphqlweekly.com/',
    published: true,
  },
  {
    id: 2,
    title: 'Follow DigitalOcean on Twitter',
    content: 'https://twitter.com/digitalocean',
    published: true,
  },
  {
    id: 3,
    title: 'What is GraphQL?',
    content: 'GraphQL is a query language for APIs',
    published: false,
  },
]

您使用三个预定义的帖子定义 posts 数组。 请注意,每个 post 对象的结构都与您在架构中定义的 Post 类型相匹配。 该数组包含将由 API 提供的帖子。 在后续步骤中,您将在引入数据库和 Prisma Client 后替换阵列。

接下来,在刚刚定义的 posts 数组下方定义 resolvers 对象:

棱镜-graphql/src/schema.js

...
const resolvers = {
  Query: {
    feed: (parent, args) => {
      return posts.filter((post) => post.published)
    },
    post: (parent, args) => {
      return posts.find((post) => post.id === Number(args.id))
    },
  },
  Mutation: {
    createDraft: (parent, args) => {
      posts.push({
        id: posts.length + 1,
        title: args.title,
        content: args.content,
        published: false,
      })
      return posts[posts.length - 1]
    },
    publish: (parent, args) => {
      const postToPublish = posts.find((post) => post.id === Number(args.id))
      postToPublish.published = true
      return postToPublish
    },
  },
  Post: {
    content: (parent) => parent.content,
    id: (parent) => parent.id,
    published: (parent) => parent.published,
    title: (parent) => parent.title,
  },
}


module.exports = {
  resolvers,
  typeDefs,
}

您可以按照与 GraphQL 模式相同的结构来定义解析器。 架构类型中的每个字段都有一个相应的解析器函数,其职责是返回架构中该字段的数据。 例如,Query.feed() 解析器将通过过滤 posts 数组来返回已发布的帖子。

解析器函数接收四个参数:

  • parent:parent是resolver链中前一个resolver的返回值。 对于顶级解析器,父级是 undefined,因为没有调用以前的解析器。 例如,当进行 feed 查询时,将使用 parent 的值 undefined 调用 query.feed() 解析器,然后调用 [ 的解析器X145X] 将被调用,其中 parent 是从 feed 解析器返回的对象。
  • args:该参数携带查询的参数,例如post查询,将接收要抓取的帖子的id
  • context:通过解析器链传递的对象,每个解析器都可以写入和读取,它允许解析器共享信息。
  • info:查询或突变的 AST 表示。 您可以在本系列的第三部分阅读更多详细信息:Demystifying the info Argument in GraphQL Resolvers

由于在这些旋转变压器中不需要 contextinfo,因此只定义了 parentargs

完成后保存并退出文件。

注意: 当解析器返回与解析器名称相同的字段时,例如 Post 的四个解析器,Apollo Server 将自动解析它们。 这意味着您不必显式定义这些解析器。

-  Post: {
-    content: (parent) => parent.content,
-    id: (parent) => parent.id,
-    published: (parent) => parent.published,
-    title: (parent) => parent.title,
-  },

最后,您导出模式和解析器,以便您可以在下一步中使用它们来使用 Apollo Server 实例化服务器。

第 3 步 — 创建 GraphQL 服务器

在这一步中,您将使用 Apollo Server 创建 GraphQL 服务器并将其绑定到端口,以便服务器可以接受连接。

首先,运行以下命令为服务器创建文件:

nano src/server.js

现在将以下代码添加到文件中:

棱镜-graphql/src/server.js

const { ApolloServer } = require('apollo-server')
const { resolvers, typeDefs } = require('./schema')

const port = process.env.PORT || 8080

new ApolloServer({ resolvers, typeDefs }).listen({ port }, () =>
  console.log(`Server ready at: http://localhost:${port}`),
)

在这里,您实例化服务器并传递上一步中的模式和解析器。

服务器将绑定到的端口是从 PORT 环境变量设置的,如果未设置,它将默认为 8080PORT 环境变量将由 App Platform 自动设置,并确保您的服务器在部署后可以接受连接。

保存并退出文件。

您的 GraphQL API 已准备好运行。 使用以下命令启动服务器:

node src/server.js

您将收到以下输出:

OutputServer ready at: http://localhost:8080

将启动脚本添加到 package.json 被认为是一种很好的做法,这样您的服务器的入口点就很清楚了。 此外,这将允许 App Platform 在部署后启动服务器。

为此,将以下行添加到 package.json 中的 "scripts" 对象:

包.json

{
  "name": "prisma-graphql",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node ./src/server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "apollo-server": "^2.18.2",
    "graphql": "^15.3.0"
  }
}

完成后保存并退出文件。

现在您可以使用以下命令启动服务器:

npm start

要测试 GraphQL API,请从输出中打开 URL,这将引导您进入 GraphQL Playground。

GraphQL Playground 是一个 IDE,您可以在其中通过发送查询和突变来测试 API。

例如,要测试仅返回已发布帖子的 feed 查询,请在 IDE 左侧输入以下查询,然后按播放按钮发送查询:

query {
  feed {
    id
    title
    content
    published
  }
}

响应将显示标题为 Subscribe to GraphQL Weekly 及其 URL 和 Follow DigitalOcean on Twitter 及其 URL。

要测试 createDraft 突变,请输入以下突变:

mutation {
  createDraft(title: "Deploying a GraphQL API to DigitalOcean") {
    id
    title
    content
    published
  }
}

提交变异后,使用播放按钮,您将在 title 字段中收到 Deploying a GraphQL API to DigitalOcean 作为响应的一部分。

注意: 您可以通过在 createDraft 后面的花括号内添加或删除字段来选择从突变中返回的字段。 例如,如果您只想返回 idtitle 您可以发送以下突变:

mutation {
  createDraft(title: "Deploying a GraphQL API to DigitalOcean") {
    id
    title
  }
}

您已成功创建并测试了 GraphQL 服务器。 在下一步中,您将为项目创建一个 GitHub 存储库。

第 4 步 - 创建 GitHub 存储库

在此步骤中,您将为您的项目创建一个 GitHub 存储库并推送您的更改,以便 GraphQL API 可以从 GitHub 自动部署到应用程序平台。

首先从 prisma-graphql 文件夹初始化存储库:

git init

接下来,使用以下两个命令将代码提交到存储库:

git add src package-lock.json package.json
git commit -m 'Initial commit'

现在更改已提交到您的本地存储库,您将在 GitHub 中创建一个存储库并推送您的更改。

前往 GitHub 创建一个新的仓库。 为了保持一致性,将存储库命名为 prisma-graphql,然后单击 Create repository

创建存储库后,使用以下命令推送更改,其中包括将默认本地分支重命名为 main

git remote add origin git@github.com:your_github_username/prisma-graphql.git
git branch -M main
git push --set-upstream origin main

您已成功提交更改并将更改推送到 GitHub。 接下来,您将存储库连接到 App Platform 并部署 GraphQL API。

第 5 步 — 部署到应用平台

在此步骤中,您将在上一步中创建的 GitHub 存储库连接到 DigitalOcean 并配置 App Platform,以便在您将更改推送到 GitHub 时自动部署 GraphQL API。

首先,进入App Platform并点击Launch Your App按钮。

您将看到一个链接您的 GitHub 帐户的按钮。

单击它,您将被重定向到 GitHub。

点击安装和授权您将被重定向回 DigitalOcean。

选择存储库 your_github_username/prisma-graphql 并单击 Next

选择您要部署应用程序的区域,然后单击 Next

您可以在此处自定义应用程序的配置。 确保 运行命令npm start。 默认情况下,App Platform 会将 HTTP 端口设置为 8080,这与您配置 GraphQL 服务器要绑定到的端口相同。

单击下一步,系统将提示您选择计划。

选择 Basic,然后单击 Launch Basic App。 您将被重定向到应用程序页面,您将在其中看到初始部署的进度。

构建完成后,您将收到一条通知,指示您的应用已部署。

您现在可以通过应用名称下方的 URL 访问已部署的 GraphQL API。 它将位于 ondigitalocean.app 子域下。 如果您打开 URL,GraphQL Playground 将以与教程第 3 步相同的方式打开。

您已成功将存储库连接到 App Platform 并部署了 GraphQL API。 接下来,您将改进您的应用程序并将 GraphQL API 的内存数据替换为数据库。

第 6 步 — 使用 PostgreSQL 设置 Prisma

到目前为止,您构建的 GraphQL API 使用内存中的 posts 数组来存储数据。 这意味着如果您的服务器重新启动,对数据的所有更改都将丢失。 为确保您的数据安全持久化,您将使用 PostgreSQL 数据库替换 posts 数组并使用 Prisma 访问数据。

在此步骤中,您将安装 Prisma CLI,创建初始 Prisma 模式,使用 Docker 在本地设置 PostgreSQL,并将 Prisma 连接到它。

Prisma 架构是您的 Prisma 设置的主要配置文件,包含您的数据库架构。

首先使用以下命令安装 Prisma CLI:

npm install --save-dev @prisma/cli

Prisma CLI 将帮助数据库工作流程,例如运行数据库迁移和生成 Prisma 客户端。

接下来,您将使用 Docker 设置 PostgreSQL 数据库。 使用以下命令创建一个新的 Docker Compose 文件:

nano docker-compose.yml

现在将以下代码添加到新创建的文件中:

棱镜-graphql/docker-compose.yml

version: '3.8'
services:
  postgres:
    image: postgres:10.3
    restart: always
    environment:
      - POSTGRES_USER=test-user
      - POSTGRES_PASSWORD=test-password
    volumes:
      - postgres:/var/lib/postgresql/data
    ports:
      - '5432:5432'
volumes:
  postgres:

这个 Docker Compose 配置文件负责在你的机器上启动官方 PostgreSQL Docker 镜像。 POSTGRES_USERPOSTGRES_PASSWORD 环境变量设置超级用户(具有管理员权限的用户)的凭据。 您还将使用这些凭据将 Prisma 连接到数据库。 最后,您定义一个卷,PostgreSQL 将在其中存储其数据,并将您机器上的 5432 端口绑定到 Docker 容器中的同一端口。

保存并退出文件。

完成此设置后,继续使用以下命令启动 PostgreSQL 数据库服务器:

docker-compose up -d

您可以使用以下命令验证数据库服务器是否正在运行:

docker ps

这将输出类似于:

OutputCONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
198f9431bf73        postgres:10.3       "docker-entrypoint.s…"   45 seconds ago      Up 11 seconds       0.0.0.0:5432->5432/tcp   prisma-graphql_postgres_1

运行 PostgreSQL 容器后,您现在可以创建 Prisma 设置。 从 Prisma CLI 运行以下命令:

npx prisma init

请注意,作为最佳实践,Prisma CLI 的所有调用都应以 npx 为前缀。 这可确保它使用您的本地安装。

运行命令后,Prisma CLI 在您的项目中创建了一个名为 prisma 的新文件夹。 它包含以下两个文件:

  • schema.prisma:Prisma 项目的主要配置文件(您将在其中包含数据模型)。
  • .env:一个 dotenv 文件,用于定义您的数据库连接 URL。

要确保 Prisma 知道数据库的位置,请打开 .env 文件:

nano prisma/.env

调整 DATABASE_URL 环境变量,如下所示:

prisma-graphql/prisma/.env

DATABASE_URL="postgresql://test-user:test-password@localhost:5432/my-blog?schema=public"

请注意,您使用的是在 Docker Compose 文件中指定的数据库凭据 test-usertest-password。 要了解有关连接 URL 格式的更多信息,请访问 Prisma 文档

您已成功启动 PostgreSQL 并使用 Prisma 模式配置了 Prisma。 在下一步中,您将为博客定义数据模型并使用 Prisma Migrate 创建数据库模式。

第 7 步 — 使用 Prisma Migrate 定义数据模型

现在您将在刚刚创建的 Prisma 模式文件中定义您的 数据模型 。 然后,该数据模型将使用 Prisma Migrate 映射到数据库,该数据库将生成并发送 SQL 语句以创建与您的数据模型对应的表。

由于您正在构建博客,因此应用程序的主要实体将是 usersposts。 在这一步中,您将定义一个 Post 模型,其结构类似于 GraphQL 模式中的 Post 类型。 在稍后的步骤中,您将改进应用程序并添加 User 模型。

注意: GraphQL API 可以看作是数据库的抽象层。 在构建 GraphQL API 时,GraphQL 架构通常与您的数据库架构非常相似。 但是,作为一种抽象,这两个模式不一定具有相同的结构,从而允许您控制要通过 API 公开的数据。 这是因为某些数据可能被认为与 API 层敏感或无关。


Prisma 使用自己的 数据建模语言 来定义应用程序数据的形状。

package.json 所在的项目文件夹中打开 schema.prisma 文件:

nano prisma/schema.prisma

注意:你可以在终端使用pwd命令验证你所在的文件夹,会输出当前工作目录。 此外,使用 ls 命令列出文件将帮助您浏览文件系统。


向其中添加以下模型定义:

prisma-graphql/prisma/schema.prisma

...
model Post {
  id        Int     @default(autoincrement()) @id
  title     String
  content   String?
  published Boolean @default(false)
}

您正在定义一个名为 Post模型 ,其中包含多个 字段 。 模型将映射到数据库; 这些字段代表单个

id 字段具有以下字段属性:

  • @default(autoincrement()):设置列的自动递增默认值。
  • @id:这会将列设置为表的主键。

完成后保存并退出文件。

模型到位后,您现在可以使用 Prisma Migrate 在数据库中创建相应的表。 这可以通过创建迁移文件并运行它们的 migrate dev 命令来完成。

再次打开终端并运行以下命令:

npx prisma migrate dev --preview-feature --name "init" --skip-generate

这将输出类似于:

OutputPostgreSQL database my-blog created at localhost:5432

Prisma Migrate created and applied the following migration(s) from new schema changes:

migrations/
  └─ 20201201110111_init/
    └─ migration.sql

Everything is now in sync.

此命令在您的文件系统上创建一个新的 migration 并针对数据库运行它以创建数据库模式。 以下是提供给命令的选项的快速概览:

  • --preview-feature:必需,因为 Prisma Migrate 当前处于 preview 状态。
  • --name "init":指定迁移的名称(将用于命名在文件系统上创建的迁移文件夹)。
  • --skip-generate:跳过生成 Prisma 客户端(这将在下一步中完成)。

您的 prisma/migrations 目录现在填充了 SQL 迁移文件。 这种方法允许您跟踪对数据库模式的更改并在生产中创建相同的数据库模式。

注意: 如果您已经使用了 my-blog 数据库的 Prisma Migrate,并且 prisma/migration 文件夹中的迁移与数据库架构之间存在不一致,系统会提示您使用以下输出重置数据库:

Output? We need to reset the PostgreSQL database "my-blog" at "localhost:5432". All data will be lost.
Do you want to continue? › (y/N)

您可以通过输入 y 来解决此问题,这将重置数据库。 请注意,这将导致数据库中的所有数据丢失。


您现在已经创建了数据库模式。 在下一步中,您将安装 Prisma Client 并在您的 GraphQL 解析器中使用它。

第 8 步 — 在 GraphQL 解析器中使用 Prisma 客户端

Prisma Client 是一个自动生成且类型安全的对象关系映射器 (ORM),您可以使用它以编程方式从 Node.js 应用程序读取和写入数据库中的数据。 在此步骤中,您将在项目中安装 Prisma Client。

再次打开终端并安装 Prisma Client npm 包:

npm install @prisma/client

注意: Prisma Client 通过根据您的 Prisma 模式生成代码到 node_modules 文件夹,为您提供丰富的自动完成功能。 要生成代码,请使用 npx prisma generate 命令。 这通常在您创建并运行新迁移之后完成。 但是,在第一次安装时,这不是必需的,因为它会在 postinstall 挂钩中自动为您生成。


创建数据库和 GraphQL 架构并安装 Prisma 客户端后,您现在将在 GraphQL 解析器中使用 Prisma 客户端来读取和写入数据库中的数据。 您将通过替换迄今为止用于保存数据的 posts 数组来完成此操作。

首先创建以下文件:

nano src/db.js

添加以下内容:

棱镜-graphql/src/db.js

const { PrismaClient } = require('@prisma/client')

module.exports = {
  prisma: new PrismaClient(),
}

这会导入 Prisma Client,创建它的一个实例,然后导出您将在解析器中使用的实例。

现在保存并关闭 src/db.js 文件。

接下来,您将 prisma 实例导入 src/schema.js。 为此,请打开 src/schema.js

nano src/schema.js

然后从文件顶部的 ./db 导入 prisma

棱镜-graphql/src/schema.js

const { prisma } = require('./db')
...

然后删除posts数组:

棱镜-graphql/src/schema.js

-const posts = [
-  {
-    id: 1,
-    title: 'Subscribe to GraphQL Weekly for community news ',
-    content: 'https://graphqlweekly.com/',
-    published: true,
-  },
-  {
-    id: 2,
-    title: 'Follow DigitalOcean on Twitter',
-    content: 'https://twitter.com/digitalocean',
-    published: true,
-  },
-  {
-    id: 3,
-    title: 'What is GraphQL?',
-    content: 'GraphQL is a query language for APIs',
-    published: false,
-  },
-]

现在您将更新 Query 解析器以从数据库中获取已发布的帖子。 使用以下解析器更新 resolvers.Query 对象:

棱镜-graphql/src/schema.js

...
const resolvers = {
  Query: {
    feed: (parent, args) => {
      return prisma.post.findMany({
        where: { published: true },
      })
    },
    post: (parent, args) => {
      return prisma.post.findOne({
        where: { id: Number(args.id) },
      })
    },
  },

在这里,您使用了两个 Prisma Client 查询:

  • findMany:获取 publish 字段为 false 的帖子。
  • findOne:获取 id 字段等于 id GraphQL 参数的单个帖子。

请注意,根据 GraphQL 规范ID 类型的序列化方式与 String 相同。 因此,您转换为 Number,因为 Prisma 模式中的 idint

接下来,您将更新 Mutation 解析器以保存和更新数据库中的帖子。 使用以下解析器更新 resolvers.Mutation 对象:

棱镜-graphql/src/schema.js

const resolvers = {
  ...
  Mutation: {
    createDraft: (parent, args) => {
      return prisma.post.create({
        data: {
          title: args.title,
          content: args.content,
        },
      })
    },
    publish: (parent, args) => {
      return prisma.post.update({
        where: {
          id: Number(args.id),
        },
        data: {
          published: true,
        },
      })
    },
  },
}

您正在使用两个 Prisma Client 查询:

  • create:创建Post记录。
  • update:更新Post记录的已发布字段,其id与查询参数中的匹配。

您的 schema.js 现在应该如下所示:

棱镜-graphql/src/schema.js

const { gql } = require('apollo-server')
const { prisma } = require('./db')

const typeDefs = gql`
  type Post {
    content: String
    id: ID!
    published: Boolean!
    title: String!
  }

  type Query {
    feed: [Post!]!
    post(id: ID!): Post
  }

  type Mutation {
    createDraft(content: String, title: String!): Post!
    publish(id: ID!): Post
  }
`

const resolvers = {
  Query: {
    feed: (parent, args) => {
      return prisma.post.findMany({
        where: { published: true },
      })
    },
    post: (parent, args) => {
      return prisma.post.findOne({
        where: { id: Number(args.id) },
      })
    },
  },
  Mutation: {
    createDraft: (parent, args) => {
      return prisma.post.create({
        data: {
          title: args.title,
          content: args.content,
        },
      })
    },
    publish: (parent, args) => {
      return prisma.post.update({
        where: {
          id: Number(args.id),
        },
        data: {
          published: true,
        },
      })
    },
  },
}

module.exports = {
  resolvers,
  typeDefs,
}

保存并关闭文件。

现在您已经更新了解析器以使用 Prisma 客户端,使用以下命令启动服务器以测试 GraphQL API 和数据库之间的数据流:

npm start

再次,您将收到以下输出:

OutputServer ready at: http://localhost:8080

在输出地址处打开 GraphQL 操场,并使用步骤 3 中的相同查询测试 GraphQL API。

现在您将提交您的更改,以便可以将更改部署到 App Platform。

为避免提交 node_modules 文件夹和 prisma/.env 文件,首先创建一个 .gitignore 文件:

nano .gitignore

将以下内容添加到文件中:

棱镜-graphql/.gitignore

node_modules
prisma/.env

保存并退出文件。

然后运行以下两个命令来提交更改:

git add .
git commit -m 'Add Prisma'

接下来,您将在 App Platform 中将 PostgreSQL 数据库添加到您的应用程序中。

第 9 步 — 在应用平台中创建和迁移 PostgreSQL 数据库

在此步骤中,您将在 App Platform 中将 PostgreSQL 数据库添加到您的应用程序中。 然后,您将使用 Prisma Migrate 对其运行迁移,以便部署的数据库架构与您的本地数据库匹配。

首先,进入 App Platform 控制台 并选择您在第 5 步中创建的 prisma-graphql 项目。

接下来,转到 组件 选项卡。

点击 + 创建组件 并选择 Database,这将引导您进入配置数据库的页面。

选择Dev Database并点击Create and Attach

您将被重定向回 Components 页面,其中将有一个用于创建数据库的进度条。

创建数据库后,您将从本地计算机对 DigitalOcean 上的生产数据库运行数据库迁移。 要运行迁移,您将需要托管数据库的连接字符串。 要获取它,请单击组件选项卡中的 db 图标。

Connection Details 下拉列表中选择 Connection String 并复制数据库 URL,其结构如下:

postgresql://db:some_password@unique_identifier.db.ondigitalocean.com:25060/db?sslmode=require

然后,在终端中运行以下命令并确保将 DATABASE_URL 设置为您复制的相同 URL:

DATABASE_URL="postgresql://db:some_password@unique_identifier.db.ondigitalocean.com:25060/db?sslmode=require" npx prisma migrate deploy --preview-feature

这将使用 Prisma Migrate 针对实时数据库运行迁移。

如果迁移成功,您将收到以下信息:

OutputPostgreSQL database db created at unique_identifier.db.ondigitalocean.com:25060

Prisma Migrate applied the following migration(s):

migrations/
  └─ 20201201110111_init/
    └─ migration.sql

您已成功迁移 DigitalOcean 上的生产数据库,该数据库现在与 Prisma 模式匹配。

现在,您可以使用以下命令推送您的 Git 更改来部署您的应用程序:

git push

注意: App Platform 将使 DATABASE_URL 环境变量在运行时对您的应用程序可用。 Prisma 客户端将使用 Prisma 模式的 datasource 块中的 env("DATABASE_URL") 使用该环境变量。


这将自动触发构建。 如果您打开 App Platform 控制台,您将看到一个 Deploying 进度条。

部署成功后,您将收到 Deployed successfully 消息。

您现在已经使用数据库备份了已部署的 GraphQL API。 打开 Live App,它将带您进入 GraphQL Playground。 使用步骤 3 中的相同查询测试 GraphQL API。

在最后一步中,您将通过添加 User 模型来改进 GraphQL API。

第 10 步——添加用户模型

用于博客的 GraphQL API 有一个名为 Post 的实体。 在这一步中,您将通过在 Prisma 模式中定义一个新模型并调整 GraphQL 模式以使用新模型来改进 API。 您将向 Post 模型引入具有一对多 关系User 模型。 这将允许您代表帖子的作者并将多个帖子与每个用户相关联。 然后,您将改进 GraphQL 模式,以允许通过 API 创建用户并将帖子与用户关联。

首先,打开 Prisma 模式并添加以下内容:

prisma-graphql/prisma/schema.prisma

...
model Post {
  id        Int     @id @default(autoincrement())
  title     String
  content   String?
  published Boolean @default(false)
  author    User?   @relation(fields: [authorId], references: [id])
  authorId  Int?
}

model User {
  id    Int    @id @default(autoincrement())
  email String @unique
  name  String
  posts Post[]
}

您已将以下内容添加到 Prisma 架构中:

  • 代表用户的 User 模型。
  • 两个关系字段:authorposts。 关系字段在 Prisma 级别定义模型之间的连接,并且在数据库中不存在。 这些字段用于生成 Prisma Client 并访问与 Prisma Client 的关系。
  • authorId 字段,由 @relation 属性引用。 Prisma 将在数据库中创建一个外键来连接 PostUser

请注意,Post 模型中的作者字段是可选的。 这意味着您将能够创建与用户无关的帖子。

完成后保存并退出文件。

接下来,使用以下命令在本地创建和应用迁移:

npx prisma migrate dev --preview-feature --name "add-user"

如果迁移成功,您将收到以下信息:

OutputPrisma Migrate created and applied the following migration(s) from new schema changes:

migrations/
  └─ 20201201123056_add_user/
    └─ migration.sql

✔ Generated Prisma Client to ./node_modules/@prisma/client in 53ms

该命令还会生成 Prisma Client,以便您可以使用新表和字段。

现在,您将对 App Platform 上的生产数据库运行迁移,以便数据库架构与您的本地数据库相同。 在终端中运行以下命令并将 DATABASE_URL 设置为来自 App Platform 的连接 URL:

DATABASE_URL="postgresql://db:some_password@unique_identifier.db.ondigitalocean.com:25060/db?sslmode=require" npx prisma migrate deploy --preview-feature

您将收到以下信息:

OutputPrisma Migrate applied the following migration(s):

migrations/
  └─ 20201201123056_add_user/
    └─ migration.sql

您现在将更新 GraphQL 模式和解析器以使用更新后的数据库模式。

打开 src/schema.js 文件并添加更新 typeDefs 如下:

棱镜-graphql/src/schema.js

...
const typeDefs = gql`
  type User {
    email: String!
    id: ID!
    name: String
    posts: [Post!]!
  }

  type Post {
    content: String
    id: ID!
    published: Boolean!
    title: String!
    author: User
  }

  type Query {
    feed: [Post!]!
    post(id: ID!): Post
  }

  type Mutation {
    createUser(data: UserCreateInput!): User!
    createDraft(authorEmail: String, content: String, title: String!): Post!
    publish(id: ID!): Post
  }

  input UserCreateInput {
    email: String!
    name: String
    posts: [PostCreateWithoutAuthorInput!]
  }

  input PostCreateWithoutAuthorInput {
    content: String
    published: Boolean
    title: String!
  }
`
...

在此更新后的代码中,您将以下更改添加到 GraphQL 架构:

  • User 类型,返回 Post 数组。
  • author 字段为 Post 类型。
  • createUser 突变,期望 UserCreateInput 作为其输入类型。
  • UserCreateInput 输入中使用的 PostCreateWithoutAuthorInput 输入类型,用于创建帖子作为 createUser 突变的一部分。
  • createDraft 突变的 authorEmail 可选参数。

更新架构后,您现在将更新解析器以匹配架构。

更新 resolvers 对象如下:

棱镜-graphql/src/schema.js

...
const resolvers = {
  Query: {
    feed: (parent, args) => {
      return prisma.post.findMany({
        where: { published: true },
      })
    },
    post: (parent, args) => {
      return prisma.post.findOne({
        where: { id: Number(args.id) },
      })
    },
  },
  Mutation: {
    createDraft: (parent, args) => {
      return prisma.post.create({
        data: {
          title: args.title,
          content: args.content,
          published: false,
          author: args.authorEmail && {
            connect: { email: args.authorEmail },
          },
        },
      })
    },
    publish: (parent, args) => {
      return prisma.post.update({
        where: { id: Number(args.id) },
        data: {
          published: true,
        },
      })
    },
    createUser: (parent, args) => {
      return prisma.user.create({
        data: {
          email: args.data.email,
          name: args.data.name,
          posts: {
            create: args.data.posts,
          },
        },
      })
    },
  },
  User: {
    posts: (parent, args) => {
      return prisma.user
        .findOne({
          where: { id: parent.id },
        })
        .posts()
    },
  },
  Post: {
    author: (parent, args) => {
      return prisma.post
        .findOne({
          where: { id: parent.id },
        })
        .author()
    },
  },
}

让我们分解对解析器的更改:

  • createDraft 变异解析器现在使用 authorEmail 参数(如果通过)在创建的草稿和现有用户之间创建关系。
  • 新的 createUser 变异解析器使用 嵌套写入 创建用户和相关帖子。
  • User.postsPost.author 解析器定义当 UserPost 为询问。 这些使用 Prisma 的 Fluent API 来获取关系。

保存并退出文件。

启动服务器以测试 GraphQL API:

npm start

首先使用以下 GraphQL 突变测试 createUser 解析器:

mutation {
  createUser(data: { email: "natalia@prisma.io", name: "Natalia" }) {
    email
    id
  }
}

这将创建一个用户。

接下来,使用以下突变测试 createDraft 解析器:

mutation {
  createDraft(
    authorEmail: "natalia@prisma.io"
    title: "Deploying a GraphQL API to App Platform"
  ) {
    id
    title
    content
    published
    author {
      id
      name
    }
  }
}

请注意,只要查询的返回值为 Post,您就可以获取 author。 在此示例中,将调用 Post.author 解析器。

最后,提交您的更改并推送以部署 API:

git add .
git commit -m "add user model"
git push

您已经使用 Prisma Migrate 成功地改进了您的数据库架构,并在您的 GraphQL API 中公开了新模型。

结论

在本文中,您使用 Prisma 和 GraphQL 构建了一个 GraphQL API,并将其部署到 DigitalOcean 的应用平台。 您使用 Apollo Server 定义了 GraphQL 模式和解析器。 然后,您在 GraphQL 解析器中使用 Prisma Client 来持久化和查询 PostgreSQL 数据库中的数据。

下一步,您可以进一步扩展 GraphQL API,使用查询来获取单个用户和突变以将现有草稿连接到用户。

如果您有兴趣探索数据库中的数据,请查看 Prisma Studio。 请务必访问 Prisma 文档 以了解 Prisma 的不同方面,并在 prisma-examples 存储库中探索一些可立即运行的示例项目。

您可以在 DigitalOcean 社区存储库 中找到此项目的代码。