如何通过 5 个步骤使用 Node.js、Prisma 和 PostgreSQL 构建 CRUD RESTful API
简介
对象关系映射器 (ORM) 是一个框架,它在数据库上提供抽象层,以促进与来自不兼容数据源的数据进行交互并管理查询。在这个基础课程中,我们将了解如何使用 Node.js、Prisma 和 PostgreSQL 数据库的 docker 实例构建后端。
Prisma是Nodejs和TypeScript用Rust编写的开源 ORM。它在 Sequelize、Bookshelf、Waterline、Objection 和 TypeORM 等其他 Javascript ORM 中脱颖而出。它由 3 个主要工具组成:
🔷Prisma Client:自动生成和类型安全的数据库客户端
🔷Prisma Migrate:声明性数据建模和可定制的迁移
🔷Prisma Studio:用于查看和编辑数据库中数据的 GUI。
[](https://res.cloudinary.com/practicaldev/image/fetch/s--tPxbUmgX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://imgur.com/ CTGhQZ9.png%3Futm_source%3DPrisma%2BAmbassador%26utm_medium%3DBlog%2Bpost%26utm_campaign%3DPrisma%2BAP%2BNdi-tah%2BAnyeh%2BSamweld)
这些工具旨在提高应用程序开发人员在其数据库工作流程中的生产力。
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 路由
这个项目的Github仓库可以在这里找到.
🕐 第 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.prism
您的 Prisma 项目的主要配置文件(将包括您的数据模型)。
🔷 .env
用于定义数据库连接 URL 的 dotenv 文件。
要确保 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 映射到数据库,该数据库将生成并发送 SQL 语句以创建与您的数据模型对应的表。
由于您正在构建一个 playlist 应用程序,该应用程序的主要实体将是 artists 和 songs。
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 Client 并熟悉可以使用它发送的查询。在接下来的步骤中为您的 REST API 实现路由之前,您将首先在一个简单的可执行脚本中探索一些 Prisma Client 查询。
首先,通过打开终端并安装 Prisma Client npm 包,在项目中安装 Prisma Client:
$ npm install @prisma/client
进入全屏模式 退出全屏模式
接下来,创建一个名为 src 的新目录,该目录将包含您的源文件,并在新目录中创建一个 TypeScript 文件:
$ nano src/main.ts
进入全屏模式 退出全屏模式
所有 Prisma Client 查询都返回承诺,您可以在代码中_await_。这需要您在 async 函数内发送查询。
添加以下样板,其中包含在脚本中执行的异步函数:
// 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 的 async 函数,接下来您将在其中添加您的 Prisma 客户端查询。
🔷 您调用 main 函数,同时捕获任何潜在的异常并确保 Prisma Client 通过调用 prisma.disconnect() 关闭任何打开的数据库连接。
使用 main 函数,您可以开始将 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:创建一个新的用户记录。请注意,您实际上是在使用嵌套写入,这意味着您在同一个查询中同时创建了 Artist 和 Song 记录。
-
findMany:从数据库中读取所有现有的艺术家记录。您提供了 include 选项,该选项额外加载每个 Artist 记录的相关歌曲记录。现在使用以下命令运行脚本:
$ 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 Client 从数据库中检索。
继续并使用以下命令安装 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() 中间件以确保 Express 可以正确处理 JSON 数据。
-
您通过在 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 获取特定歌曲。
3
邮政
/艺术家
创建一个新的艺术家。
4
邮政
/歌曲
创作(或作曲)一首新歌(未发行)
5
放
/歌曲/发行/:id
将歌曲的已发布字段设置为 true。
6
删除
/歌曲/:身份证
按数据库记录 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
进入全屏模式 退出全屏模式
[
](#test-the-api-routes)
测试 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
- 将歌曲的已发布字段设置为真。
curl -X PUT http://localhost:3000/song/release/2
- 根据歌曲的数据库记录 ID 删除歌曲。
curl -X DELETE http://localhost:3000/song/1
7.重新查询播放列表
curl http://localhost:3000/playlist
结论
在本课中,您创建了一个 REST API 服务器,其中包含许多不同的路由来创建、读取、更新和删除示例播放列表后端应用程序的艺术家和歌曲数据。在 API 路由内部,您正在使用 Prisma Client 将相应的查询发送到您的 postgres 数据库。
在我们的下一课中,您将学习如何使用 Node、Prisma 和 Postgres 构建 GraphQL API。
延伸阅读
[1] 下一课了解更多关于如何如何使用 Node、Prisma 和 Postgres 构建 GraphQL API
[2] 棱镜组件
快乐阅读和编码
💻 📓 💝 📕 💓 📗 💖 📘 💗 📙 💜 📔 📒 🎊 📚 📖 💙 🎁 🎉
更多推荐
所有评论(0)