如何使用 Node.js、Prisma 和 PostgreSQL 通过 5 个步骤构建 CRUD RESTful API 💻 📓 💝 📕 💓 📗 💖 📘 💗 📙 💜 📔 📒 🎊 📚 📖 💙 🎁 🎉

2025-06-10

如何使用 Node.js、Prisma 和 PostgreSQL 通过 5 个步骤构建 CRUD RESTful API

💻📓💝📕💓📗💖📘💗📙💜📔📒🎊📚📖💙🎁🎉

介绍

对象关系映射 (ORM) 是一个框架,它在数据库上提供抽象层,以便与来自不兼容数据源的数据进行交互并管理查询。在本基础课程中,我们将学习如何使用 Node.js、Prisma 和 PostgreSQL 数据库的 Docker 实例构建后端。

Prisma是一个用Rust编写的NodejsTypeScript开源 ORM 。它在 Sequelize、Bookshelf、Waterline、Objection 和 TypeORM 等众多 JavaScript ORM 中脱颖而出。它包含 3 个主要工具:

🔷 Prisma Client:自动生成且类型安全的数据库客户端
🔷 Prisma Migrate:声明式数据建模和可定制的迁移
🔷 Prisma Studio:用于查看和编辑数据库中数据的 GUI。

Prisma工具

这些工具旨在提高应用程序开发人员在数据库工作流程中的工作效率。

Prisma 目前支持 PostgreSQL、MySQL、SQLite、SQL Server(预览版)和 MongoDB(预览版)。

先决条件

要练习本课,您需要具备以下条件:

内容

  • 第 1 步 - 创建 TypeScript 项目
  • 第2步 - 使用PostgreSQL设置Prisma
  • 第 3 步 - 定义数据模型并创建数据库表
  • 第 4 步 - 在纯脚本中探索 Prisma 客户端查询
  • 第5步 - 实现您的第一个REST API路由

该项目的 Github 存储库可以在这里找到

🕐 第 1 步 — 创建 TypeScript 项目

在此步骤中,您将使用 npm 设置一个简单的 TypeScript 项目。该项目将作为您在本课程中构建的 REST API 的基础。

首先,让我们为项目创建一个新目录:

$    mkdir playlist
Enter fullscreen mode Exit fullscreen mode

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

$    cd playlist
$    npm init -y
Enter fullscreen mode Exit fullscreen mode

您将得到类似这样的结果:

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"
}
Enter fullscreen mode Exit fullscreen mode

接下来,通过执行以下命令在您的项目中设置 TypeScript:

$    npm install typescript ts-node @types/node -D
Enter fullscreen mode Exit fullscreen mode

这将在您的项目中安装三个包作为开发依赖项:

🔷 typescriptTypeScript 工具链。🔷 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
  }
}
Enter fullscreen mode Exit fullscreen mode

🕐第 2 步 - 使用 PostgreSQL 设置 Prisma

在此步骤中,您将安装 Prisma CLI,创建初始 Prisma 模式文件,并使用 Docker 设置 PostgreSQL 并将 Prisma 连接到它。Prisma 模式是 Prisma 设置的主要配置文件,包含您的数据库模式。

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

$    npm install prisma -D
Enter fullscreen mode Exit fullscreen mode

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

$    nano docker-compose.yml
Enter fullscreen mode Exit fullscreen mode

现在将以下代码添加到新创建的文件中:
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:
Enter fullscreen mode Exit fullscreen mode

此 Docker Compose 文件配置了一个 PostgreSQL 数据库,可通过 Docker 容器的 5432 端口访问。另请注意,数据库凭据当前设置为africa(用户名)和village_people(用户密码)。您可以根据需要调整这些凭据。保存并退出文件。

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

$    docker-compose up -d
Enter fullscreen mode Exit fullscreen mode

嘿,这可能需要一段时间,因为需要拉取并启动 docker 镜像,除非你之前运行过它。现在就喝杯茶☕️。完成后,运行:

$    docker ps 
Enter fullscreen mode Exit fullscreen mode

该命令的输出将类似于以下内容:


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

Enter fullscreen mode Exit fullscreen mode

数据库服务器运行后,您现在可以创建 Prisma 设置。从 Prisma CLI 运行以下命令:

$    npx prisma init
Enter fullscreen mode Exit fullscreen mode

这将打印以下输出:

✔ Your Prisma schema was created at prisma/schema.prisma
  You can now open it in your favorite editor.
Enter fullscreen mode Exit fullscreen mode

运行该命令后,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
Enter fullscreen mode Exit fullscreen mode

🕐 第 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?
}

Enter fullscreen mode Exit fullscreen mode

您正在定义两个模型,分别名为ArtistSong。每个模型都包含多个字段,用于表示模型的属性。这些模型将映射到数据库表;字段代表各个列。

还要注意,这两个模型之间存在一对多关系,由ArtistSong上的songssinger关系字段指定。这意味着一位艺术家可以与多首歌曲相关联。

有了这些模型,您现在可以使用 Prisma Migrate 在数据库中创建相应的表。在终端中运行以下命令:

$    npx prisma migrate dev --name "init"
Enter fullscreen mode Exit fullscreen mode

此命令会在文件系统上创建一个新的 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.
Enter fullscreen mode Exit fullscreen mode

/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;
Enter fullscreen mode Exit fullscreen mode

🕐 第 4 步 — 在纯脚本中探索 Prisma 客户端查询

