如何使用 Node.js、Prisma 和 PostgreSQL 通过 5 个步骤构建 CRUD RESTful API
💻📓💝📕💓📗💖📘💗📙💜📔📒🎊📚📖💙🎁🎉
介绍
对象关系映射 (ORM) 是一个框架,它在数据库上提供抽象层,以便与来自不兼容数据源的数据进行交互并管理查询。在本基础课程中,我们将学习如何使用 Node.js、Prisma 和 PostgreSQL 数据库的 Docker 实例构建后端。
Prisma是一个用Rust编写的Nodejs和TypeScript开源 ORM 。它在 Sequelize、Bookshelf、Waterline、Objection 和 TypeORM 等众多 JavaScript ORM 中脱颖而出。它包含 3 个主要工具:
🔷 Prisma Client:自动生成且类型安全的数据库客户端
🔷 Prisma Migrate:声明式数据建模和可定制的迁移
🔷 Prisma Studio:用于查看和编辑数据库中数据的 GUI。
这些工具旨在提高应用程序开发人员在数据库工作流程中的工作效率。
Prisma 目前支持 PostgreSQL、MySQL、SQLite、SQL Server(预览版)和 MongoDB(预览版)。
先决条件
要练习本课,您需要具备以下条件:
- 您的计算机上安装了 Node.js v10 到 v14。
- PostgreSQL v13 正在运行,可以使用Docker轻松设置,如此处所示。
- 或者,VsCode和一杯好茶 ☕️
内容
- 第 1 步 - 创建 TypeScript 项目
- 第2步 - 使用PostgreSQL设置Prisma
- 第 3 步 - 定义数据模型并创建数据库表
- 第 4 步 - 在纯脚本中探索 Prisma 客户端查询
- 第5步 - 实现您的第一个REST API路由
🕐 第 1 步 — 创建 TypeScript 项目
在此步骤中,您将使用 npm 设置一个简单的 TypeScript 项目。该项目将作为您在本课程中构建的 REST API 的基础。
首先,让我们为项目创建一个新目录:
$ mkdir playlist
接下来,导航到目录并初始化一个空的 npm 项目。
$ cd playlist
$ npm init -y
您将得到类似这样的结果:
Wrote to /home/user/Projects/lesson/playlist/package.json:
{
"name": "playlist",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
接下来,通过执行以下命令在您的项目中设置 TypeScript:
$ npm install typescript ts-node @types/node -D
这将在您的项目中安装三个包作为开发依赖项:
🔷 typescript :TypeScript 工具链。🔷 ts-node:无需事先编译为 JavaScript 即可运行 TypeScript 应用程序的包。🔷 @types/node:Node.js 的 TypeScript 类型定义
。
最后,在播放列表目录中添加一个tsconfig.json文件,以确保 TypeScript 已为项目正确配置。
播放列表/tsconfig.json
{
"compilerOptions": {
"sourceMap": true,
"outDir": "dist",
"strict": true,
"lib": ["esnext"],
"esModuleInterop": true
}
}
🕐第 2 步 - 使用 PostgreSQL 设置 Prisma
在此步骤中,您将安装 Prisma CLI,创建初始 Prisma 模式文件,并使用 Docker 设置 PostgreSQL 并将 Prisma 连接到它。Prisma 模式是 Prisma 设置的主要配置文件,包含您的数据库模式。
首先使用以下命令安装 Prisma CLI:
$ npm install prisma -D
接下来,您将使用 Docker 设置 PostgreSQL 数据库。使用以下命令创建一个新的 Docker-Compose 文件:
$ nano docker-compose.yml
现在将以下代码添加到新创建的文件中:
playlist/docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:13
restart: always
environment:
- POSTGRES_USER=africa
- POSTGRES_PASSWORD=village_people
volumes:
- postgres:/var/lib/postgresql/data
ports:
- '5432:5432'
volumes:
postgres:
此 Docker Compose 文件配置了一个 PostgreSQL 数据库,可通过 Docker 容器的 5432 端口访问。另请注意,数据库凭据当前设置为africa(用户名)和village_people(用户密码)。您可以根据需要调整这些凭据。保存并退出文件。
完成此设置后,继续使用以下命令启动 PostgreSQL 数据库服务器:
$ docker-compose up -d
嘿,这可能需要一段时间,因为需要拉取并启动 docker 镜像,除非你之前运行过它。现在就喝杯茶☕️。完成后,运行:
$ docker ps
该命令的输出将类似于以下内容:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7621fce68710 postgres:13 "docker-entrypoint.s…" 13 hours ago Up 13 hours 0.0.0.0:5432->5432/tcp, :::5432->5432/tcp playlist_postgres_1
数据库服务器运行后,您现在可以创建 Prisma 设置。从 Prisma CLI 运行以下命令:
$ npx prisma init
这将打印以下输出:
✔ Your Prisma schema was created at prisma/schema.prisma
You can now open it in your favorite editor.
运行该命令后,Prisma CLI 会在项目中创建一个名为prisma的新文件夹。它包含以下两个文件:
🔷 schema.prisma
您的 Prisma 项目的主要配置文件(将包括您的数据模型)。
🔷 .env
一个 dotenv 文件,用于定义数据库连接 URL。
为了确保 Prisma 知道数据库的位置,请打开 .env 文件并调整DATABASE_URL环境变量。
首先打开.env文件:
# Set the appropriate value for the Database
DB_HOST=localhost
DB_PORT=5432
DB_SCHEMA=playlist
POSTGRES_USER=africa
POSTGRES_PASSWORD=village_people
POSTGRES_DB=playlist
DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${DB_HOST}:${DB_PORT}/${POSTGRES_DB}?schema=${DB_SCHEMA}&sslmode=prefer
🕐 第 3 步 — 定义数据模型并创建数据库表
在此步骤中,您将在 Prisma 模式文件中定义数据模型。
然后,该数据模型将使用 Prisma Migrate 映射到数据库,Prisma Migrate 将生成并发送用于创建与数据模型对应的表的 SQL 语句。
由于您正在构建播放列表应用程序,因此该应用程序的主要实体将是艺术家和歌曲。
Prisma 使用自己的数据建模语言来定义应用程序数据的形状。
首先,使用您最喜欢的编辑器打开prisma/schema.prisma文件并进行以下更改:
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model Artist {
id Int @default(autoincrement()) @id
email String @unique
name String?
songs Song[]
}
model Song {
id Int @default(autoincrement()) @id
title String
content String?
released Boolean @default(false)
singer Artist? @relation(fields: [singerId], references: [id])
singerId Int?
}
您正在定义两个模型,分别名为Artist和Song。每个模型都包含多个字段,用于表示模型的属性。这些模型将映射到数据库表;字段代表各个列。
还要注意,这两个模型之间存在一对多关系,由Artist和Song上的songs和singer关系字段指定。这意味着一位艺术家可以与多首歌曲相关联。
有了这些模型,您现在可以使用 Prisma Migrate 在数据库中创建相应的表。在终端中运行以下命令:
$ npx prisma migrate dev --name "init"
此命令会在文件系统上创建一个新的 SQL 迁移并将其发送到数据库。该命令的输出类似如下:
Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Datasource "db": PostgreSQL database "playlist", schema "public" at "localhost:5432"
PostgreSQL database playlist created at localhost:5432
The following migration(s) have been created and applied from new schema changes:
migrations/
└─ 20210810103751_init/
└─ migration.sql
Your database is now in sync with your schema.
/home/user/Projects/lesson/playlist/prisma/migrations/20210810103751_init/migration.sql目录中的 SQL 迁移文件包含针对数据库执行的以下语句:
-- CreateTable
CREATE TABLE "Artist" (
"id" SERIAL NOT NULL,
"email" TEXT NOT NULL,
"name" TEXT,
PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Song" (
"id" SERIAL NOT NULL,
"title" TEXT NOT NULL,
"content" TEXT,
"released" BOOLEAN NOT NULL DEFAULT false,
"singerId" INTEGER,
PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "Artist.email_unique" ON "Artist"("email");
-- AddForeignKey
ALTER TABLE "Song" ADD FOREIGN KEY ("singerId") REFERENCES "Artist"("id") ON DELETE SET NULL ON UPDATE CASCADE;
🕐 第 4 步 — 在纯脚本中探索 Prisma 客户端查询
Prisma Client 是一个自动生成且类型安全的查询构建器,您可以使用它以编程方式从 Node.js 或 TypeScript 应用程序读取和写入数据库中的数据。您可以使用它在 REST API 路由中访问数据库,从而取代传统的 ORM、纯 SQL 查询、自定义数据访问层或任何其他与数据库通信的方法。
在此步骤中,您将安装 Prisma 客户端并熟悉可以使用它发送的查询。在后续步骤中实现 REST API 的路由之前,您将首先在一个普通的可执行脚本中探索一些 Prisma 客户端查询。
首先,打开终端并安装 Prisma Client npm 包,继续在您的项目中安装 Prisma Client:
$ npm install @prisma/client
接下来,创建一个名为src的新目录,其中将包含您的源文件,并在新目录中创建一个 TypeScript 文件:
$ nano src/main.ts
所有 Prisma 客户端查询都会返回 Promise,您可以在代码中等待。这需要您在异步函数内部发送查询。
添加以下在脚本中执行的异步函数的样板:
// playlist/src/main.ts
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
async function main() {
// ... your Prisma Client queries will go here
}
main()
.catch((e) => console.error(e))
.finally(async () => await prisma.disconnect())
以下是样板的简要分解:
🔷从先前安装的@prisma/client npm包中导入PrismaClient构造函数。🔷通过调用构造函数 实例化PrismaClient并获取一个名为prisma的实例。🔷 定义一个名为main的异步函数,接下来将在其中添加 Prisma Client 查询。🔷 调用 main 函数,同时捕获任何潜在异常并确保 Prisma Client 通过调用prisma.disconnect()关闭所有打开的数据库连接。
有了主要功能后,您就可以开始向脚本添加 Prisma Client 查询了。调整 index.ts 使其如下所示:
// playlist/src/main.ts
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
async function main() {
const newArtist = await prisma.artist.create({
data: {
name: 'Osinachi Kalu',
email: 'sinach@sinachmusic.com',
songs: {
create: {
title: 'I Know Who I Am',
},
},
},
})
console.log('Created new artist: ', newArtist)
const allArtists = await prisma.artist.findMany({
include: { songs: true },
})
console.log('All artists: ')
console.dir(allArtists, { depth: null })
}
main()
.catch((e) => console.error(e))
.finally(async () => await prisma.$disconnect())
//! put a dollar-sign between "." and "disconnect"
在此代码中,您使用了两个 Prisma Client 查询:
- create:创建一条新的用户记录。请注意,您实际上使用了嵌套写入,这意味着您在同一个查询中同时创建了艺术家记录和歌曲记录。
- findMany:从数据库中读取所有现有的 Artist 记录。你提供了 include 选项,该选项还会加载每条 Artist 记录对应的 Song 记录。现在使用以下命令运行脚本:
$ npx ts-node src/main.ts
您将在终端中收到以下输出:
Created new artist: { id: 1, email: 'sinach@sinachmusic.com', name: 'Osinachi Kalu' }
All artists:
[
{
id: 1,
email: 'sinach@sinachmusic.com',
name: 'Osinachi Kalu',
songs: [
{
id: 1,
title: 'I Know Who I Am',
content: null,
released: false,
singerId: 1
}
]
}
]
或者,您可以通过运行以下命令使用Prisma Studio操作 Postgres 数据库中的记录:
$ npx prisma studio
输出:
Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Prisma Studio is up on http://localhost:5555
在浏览器中访问http://localhost:5555并浏览你的模型。然后按Ctrl + C在终端停止Prisma Studio,或者在同一个播放列表项目目录中打开一个新终端。
🕐 第 5 步 — 实现您的第一个 REST API 路由
在此步骤中,您将在应用程序中安装 Express。Express 是一个流行的 Node.js Web 框架,您将使用它来实现此项目中的 REST API 路由。您将实现的第一个路由将允许您使用 GET 请求从 API 中获取所有艺术家。艺术家数据将使用 Prisma 客户端从数据库中检索。
继续使用以下命令安装 Express:
$ npm install express
由于你使用的是 TypeScript,因此你还需要安装相应的类型作为开发依赖项。运行以下命令即可执行此操作:
$ npm install @types/express -D
在 src 目录中创建一个新文件 index.ts 并输入以下内容以启动您的 REST API:
// playlist/src/index.ts
// #1
import { PrismaClient } from '@prisma/client'
import express from 'express'
// #2
const prisma = new PrismaClient()
// #3
const app = express()
// #4
app.use(express.json())
// #5
app.get('/artists', async (req, res) => {
const artists = await prisma.artist.findMany()
res.json({
success: true,
payload: artists,
message: "Operation Successful",
})
})
app.use((req, res, next) => {
res.status(404);
return res.json({
success: false,
payload: null,
message: `API SAYS: Endpoint not found for path: ${req.path}`,
});
});
// #6
app.listen(3000, () =>
console.log('REST API server ready at: http://localhost:3000'),
)
以下是代码的简要分解:
-
您从相应的 npm 包中导入PrismaClient和 express。
-
您可以通过调用构造函数来实例化 PrismaClient 并获得一个名为prisma的实例。
-
您可以通过调用 express() 来创建 Express 应用程序。
-
添加express.json()中间件以确保 JSON 数据能够被 Express 正确处理。
-
通过在app.use和app.listen调用之间添加 api 端点来实现您的第一条路由。
-
您在端口 3000 上启动服务器。
输出:
REST API server ready at: http://localhost:3000
要测试你的路由,请打开浏览器访问http://localhost:3000。
或者,打开新的终端窗口或标签页(以便本地 Web 服务器可以继续运行)并执行以下命令:
curl http://localhost:3000/artists
您将收到在上一步中创建的用户数据:
输出:
{"success":true,"payload":[{"id":1,"email":"sinach@sinachmusic.com","name":"Osinachi Kalu"}],"message":"Operation Successful"}
第 6 步 - 实现剩余的 REST API 路由
在此步骤中,您将为博客应用程序实现其余的 REST API 路由。最后,您的 Web 服务器将处理各种GET、POST、PUT和DELETE请求。
以下是您将实施的不同路线的概述:
序号 | HTTP 方法 | 路线 | 描述 |
---|---|---|---|
1 | 得到 | /播放列表 | 获取所有已发布的歌曲。 |
2 | 得到 | /歌曲/:id | 通过 ID 获取特定歌曲。 |
3 | 邮政 | /艺术家 | 創造了一位新的藝術家。 |
4 | 邮政 | /歌曲 | 创作(或谱写)一首新歌(未发行) |
5 | 放 | /歌曲/发行/:id | 将歌曲的已发布字段设置为 true。 |
6 | 删除 | /歌曲/:id | 根据数据库记录 ID 删除歌曲。 |
接下来,修改 index.ts 文件以实现其他 API 路由:
// playlist/src/index.ts
import { PrismaClient } from '@prisma/client'
import express from 'express'
const prisma = new PrismaClient()
const app = express()
app.use(express.json())
//* 1. Fetches all released songs.
app.get('/playlist', async (req, res) => {
const songs = await prisma.song.findMany({
where: { released: true },
include: { singer: true }
})
res.json({
success: true,
payload: songs,
})
})
//* 2. Fetches a specific song by its ID.
app.get(`/song/:id`, async (req, res) => {
const { id } = req.params
const song = await prisma.song.findFirst({
where: { id: Number(id) },
})
res.json({
success: true,
payload: song,
})
})
//* 3. Creates a new artist.
app.post(`/artist`, async (req, res) => {
const result = await prisma.artist.create({
data: { ...req.body },
})
res.json({
success: true,
payload: result,
})
})
//* 4. Creates (or compose) a new song (unreleased)
app.post(`/song`, async (req, res) => {
const { title, content, singerEmail } = req.body
const result = await prisma.song.create({
data: {
title,
content,
released: false,
singer: { connect: { email: singerEmail } },
},
})
res.json({
success: true,
payload: result,
})
})
//* 5. Sets the released field of a song to true.
app.put('/song/release/:id', async (req, res) => {
const { id } = req.params
const song = await prisma.song.update({
where: { id: Number(id) },
data: { released: true },
})
res.json({
success: true,
payload: song,
})
})
//* 6. Deletes a song by its ID.
app.delete(`/song/:id`, async (req, res) => {
const { id } = req.params
const song = await prisma.song.delete({
where: { id: Number(id) },
})
res.json({
success: true,
payload: song,
})
})
//* 7. Fetches all Artist.
app.get('/artists', async (req, res) => {
const artists = await prisma.artist.findMany()
res.json({
success: true,
payload: artists,
})
})
app.use((req, res, next) => {
res.status(404);
return res.json({
success: false,
payload: null,
message: `API SAYS: Endpoint not found for path: ${req.path}`,
});
});
// #6
app.listen(3000, () =>
console.log('REST API server ready at: http://localhost:3000'),
)
您可以使用CTRL + C停止服务器来测试新路由。然后,使用以下命令重新启动服务器:
$ npx ts-node src/index.ts
测试 API 路由
-
获取所有已发布的歌曲。
$ curl http://localhost:3000/playlist
-
通过 ID 获取特定歌曲。
$ curl http://localhost:3000/song/1
-
創造了一位新的藝術家。
curl -X POST -H "Content-Type: application/json" -d '{"name":"Nditah Sam", "email":"contact@telixia.com"}' http://localhost:3000/artist
-
创作(或谱写)一首新歌(未发行)
curl -X POST -H "Content-Type: application/json" -d '{"title":"Take my hand", "singerEmail":"contact@telixia.com"}' http://localhost:3000/song
-
将歌曲的已发布字段设置为 true。
curl -X PUT http://localhost:3000/song/release/2
-
根据数据库记录 ID 删除歌曲。
curl -X DELETE http://localhost:3000/song/1
-
再次重新查询播放列表
curl http://localhost:3000/playlist
结论
在本课中,你创建了一个 REST API 服务器,其中包含多个不同的路由,用于为示例播放列表后端应用程序创建、读取、更新和删除艺术家和歌曲数据。在 API 路由内部,你将使用 Prisma 客户端将相应的查询发送到你的 postgres 数据库。
在下一课中,您将学习如何使用 Node、Prisma 和 Postgres 构建 GraphQL API。
进一步阅读
[1]下一课将学习如何使用 Node、Prisma 和 Postgres 构建 GraphQL API
[2] Prisma 组件
快乐阅读和编码