作者选择了COVID-19 救济基金来接受捐赠,作为Write for DOnations计划的一部分。
介绍
GraphQL是一种用于 API 的查询语言,它由架构定义语言和查询语言组成,它允许 API 使用者仅获取他们需要的数据以支持灵活查询。GraphQL 使开发人员能够在满足多个客户端(例如 iOS、Android 和应用程序的 Web 变体)的不同需求的同时改进 API。此外,GraphQL 模式为 API 增加了一定程度的类型安全性,同时也作为 API 的一种文档形式。
Prisma是一个开源数据库工具包。它由三个主要工具组成:
- Prisma Client:用于 Node.js 和 TypeScript 的自动生成和类型安全的查询构建器。
- Prisma Migrate:声明式数据建模和迁移系统。
- Prisma Studio:用于查看和编辑数据库中数据的 GUI。
Prisma 为希望专注于实现增值功能而不是将时间花在复杂的数据库工作流(例如模式迁移或编写复杂的 SQL 查询)上的应用程序开发人员使用数据库提供便利。
在本教程中,您将结合使用 GraphQL 和 Prisma,因为它们的职责相辅相成。GraphQL 为您的数据提供了一个灵活的接口,以便在客户端(例如前端和移动应用程序)中使用——GraphQL 不依赖于任何特定的数据库。这就是 Prisma 处理与存储数据的数据库交互的地方。
DigitalOcean 的应用程序平台提供了一种在云中部署应用程序和配置数据库的无缝方式,而无需担心基础设施。这减少了在云中运行应用程序的运营开销;尤其是能够创建具有每日备份和自动故障转移的托管 PostgreSQL 数据库。App Platform 具有原生 Node.js 支持简化部署。
您将使用 Node.js 在 JavaScript 中为博客应用程序构建 GraphQL API。您将首先使用 Apollo Server 构建由内存数据结构支持的 GraphQL API。然后,您将把 API 部署到 DigitalOcean 应用平台。最后,您将使用 Prisma 替换内存中的存储并将数据保存在 PostgreSQL 数据库中并再次部署应用程序。
在本教程结束时,您将有一个 Node.js GraphQL API 部署到 DigitalOcean,它处理通过 HTTP 发送的 GraphQL 请求并对 PostgreSQL 数据库执行 CRUD 操作。
您可以在DigitalOcean 社区资源库中找到该项目的代码。
先决条件
在开始本指南之前,您需要具备以下条件:
- 一个GitHub帐户。
- 一个DigitalOcean帐户。
- Git安装在您的计算机上。您可以按照教程贡献开源:Git 入门在您的计算机上安装和设置 Git。
- 您的计算机上安装了Node.js v10或更高版本。您可以按照教程如何安装 Node.js 和创建本地开发环境在您的计算机上安装和设置 Node.js。
- Docker安装在您的计算机上(在本地运行 PostgreSQL 数据库)。
基本熟悉JavaScript、Node.js、 GraphQL 和 PostgreSQL 很有帮助,但本教程并不严格要求。
第 1 步 – 创建 Node.js 项目
在这一步中,您将使用 npm 设置一个 Node.js 项目并安装依赖项apollo-server
和graphql
.
该项目将是您将在本教程中构建和部署的 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架构和相应的解析器。模式将定义 API 可以处理的操作。解析器将使用内存数据结构定义处理这些请求的逻辑,您将在下一步中将其替换为数据库查询。
首先,创建一个名为的新目录,该目录src
将包含您的源文件:
- mkdir src
然后运行以下命令为架构创建文件:
- nano 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
返回由方括号表示的多个帖子的post
查询和接受单个参数并返回单个Post
.Mutation
: 定义createDraft
用于创建草稿Post
的publish
突变和接受 anid
并返回 a的突变Post
。
请注意,每个 GraphQL API 都有一个查询类型,并且可能有也可能没有突变类型。这些类型与常规对象类型相同,但它们很特殊,因为它们定义了每个 GraphQL 查询的入口点。
接下来,将posts
数组添加到src/schema.js
文件中,在typeDefs
变量下方:
...
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 客户端后替换阵列。
接下来,在刚刚定义resolvers
的posts
数组下面定义对象:
...
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 是解析器链中前一个解析器的返回值。对于顶级解析器,父解析器是undefined
,因为没有调用先前的解析器。例如,在进行feed
查询时,query.feed()
将使用parent
的值调用undefined
解析器,然后在从解析器返回的对象Post
所在的位置调用parent
的feed
解析器。args
: 该参数携带查询的参数,例如post
查询,将接收id
要获取的帖子的 。context
:通过解析器链传递的对象,每个解析器都可以写入和读取,这允许解析器共享信息。info
:查询或突变的 AST 表示。您可以在本系列的第三部分阅读更多关于细节的信息:揭开 GraphQL 解析器中的信息参数的神秘面纱。
由于context
与info
这些解析器是没有必要的,只有parent
和args
定义。
完成后保存并退出文件。
注意:当解析器返回与解析器名称相同的字段时,例如 的四个解析器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
现在将以下代码添加到文件中:
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
环境变量设置的,如果未设置,它将默认为8080
. 该PORT
环境变量将被应用平台自动设置,并确保您的服务器可以接受连接一旦部署。
保存并退出文件。
您的 GraphQL API 已准备好运行。使用以下命令启动服务器:
- node src/server.js
您将收到以下输出:
OutputServer ready at: http://localhost:8080
将启动脚本添加package.json
到您的服务器被认为是一种很好的做法,以便您的服务器的入口点清晰。此外,这将允许 App Platform 在部署后启动服务器。
为此,"scripts"
将以下行添加到 中的对象package.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 和 URL的标题Follow DigitalOcean on Twitter
。
要测试createDraft
突变,请输入以下突变:
mutation {
createDraft(title: "Deploying a GraphQL API to DigitalOcean") {
id
title
content
published
}
}
在您提交变更后,使用播放按钮,您将Deploying a GraphQL API to DigitalOcean
在title
字段内收到作为响应的一部分。
注意:您可以通过添加或删除后面的大括号内的字段来选择从突变中返回哪些字段createDraft
。例如,如果您只想返回id
并且title
您可以发送以下更改:
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 [email protected]: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。
首先,转到应用程序平台并单击启动您的应用程序按钮。
您将看到一个链接您的 GitHub 帐户的按钮。
单击它,您将被重定向到 GitHub。
单击安装和授权,您将被重定向回 DigitalOcean。
选择存储库your_github_username/prisma-graphql
并单击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
数组来存储数据。这意味着如果您的服务器重新启动,对数据的所有更改都将丢失。为确保您的数据安全持久化,您将posts
使用 PostgreSQL 数据库替换数组并使用 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
现在将以下代码添加到新创建的文件中:
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_USER
和POSTGRES_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 CLIprisma
在您的项目中创建了一个名为的新文件夹。它包含以下两个文件:
schema.prisma
:您的 Prisma 项目的主要配置文件(您将在其中包含您的数据模型)。.env
:用于定义数据库连接 URL 的dotenv文件。
要确保 Prisma 知道您的数据库的位置,请打开.env
文件:
- nano prisma/.env
调整DATABASE_URL
环境变量如下:
DATABASE_URL="postgresql://test-user:test-password@localhost:5432/my-blog?schema=public"
请注意,您正在使用Docker Compose 文件中指定的数据库凭据test-user
和test-password
。要了解有关连接 URL 格式的更多信息,请访问Prisma 文档。
您已经成功启动了 PostgreSQL 并使用 Prisma 模式配置了 Prisma。在下一步中,您将为博客定义数据模型并使用 Prisma Migrate 创建数据库模式。
步骤 7 — 使用 Prisma Migrate 定义数据模型
现在,您将在刚刚创建的 Prisma 模式文件中定义数据模型。然后,该数据模型将通过 Prisma Migrate 映射到数据库,后者将生成并发送 SQL 语句以创建与您的数据模型对应的表。
由于您正在构建博客,因此应用程序的主要实体将是users和posts。在这一步中,您将定义一个Post
与Post
GraphQL 模式中的类型具有相似结构的模型。在后面的步骤中,您将改进应用程序并添加User
模型。
注意: GraphQL API 可以被视为数据库的抽象层。在构建 GraphQL API 时,GraphQL 架构通常与您的数据库架构非常相似。但是,作为一种抽象,这两个模式不一定具有相同的结构,因此您可以控制要通过 API 公开哪些数据。这是因为某些数据可能被视为与 API 层敏感或无关。
Prisma 使用自己的数据建模语言来定义应用程序数据的形状。
schema.prisma
从位于项目的文件夹中打开您的文件package.json
:
- nano prisma/schema.prisma
注意:您可以使用pwd
命令从终端验证您所在的文件夹,该命令将输出当前工作目录。此外,使用ls
命令列出文件将帮助您浏览文件系统。
向其中添加以下模型定义:
...
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.
此命令在您的文件系统上创建一个新迁移,并针对数据库运行它以创建数据库架构。以下是提供给命令的选项的快速概览:
--preview-feature
:必需,因为 Prisma Migrate 当前处于预览状态。--name "init"
:指定迁移的名称(将用于命名在您的文件系统上创建的迁移文件夹)。--skip-generate
:跳过生成 Prisma Client(这将在下一步中完成)。
您的prisma/migrations
目录现在填充了 SQL 迁移文件。这种方法允许您跟踪对数据库架构的更改并在生产中创建相同的数据库架构。
注意:如果您已经将 Prisma Migrate 与my-blog
数据库一起使用,并且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
which 将重置数据库来解决此问题。请注意,这会导致数据库中的所有数据丢失。
您现在已经创建了数据库架构。在下一步中,您将安装 Prisma Client 并在您的 GraphQL 解析器中使用它。
第 8 步 – 在 GraphQL 解析器中使用 Prisma 客户端
Prisma Client 是一个自动生成且类型安全的对象关系映射器 (ORM),您可以使用它以编程方式从 Node.js 应用程序读取和写入数据库中的数据。在此步骤中,您将在项目中安装 Prisma Client。
再次打开终端并安装 Prisma Client npm 包:
- npm install @prisma/client
注意: Prisma 客户端通过为node_modules
文件夹生成基于 Prisma 架构的代码,为您提供丰富的自动完成功能。要生成代码,请使用该npx prisma generate
命令。这通常在您创建并运行新迁移后完成。但是,在第一次安装时,这不是必需的,因为它会在postinstall
挂钩中自动生成。
创建数据库和 GraphQL 模式并安装 Prisma 客户端后,您现在将在 GraphQL 解析器中使用 Prisma 客户端来读取和写入数据库中的数据。您将通过替换posts
迄今为止用于保存数据的数组来完成此操作。
首先创建以下文件:
- nano 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
然后prisma
从./db
文件顶部导入:
const { prisma } = require('./db')
...
然后删除posts
数组:
-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
使用以下解析器更新对象:
...
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 客户端查询:
findMany
: 获取publish
字段为 的帖子false
。findOne
:获取id
字段等于id
GraphQL 参数的单个帖子。
请注意,根据GraphQL 规范,ID
类型的序列化方式与String
. 因此您转换为 aNumber
因为id
Prisma 架构中的 是int
.
接下来,您将更新Mutation
解析器以保存和更新数据库中的帖子。resolvers.Mutation
使用以下解析器更新对象:
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 客户端查询:
create
: 创建Post
记录。update
:更新与查询参数中匹配的Post
记录的已发布字段id
。
您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
将以下内容添加到文件中:
node_modules
prisma/.env
保存并退出文件。
然后运行以下两个命令来提交更改:
- git add .
- git commit -m 'Add Prisma'
接下来,您将在 App Platform 中将 PostgreSQL 数据库添加到您的应用程序。
步骤 9 — 在 App Platform 中创建和迁移 PostgreSQL 数据库
在此步骤中,您将向 App Platform 中的应用程序添加 PostgreSQL 数据库。然后,您将使用 Prisma Migrate 对其运行迁移,以便部署的数据库架构与您的本地数据库相匹配。
首先,转到App Platform 控制台并选择您在第 5 步中创建的prisma-graphql项目。
接下来,转到“组件”选项卡。
单击+ Create Component并选择Database,这将引导您进入配置数据库的页面。
选择Dev Database并单击Create and Attach。
您将被重定向回组件页面,其中将有一个用于创建数据库的进度条。
创建数据库后,您将从本地计算机对 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的客户端将使用通过该环境变量env("DATABASE_URL")
在datasource
你的PRISMA模式的块。
这将自动触发构建。如果您打开 App Platform 控制台,您将看到一个Deploying进度条。
部署成功后,您将收到“已部署成功”消息。
您现在已经使用数据库备份了已部署的 GraphQL API。打开Live App,它将带您进入 GraphQL Playground。使用步骤 3 中的相同查询测试 GraphQL API。
在最后一步中,您将通过添加User
模型来改进 GraphQL API 。
第 10 步 – 添加用户模型
您用于博客的 GraphQL API 有一个名为Post
. 在此步骤中,您将通过在 Prisma 模式中定义新模型并调整 GraphQL 模式以使用新模型来改进 API。您将介绍一个User
与模型具有一对多关系的Post
模型。这将允许您代表帖子的作者并将多个帖子关联到每个用户。然后,您将改进 GraphQL 架构,以允许通过 API 创建用户并将帖子与用户相关联。
首先,打开 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
代表用户的模型。- 两个关系字段:
author
和posts
。关系字段在 Prisma 级别定义模型之间的连接,并且不存在于数据库中。这些字段用于生成 Prisma 客户端并访问与 Prisma 客户端的关系。 - 的
authorId
字段,其通过引用的@relation
属性。Prisma 将在数据库中创建一个外键来连接Post
和User
。
请注意,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 客户端,以便您可以使用新表和字段。
现在,您将对 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
如下:
...
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
作为其输入类型。 PostCreateWithoutAuthorInput
输入中使用的输入类型,用于UserCreateInput
创建帖子作为createUser
突变的一部分。- 突变的
authorEmail
可选参数createDraft
。
更新架构后,您现在将更新解析器以匹配架构。
更新resolvers
对象如下:
...
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
突变解析器使用嵌套写入创建用户和相关帖子。 - The
User.posts
andPost.author
resolvers define how to resolve theposts
andauthor
fields when theUser
orPost
are queried. These use Prisma’s Fluent API to fetch the relations.
Save and exit the file.
Start the server to test the GraphQL API:
- npm start
Begin by testing the createUser
resolver with the following GraphQL mutation:
mutation {
createUser(data: { email: "[email protected]", name: "Natalia" }) {
email
id
}
}
This will create a user.
Next, test the createDraft
resolver with the following mutation:
mutation {
createDraft(
authorEmail: "[email protected]"
title: "Deploying a GraphQL API to App Platform"
) {
id
title
content
published
author {
id
name
}
}
}
Notice that you can fetch the author
whenever the return value of a query is Post
. In this example, the Post.author
resolver will be called.
Finally, commit your changes and push to deploy the API:
- git add .
- git commit -m "add user model"
- git push
You have successfully evolved your database schema with Prisma Migrate and exposed the new model in your GraphQL API.
Conclusion
In this article, you built a GraphQL API with Prisma and GraphQL, and deployed it to DigitalOcean’s App Platform. You defined a GraphQL schema and resolvers with Apollo Server. You then used Prisma Client in your GraphQL resolvers to persist and query data in the PostgreSQL database.
As a next step, you can further extend the GraphQL API with a query to fetch individual users and a mutation to connect an existing draft to a user.
If you’re interested in exploring the data in the database, check out Prisma Studio. Be sure to visit the Prisma documentation to learn about different aspects of Prisma and explore some ready-to-run example projects in the prisma-examples
repository.
You can find the code for this project in the DigitalOcean Community repository.