Prisma Client 是一个自动生成且类型安全的查询构建器,您可以使用它以编程方式从 Node.js 或 TypeScript 应用程序读取和写入数据库中的数据。您可以使用它在 REST API 路由中访问数据库,从而取代传统的 ORM、纯 SQL 查询、自定义数据访问层或任何其他与数据库通信的方法。

在此步骤中,您将安装 Prisma 客户端并熟悉可以使用它发送的查询。在后续步骤中实现 REST API 的路由之前,您将首先在一个普通的可执行脚本中探索一些 Prisma 客户端查询。

首先,打开终端并安装 Prisma Client npm 包,继续在您的项目中安装 Prisma Client:

$    npm install @prisma/client
Enter fullscreen mode Exit fullscreen mode

接下来,创建一个名为src的新目录,其中将包含您的源文件,并在新目录中创建一个 TypeScript 文件:

$    nano src/main.ts
Enter fullscreen mode Exit fullscreen mode

所有 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())
Enter fullscreen mode Exit fullscreen mode

以下是样板的简要分解:

🔷从先前安装的@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"
Enter fullscreen mode Exit fullscreen mode

在此代码中,您使用了两个 Prisma Client 查询:

  1. create:创建一条新的用户记录。请注意,您实际上使用了嵌套写入,这意味着您在同一个查询中同时创建了艺术家记录和歌曲记录。
  2. 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
      }
    ]
  }
]
Enter fullscreen mode Exit fullscreen mode

或者,您可以通过运行以下命令使用Prisma Studio操作 Postgres 数据库中的记录

$    npx prisma studio
Enter fullscreen mode Exit fullscreen mode

输出:

Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Prisma Studio is up on http://localhost:5555
Enter fullscreen mode Exit fullscreen mode

在浏览器中访问http://localhost:5555并浏览你的模型。然后按Ctrl + C在终端停止Prisma Studio,或者在同一个播放列表项目目录中打开一个新终端。

🕐 第 5 步 — 实现您的第一个 REST API 路由

在此步骤中,您将在应用程序中安装 Express。Express 是一个流行的 Node.js Web 框架,您将使用它来实现此项目中的 REST API 路由。您将实现的第一个路由将允许您使用 GET 请求从 API 中获取所有艺术家。艺术家数据将使用 Prisma 客户端从数据库中检索。

继续使用以下命令安装 Express:

$    npm install express
Enter fullscreen mode Exit fullscreen mode

由于你使用的是 TypeScript,因此你还需要安装相应的类型作为开发依赖项。运行以下命令即可执行此操作:

$    npm install @types/express -D
Enter fullscreen mode Exit fullscreen mode

在 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'),
)
Enter fullscreen mode Exit fullscreen mode

以下是代码的简要分解:

  1. 从相应的 npm 包中导入PrismaClient和 express。

  2. 您可以通过调用构造函数来实例化 PrismaClient 并获得一个名为prisma的实例。

  3. 您可以通过调用 express() 来创建 Express 应用程序。

  4. 添加express.json()中间件以确保 JSON 数据能够被 Express 正确处理。

  5. 通过在app.useapp.listen调用之间添加 api 端点来实现您的第一条路由

  6. 您在端口 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"}
Enter fullscreen mode Exit fullscreen mode

第 6 步 - 实现剩余的 REST API 路由

在此步骤中,您将为博客应用程序实现其余的 REST API 路由。最后,您的 Web 服务器将处理各种GETPOSTPUTDELETE请求。

以下是您将实施的不同路线的概述:

序号 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'),
)

Enter fullscreen mode Exit fullscreen mode

您可以使用CTRL + C停止服务器来测试新路由。然后,使用以下命令重新启动服务器:

$     npx ts-node src/index.ts
Enter fullscreen mode Exit fullscreen mode




测试 API 路由

  1. 获取所有已发布的歌曲。

    $ curl http://localhost:3000/playlist

  2. 通过 ID 获取特定歌曲。

    $ curl http://localhost:3000/song/1

  3. 創造了一位新的藝術家。

    curl -X POST -H "Content-Type: application/json" -d '{"name":"Nditah Sam", "email":"contact@telixia.com"}' http://localhost:3000/artist

  4. 创作(或谱写)一首新歌(未发行)

    curl -X POST -H "Content-Type: application/json" -d '{"title":"Take my hand", "singerEmail":"contact@telixia.com"}' http://localhost:3000/song

  5. 将歌曲的已发布字段设置为 true。

    curl -X PUT http://localhost:3000/song/release/2

  6. 根据数据库记录 ID 删除歌曲。

    curl -X DELETE http://localhost:3000/song/1

  7. 再次重新查询播放列表

    curl http://localhost:3000/playlist

结论

在本课中,你创建了一个 REST API 服务器,其中包含多个不同的路由,用于为示例播放列表后端应用程序创建、读取、更新和删除艺术家和歌曲数据。在 API 路由内部,你将使用 Prisma 客户端将相应的查询发送到你的 postgres 数据库。

在下一课中,您将学习如何使用 Node、Prisma 和 Postgres 构建 GraphQL API

进一步阅读

[1]下一课将学习如何使用 Node、Prisma 和 Postgres 构建 GraphQL API

[2] Prisma 组件

快乐阅读和编码

💻📓💝📕💓📗💖📘💗📙💜📔📒🎊📚📖💙🎁🎉

鏂囩珷鏉ユ簮锛�https://dev.to/nditah/how-to-build-a-rest-api-with-node-prisma-and-postgresql-429a
PREV
如何通过四个简单的步骤扼杀你的动力
NEXT
交易日志事件解码:使钱包活动易于理解