作者选择了(https://www.brightfunds.org/funds/diversity-in-tech)和(https://www.brightfunds.org/funds/tech-education)作为 写给捐款计划的一部分的捐款。
介绍
Prisma是Node.js和TypeScript的开源ORM,由三个主要工具组成:
- Prisma 客户端 :自动生成和安全编写的查询构建程序. * ** Prisma Migrate** :强大的数据建模和迁移系统. * ** Prisma Studio** :用于查看和编辑数据库中的 GUI。
这些工具旨在提高应用程序开发人员在其数据库工作流中的生产力. Prisma 的主要好处之一是其提供的抽象程度:而不是弄清楚复杂的 SQL 查询或方案迁移,应用程序开发人员可以在使用 Prisma 时更直观地推理他们的数据。
在本教程中,您将使用 Prisma 和 PostgreSQL数据库构建一个 REST API 用于在 TypeScript中的小型博客应用程序,您将使用 Docker本地设置您的 PostgreSQL 数据库,并使用 Express实施 REST API 路由。
前提条件
本教程假设如下:
- Node.js版本 14 或更高版本安装在您的计算机上。您可以使用 如何安装 Node.js 和创建本地开发环境指南为您的操作系统设置此设置。 * Docker安装在您的计算机上(运行 PostgreSQL 数据库)。
对TypeScript和REST API的基本熟悉是有帮助的,但不需要这个教程。
第1步:创建您的TypeScript项目
在此步骤中,您将使用npm
设置一个简单的TypeScript项目,该项目将成为您将在本教程中构建的REST API的基础。
首先,为您的项目创建一个新目录:
1mkdir my-blog
接下来,导航到目录并初始化一个空的npm
项目. 请注意,这里的-y
选项意味着您正在跳过命令的互动提示。
1cd my-blog
2npm init -y
有关这些提示的详细信息,您可以按照 如何使用 npm 和 package.json 的 Node.js 模块中的步骤 1 进行。
您将收到类似于以下的输出,默认答案在位置:
1[secondary_label Output]
2Wrote to /.../my-blog/package.json:
3
4{
5 "name": "my-blog",
6 "version": "1.0.0",
7 "description": "",
8 "main": "index.js",
9 "scripts": {
10 "test": "echo \"Error: no test specified\" && exit 1"
11 },
12 "keywords": [],
13 "author": "",
14 "license": "ISC"
15}
此命令创建一个最小的 package.json
文件,您将其用作您的 npm
项目的配置文件。
執行下列命令以進行簡單的 TypeScript 設定:
1npm install typescript ts-node @types/node --save-dev
这将安装三个软件包作为项目中的开发依赖:
typescript
:TypeScript工具链. *ts-node
:一个包来运行TypeScript应用程序而无需先编译到JavaScript。
最后要做的是添加一个 tsconfig.json
文件,以确保您要构建的应用程序的TypeScript配置正确。
首先,运行以下命令来创建文件:
1nano tsconfig.json
将下列 JSON 代码添加到文件中:
1[label my-blog/tsconfig.json]
2{
3 "compilerOptions": {
4 "sourceMap": true,
5 "outDir": "dist",
6 "strict": true,
7 "lib": ["esnext"],
8 "esModuleInterop": true
9 }
10}
保存和退出文件。
此设置是 TypeScript 项目的标准和最小配置,如果你想了解配置文件的个别属性,可以查看 TypeScript 文档。
您已经使用npm
设置了您的简单的 TypeScript 项目,接下来您将使用 Docker 设置您的 PostgreSQL 数据库,并将 Prisma 连接到它。
第2步:用PostgreSQL设置 Prisma
在此步骤中,您将安装 Prisma CLI,创建您的初始 Prisma schema文件,并与 Docker 设置 PostgreSQL,并将 Prisma 连接到它。
开始使用以下命令安装 Prisma CLI:
1npm install prisma --save-dev
作为最佳做法,建议您在项目中(而不是作为全球安装)安装 Prisma CLI(本地安装 Prisma CLI)。
接下来,您将使用 Docker 设置您的 PostgreSQL 数据库. 使用以下命令创建一个新的 Docker Compose 文件:
1nano docker-compose.yml
现在将以下代码添加到新创建的文件中:
1[label my-blog/docker-compose.yml]
2version: '3.8'
3services:
4 postgres:
5 image: postgres:10.3
6 restart: always
7 environment:
8 - POSTGRES_USER=sammy
9 - POSTGRES_PASSWORD=your_password
10 volumes:
11 - postgres:/var/lib/postgresql/data
12 ports:
13 - '5432:5432'
14volumes:
15 postgres:
此 Docker Compose 文件配置了一个 PostgreSQL 数据库,可以通过 Docker 容器的端口 5432
访问。 数据库凭据目前设置为 sammy' (用户) 和
your_password' (密码)。 请自由将这些凭据调整为您喜爱的用户和密码。 保存和退出文件。
有了此设置,请使用以下命令启动 PostgreSQL 数据库服务器:
1docker-compose up -d
这个命令的输出将类似于此:
1[secondary_label Output]
2Pulling postgres (postgres:10.3)...
310.3: Pulling from library/postgres
4f2aa67a397c4: Pull complete
56de83ca23e55: Pull complete
6. . .
7Status: Downloaded newer image for postgres:10.3
8Creating my-blog_postgres_1 ... done
您可以通过以下命令验证数据库服务器是否正在运行:
1docker ps
这个命令将输出类似于此的东西:
1[secondary_label Output]
2CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
38547f8e007ba postgres:10.3 "docker-entrypoint.s…" 3 seconds ago Up 2 seconds 0.0.0.0:5432->5432/tcp my-blog_postgres_1
随着数据库服务器运行,您现在可以创建您的 Prisma 设置. 从 Prisma CLI 运行以下命令:
1npx prisma init
此命令将打印以下输出:
1[secondary_label Output]
2✔ Your Prisma schema was created at prisma/schema.prisma.
3 You can now open it in your favorite editor.
作为最佳做法,您应该将Prisma CLI的所有召唤前缀为npx
,以确保您的本地安装正在使用。
在运行命令后,Prisma CLI在您的项目中创建了一个名为prisma
的新文件夹. 您将在其内部找到一个schema.prisma
文件,这是您的Prisma项目的主要配置文件(包括您的数据模型)。
要确保 Prisma 知道您的数据库的位置,请打开.env 文件并调整环境变量。
首先打开.env 文件:
1nano .env
现在,您可以更新环境变量如下:
1[label my-blog/.env]
2DATABASE_URL="postgresql://sammy:your_password@localhost:5432/my-blog?schema=public"
请确保将数据库凭据更改为您在 Docker Compose 文件中指定的凭据。 有关连接 URL 格式的更多信息,请访问 Prisma docs。
一旦完成,保存并退出文件。
在此步骤中,您将使用 Docker 设置您的 PostgreSQL 数据库,安装 Prisma CLI,并通过环境变量连接 Prisma 到数据库。
第3步:定义您的数据模型和创建数据库表
在此步骤中,您将在 Prisma 方案文件中定义您的 数据模型 然后将该数据模型与 Prisma Migrate 对照到数据库中,该数据模型将生成并发送 SQL 陈述来创建与您的数据模型相符的表格。
Prisma 使用自己的数据建模语言(https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-schema# syntax)来定义应用数据的形状。
首先,用以下命令打开你的schema.prisma
文件:
1nano prisma/schema.prisma
现在,添加以下模型定义,您可以将模型放置在文件的底部,就在发电机客户端
块后:
1[label my-blog/prisma/schema.prisma]
2. . .
3model User {
4 id Int @default(autoincrement()) @id
5 email String @unique
6 name String?
7 posts Post[]
8}
9
10model Post {
11 id Int @default(autoincrement()) @id
12 title String
13 content String?
14 published Boolean @default(false)
15 author User? @relation(fields: [authorId], references: [id])
16 authorId Int?
17}
您正在定义两个模型(https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-schema/models):`用户和
邮件`。 每个模型都有代表模型的属性的一些 字段。
两个模型之间存在一个对许多的关系(https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-schema/relations),由用户
和帖子
上的帖子
和作者
的关系字段指定。
保存和退出文件。
有了这些模型,您现在可以使用 Prisma Migrate 在数据库中创建相应的表。
1npx prisma migrate dev --name init
此命令会在您的文件系统上创建一个新的 SQL 迁移,并将其发送到数据库. 指令中提供的 --name init
选项将指定迁移的名称,并将用于在您的文件系统上创建的迁移文件夹。
这个命令的输出将类似于此:
1[secondary_label Output]
2Environment variables loaded from .env
3Prisma schema loaded from prisma/schema.prisma
4Datasource "db": PostgreSQL database "my-blog", schema "public" at "localhost:5432"
5
6PostgreSQL database my-blog created at localhost:5432
7
8The following migration(s) have been created and applied from new schema changes:
9
10migrations/
11 └─ 20201209084626_init/
12 └─ migration.sql
13
14Running generate... (Use --skip-generate to skip the generators)
15
16✔ Generated Prisma Client (2.13.0) to ./node_modules/@prisma/client in 75ms
在prisma/migrations/20201209084626_init/migration.sql
目录中的 SQL 迁移文件中,对数据库进行了执行的下列陈述(文件名中的突出部分在您的设置中可能有所不同):
1[label prisma/migrations/20201209084626_init/migration.sql]
2-- CreateTable
3CREATE TABLE "User" (
4"id" SERIAL,
5 "email" TEXT NOT NULL,
6 "name" TEXT,
7
8 PRIMARY KEY ("id")
9);
10
11-- CreateTable
12CREATE TABLE "Post" (
13"id" SERIAL,
14 "title" TEXT NOT NULL,
15 "content" TEXT,
16 "published" BOOLEAN NOT NULL DEFAULT false,
17 "authorId" INTEGER,
18
19 PRIMARY KEY ("id")
20);
21
22-- CreateIndex
23CREATE UNIQUE INDEX "User.email_unique" ON "User"("email");
24
25-- AddForeignKey
26ALTER TABLE "Post" ADD FOREIGN KEY("authorId")REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
您还可以自定义生成的 SQL 迁移文件,如果您将 --create-only
选项添加到 prisma migrate dev
命令中;例如,您可以设置触发器或使用潜在数据库的其他功能。
在此步骤中,您在 Prisma 方案中定义了数据模型,并使用 Prisma Migrate 创建了相应的数据库表。
步骤4 — 在简单的脚本中探索 Prisma 客户端查询
Prisma 客户端是一个自动生成和类型安全的查询构建程序,您可以使用它来从 Node.js 或 TypeScript 应用程序中编程读取和写入数据库中的数据,您将使用它用于 REST API 路径中的数据库访问,取代传统的 ORM,简单的 SQL 查询,自定义数据访问层或任何其他方法来与数据库交谈。
在此步骤中,您将安装 Prisma 客户端并熟悉您可以发送的查询,然后在下一个步骤中实现 REST API 的路径之前,您将首先在简单的可执行脚本中探索一些 Prisma 客户端查询。
首先,在您的项目文件夹中安装 Prisma 客户端,使用 Prisma 客户端 npm
包:
1npm install @prisma/client
接下来,创建一个名为src
的新目录,该目录将包含您的源文件:
1mkdir src
现在,在新目录中创建一个 TypeScript 文件:
1nano src/index.ts
所有 Prisma 客户端查询都返回 承诺,您可以在代码中等待
。
在「src/index.ts」文件中,添加下列 boilerplate 函数「async」函数,该函数在你的脚本中执行:
1[label my-blog/src/index.ts]
2import { PrismaClient } from '@prisma/client'
3
4const prisma = new PrismaClient()
5
6async function main() {
7 // ... your Prisma Client queries will go here
8}
9
10main()
11 .catch((e) => console.error(e))
12 .finally(async () => await prisma.$disconnect())
以下是锅炉板的快速分解:
您从先前安装的@prisma/client``npm
包中导入了PrismaClient
构建器,2.您通过呼叫构建器并获得一个名为prisma
的实例来实时化PrismaClient
。3您定义了一个名为main
的async
函数,在那里您将添加您的Prisma Client查询。4您呼叫main
函数,捕捉任何潜在的例外,并确保Prisma Client通过prisma.$disconnect()
关闭任何开放的数据库连接。
有了主要
函数,您可以开始将 Prisma 客户端查询添加到脚本中。
1[label my-blog/src/index.ts]
2import { PrismaClient } from '@prisma/client'
3
4const prisma = new PrismaClient()
5
6async function main() {
7 const newUser = await prisma.user.create({
8 data: {
9 name: 'Alice',
10 email: '[email protected]',
11 posts: {
12 create: {
13 title: 'Hello World',
14 },
15 },
16 },
17 })
18 console.log('Created new user: ', newUser)
19
20 const allUsers = await prisma.user.findMany({
21 include: { posts: true },
22 })
23 console.log('All users: ')
24 console.dir(allUsers, { depth: null })
25}
26
27main()
28 .catch((e) => console.error(e))
29 .finally(async () => await prisma.$disconnect())
在此代码中,您正在使用两个 Prisma 客户端查询:
create
: 创建一个新的User
记录. 您使用一个 nested write 查询在同一个查询中创建一个User
和一个Post
记录。 *findMany
: 从数据库中读取所有现有的User
记录。
保存并关闭文件。
现在用以下命令运行脚本:
1npx ts-node src/index.ts
您将在您的终端中收到以下输出:
1[secondary_label Output]
2Created new user: { id: 1, email: '[email protected]', name: 'Alice' }
3[
4 {
5 id: 1,
6 email: '[email protected]',
7 name: 'Alice',
8 posts: [
9 {
10 id: 1,
11 title: 'Hello World',
12 content: null,
13 published: false,
14 authorId: 1
15 }
16 ]
17 }
注意: 如果您正在使用数据库 GUI,您可以通过查看用户
和邮件
表来验证数据的创建,也可以通过运行npx prisma studio
在Prisma Studio中探索数据。
您现在已经使用 Prisma Client 读取和写入数据库中的数据,在剩余的步骤中,您将实现样本 REST API 的路径。
步骤5 - 实施您的第一个 REST API 路径
在此步骤中,您将安装 Express在您的应用程序中。Express 是 Node.js 的流行的 Web 框架,您将在该项目中使用它来实现 REST API 路由。
使用以下命令安装 Express:
1npm install express
由于您正在使用 TypeScript,您还需要将相应类型安装为开发依赖。
1npm install @types/express --save-dev
有了依赖性,您可以设置您的Express应用程序。
重新打开主源文件:
1nano src/index.ts
现在删除index.ts
中的所有代码,并用以下代码来启动 REST API:
1[label my-blog/src/index.ts]
2import { PrismaClient } from '@prisma/client'
3import express from 'express'
4
5const prisma = new PrismaClient()
6const app = express()
7
8app.use(express.json())
9
10// ... your REST API routes will go here
11
12app.listen(3000, () =>
13 console.log('REST API server ready at: http://localhost:3000'),
14)
以下是代码的快速分解:
你从各自的npm
包中导入PrismaClient
和express
。2你通过调用构建器来实时化PrismaClient
,并获得一个名为prisma
的实例。3你通过调用express()
来创建你的Express应用。4你添加了express.json()
中间软件,以确保Express能正确处理JSON数据。5你在端口3000
上启动服务器。
在app.use
和app.listen
的呼叫之间,添加突出的行以创建app.get
呼叫:
1[label my-blog/src/index.ts]
2. . .
3app.use(express.json())
4
5app.get('/users', async (req, res) => {
6 const users = await prisma.user.findMany()
7 res.json(users)
8})
9
10app.listen(3000, () =>
11console.log('REST API server ready at: http://localhost:3000'),
12)
添加后,保存并退出文件,然后使用以下命令启动本地 Web 服务器:
1npx ts-node src/index.ts
您将获得以下输出:
1[secondary_label Output]
2REST API server ready at: http://localhost:3000
要访问用户路线,您可以将浏览器指向 http://localhost:3000/users
或任何其他HTTP客户端。
在本教程中,您将使用基于终端的 HTTP 客户端 curl
测试所有 REST API 路径。
注意: 如果您更喜欢使用基于 GUI 的 HTTP 客户端,您可以使用 Hoppscotch或 Postman等替代方案。
要测试路线,请打开一个新的终端窗口或选项卡(以便您的本地 Web 服务器可以继续运行),并执行以下命令:
1[environment second]
2curl http://localhost:3000/users
您将收到您在上一步创建的用户
数据:
1[environment second]
2[secondary_label Output]
3[{"id":1,"email":"[email protected]","name":"Alice"}]
这次不包含帖子
数组,因为在实施/用户
路径时,您没有将包括
选项转移到findMany
呼叫中。
您已在 /users
上实现了第一个 REST API 路线,下一步您将部署剩余的 REST API 路线,以便为您的 API 添加更多功能。
步骤6 – 实施剩余的 REST API 路径
在此步骤中,您将为您的博客应用程序实施剩余的 REST API 路径. 最终,您的 Web 服务器将服务于各种GET
,POST
,PUT
和DELETE
请求。
您将实施的路线包括以下选项:
HTTP Method | Route | Description |
---|---|---|
GET | /feed | Fetches all published posts. |
GET | /post/:id | Fetches a specific post by its ID. |
POST | /user | Creates a new user. |
POST | /post | Creates a new post (as a draft). |
PUT | /post/publish/:id | Sets the published field of a post to true . |
DELETE | post/:id | Deletes a post by its ID. |
您将首先执行剩余的两个GET
路径。
您可以通过在键盘上按CTRL+C
来阻止服务器,然后,您可以通过先打开文件进行编辑来更新您的index.ts
文件:
1nano src/index.ts
接下来,添加/app.get
用户路线的实施后所突出的行:
1[label my-blog/src/index.ts]
2. . .
3
4app.get('/feed', async (req, res) => {
5 const posts = await prisma.post.findMany({
6 where: { published: true },
7 include: { author: true }
8 })
9 res.json(posts)
10})
11
12app.get(`/post/:id`, async (req, res) => {
13 const { id } = req.params
14 const post = await prisma.post.findUnique({
15 where: { id: Number(id) },
16 })
17 res.json(post)
18})
19
20app.listen(3000, () =>
21 console.log('REST API server ready at: http://localhost:3000'),
22)
此代码实现了两个GET
请求的API路径:
/feed
: 返回发布帖子的列表. */post/:id
: 返回特定帖子以其ID。
Prisma 客户端在两种实现中都使用。在/feed
路径实现中,您通过 Prisma 客户端过滤器发送的查询用于所有发布
列包含真实
值的邮件
记录。此外,Prisma 客户端查询还使用包括
来获取每个返回的邮件的相关作者
信息。
保存并退出您的文件,然后使用以下方式重新启动服务器:
1npx ts-node src/index.ts
要测试/feed
路径,您可以在第二个终端会话中使用以下curl
命令:
1[environment second]
2curl http://localhost:3000/feed
由于尚未发布任何帖子,答案是一个空数组:
1[environment second]
2[secondary_label Output]
3[]
要测试 /post/:id
路径,您可以使用以下 curl
命令:
1[environment second]
2curl http://localhost:3000/post/1
此命令将返回您最初创建的帖子:
1[environment second]
2[secondary_label Output]
3{"id":1,"title":"Hello World","content":null,"published":false,"authorId":1}
在原始终端会话中,用CTRL+C停止服务器,然后打开index.ts进行编辑:
1nano src/index.ts
将突出的行列添加到index.ts
中,然后执行三个GET
路径:
1[label my-blog/src/index.ts]
2. . .
3
4app.post(`/user`, async (req, res) => {
5 const result = await prisma.user.create({
6 data: { ...req.body },
7 })
8 res.json(result)
9})
10
11app.post(`/post`, async (req, res) => {
12 const { title, content, authorEmail } = req.body
13 const result = await prisma.post.create({
14 data: {
15 title,
16 content,
17 published: false,
18 author: { connect: { email: authorEmail } },
19 },
20 })
21 res.json(result)
22})
23
24app.listen(3000, () =>
25 console.log('REST API server ready at: http://localhost:3000'),
26)
此代码实现了两个POST
请求的 API 路径:
/user
:在数据库中创建一个新用户。
在/user
路径实现中,您将从 HTTP 请求的身体传输到 Prisma 客户端创建
查询的值。
你不能直接从 HTTP 请求的体内传输值;相反,你需要先将它们手动提取,以便将它们传输到 Prisma 客户端查询中。
一旦完成,保存并退出您的文件。
重启服务器使用:
1npx ts-node src/index.ts
要通过/user
路径创建新用户,您可以通过curl
发送以下POST
请求:
1[environment second]
2curl -X POST -H "Content-Type: application/json" -d '{"name":"Bob", "email":"[email protected]"}' http://localhost:3000/user
这将创建数据库中的新用户,打印以下输出:
1[environment second]
2[secondary_label Output]
3{"id":2,"email":"[email protected]","name":"Bob"}
要通过/post
路径创建新帖子,您可以通过curl
发送以下POST
请求:
1[environment second]
2curl -X POST -H "Content-Type: application/json" -d '{"title":"I am Bob", "authorEmail":"[email protected]"}' http://localhost:3000/post
这将在数据库中创建一个新的帖子,并通过电子邮件[email protected]
将其连接到用户。
1[environment second]
2[secondary_label Output]
3{"id":2,"title":"I am Bob","content":null,"published":false,"authorId":2}
最后,您将执行PUT
和DELETE
路径. 停止开发服务器,然后使用以下命令打开index.ts
:
1nano src/index.ts
接下来,在实现两个POST
路径后,添加突出的代码:
1[label my-blog/src/index.ts]
2. . .
3
4app.put('/post/publish/:id', async (req, res) => {
5 const { id } = req.params
6 const post = await prisma.post.update({
7 where: { id: Number(id) },
8 data: { published: true },
9 })
10 res.json(post)
11})
12
13app.delete(`/post/:id`, async (req, res) => {
14 const { id } = req.params
15 const post = await prisma.post.delete({
16 where: { id: Number(id) },
17 })
18 res.json(post)
19})
20
21app.listen(3000, () =>
22 console.log('REST API server ready at: http://localhost:3000'),
23)
此代码实现了一个PUT
和一个DELETE
请求的API路径:
/post/publish/:id
(PUT
):以其ID发布帖子. */post/:id
(DELETE
):以其ID删除帖子。
在/post/publish/:id
路径实现中,要发布的帖子的ID从URL中获取并传递到Prisma客户端的更新
查询中。
保存和退出您的文件。
重启服务器使用:
1npx ts-node src/index.ts
您可以使用以下curl
命令测试PUT
路线:
1[environment second]
2curl -X PUT http://localhost:3000/post/publish/2
此命令将以2
的ID值发布帖子. 如果您重新发送/feed
请求,此帖子将被包含在回复中。
最后,您可以使用以下curl
命令测试DELETE
路径:
1[environment second]
2curl -X DELETE http://localhost:3000/post/1
此命令会删除具有1
ID 值的邮件. 若要验证具有此 ID 的邮件已被删除,您可以使用以下curl
命令将GET
请求重新发送到/post/1
路线:
1[environment second]
2curl http://localhost:3000/post/1
在此步骤中,您已经为您的博客应用程序实现了剩余的 REST API 路径,该 API 现在响应各种GET
,POST
,PUT
和DELETE
请求,并实现可读和写入数据库的功能。
结论
在本文中,您创建了一台 REST API 服务器,使用多种不同的路径来创建、阅读、更新和删除用户和发布样本博客应用程序的数据。
作为下一个步骤,您可以使用 Prisma Migrate 实现额外的 API 路径或扩展您的数据库架构。 访问 Prism 文档(https://www.prisma.io/docs)以了解 Prisma 的不同方面,并使用 GraphQL 或 grPC API 等工具探索一些准备运行的示例项目。