Reading Time: 17 minutes

Xây dựng REST API với Prisma và PostgreSQL

Prisma là một ORM mã nguồn mở dành cho Node.js và TypeScript. Nó bao gồm ba công cụ chính:

  • Prisma Client: Trình xây dựng truy vấn được tự động tạo và an toàn theo kiểu dữ liệu.
  • Prisma Migrate: Hệ thống thiết kế và di trú dữ liệu mạnh mẽ.
  • Prisma Studio: Giao diện đồ họa (GUI) để xem và chỉnh sửa dữ liệu trong cơ sở dữ liệu.

Những công cụ này nhằm nâng cao hiệu suất làm việc với cơ sở dữ liệu cho các lập trình viên ứng dụng. Một trong những lợi ích lớn nhất của Prisma là mức độ trừu tượng mà nó cung cấp: thay vì phải viết các truy vấn SQL phức tạp hoặc tự xử lý các di trú của lược đồ, lập trình viên có thể làm việc với dữ liệu một cách trực quan hơn.

Trong hướng dẫn này, bạn sẽ xây dựng một REST API cho một ứng dụng blog nhỏ bằng TypeScript, sử dụng Prisma và cơ sở dữ liệu PostgreSQL. Bạn sẽ cài đặt cơ sở dữ liệu PostgreSQL cục bộ bằng Docker và triển khai các tuyến REST API bằng Express. Sau khi hoàn thành, bạn sẽ có một máy chủ web chạy trên máy của bạn, có thể phản hồi các yêu cầu HTTP và thao tác đọc/ghi dữ liệu vào cơ sở dữ liệu.

Yêu cầu trước khi bắt đầu

Hướng dẫn này giả định rằng bạn đã có:

  • Node.js phiên bản 14 trở lên được cài đặt trên máy.
  • Docker đã được cài đặt (để chạy cơ sở dữ liệu PostgreSQL). Có thể cài Docker trên macOS hoặc Windows từ trang chính thức của Docker.
  • Biết cơ bản về TypeScript và REST APIs sẽ có ích nhưng không bắt buộc để theo dõi hướng dẫn này.

Bước 1: Tạo dự án TypeScript

Trong bước này, bạn sẽ thiết lập một dự án TypeScript đơn giản bằng npm. Đây sẽ là nền tảng cho REST API mà bạn sẽ xây dựng.

Đầu tiên, hãy tạo một thư mục mới cho dự án của bạn:

$ mkdir my-blog

Tiếp theo, hãy điều hướng vào thư mục vừa tạo và khởi tạo một dự án npm rỗng. Lưu ý rằng tùy chọn -y ở đây có nghĩa là bạn sẽ bỏ qua các câu hỏi tương tác của lệnh. Nếu muốn thực hiện từng bước thủ công, hãy bỏ -y khỏi lệnh:

$ cd my-blog
$ npm init -y

Bạn sẽ nhận được đầu ra tương tự như sau với các giá trị mặc định đã được thiết lập:

Output
Wrote to /.../my-blog/package.json:

{
  "name": "my-blog",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \\"Error: no test specified\\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

Lệnh này sẽ tạo một tệp package.json tối giản mà bạn sẽ sử dụng như một tệp cấu hình cho dự án npm của mình. Bây giờ bạn đã sẵn sàng để cấu hình TypeScript cho dự án.

Chạy lệnh sau để thiết lập TypeScript cơ bản:

$ npm install typescript ts-node @types/node --save-dev

Lệnh này sẽ cài đặt ba gói như các phụ thuộc dành cho môi trường phát triển trong dự án của bạn:

  • typescript: Bộ công cụ TypeScript.
  • ts-node: Gói cho phép chạy ứng dụng TypeScript mà không cần biên dịch trước sang JavaScript.
  • @types/node: Các định nghĩa kiểu TypeScript dành cho Node.js.

Việc cuối cùng cần làm là thêm tệp tsconfig.json để đảm bảo TypeScript được cấu hình đúng cho ứng dụng bạn sắp xây dựng.

Trước tiên, hãy chạy lệnh sau để tạo tệp cấu hình này:

$ nano tsconfig.json

Thêm đoạn mã JSON sau vào tệp:

{
  "compilerOptions": {
    "sourceMap": true,
    "outDir": "dist",
    "strict": true,
    "lib": ["esnext"],
    "esModuleInterop": true
  }
}

Lưu và thoát khỏi tệp.

Cấu hình này là thiết lập tiêu chuẩn và tối giản cho một dự án TypeScript. Nếu bạn muốn tìm hiểu thêm về từng thuộc tính trong tệp cấu hình, bạn có thể tham khảo tài liệu chính thức của TypeScript.

Bạn đã hoàn tất việc thiết lập dự án TypeScript cơ bản bằng npm. Tiếp theo, bạn sẽ thiết lập cơ sở dữ liệu PostgreSQL bằng Docker và kết nối Prisma với nó.

Bước 2: Thiết lập Prisma với PostgreSQL

Trong bước này, bạn sẽ cài đặt Prisma CLI, tạo tệp schema đầu tiên của Prisma, thiết lập PostgreSQL bằng Docker và kết nối Prisma với cơ sở dữ liệu đó.

Tệp Prisma schema là tệp cấu hình chính của Prisma và chứa mô hình cơ sở dữ liệu của bạn.

Hãy bắt đầu bằng cách cài đặt Prisma CLI với lệnh sau:

$ npm install prisma --save-dev

Bạn nên cài đặt Prisma CLI cục bộ trong từng dự án thay vì cài đặt toàn cục. Việc này giúp tránh xung đột phiên bản nếu bạn có nhiều dự án sử dụng Prisma trên cùng một máy.

Tiếp theo, bạn sẽ thiết lập cơ sở dữ liệu PostgreSQL bằng Docker. Tạo một tệp Docker Compose mới với lệnh sau:

$ nano docker-compose.yml

Bây giờ hãy thêm đoạn mã sau vào tệp vừa tạo:

version: '3.8'
services:
  postgres:
    image: postgres:10.3
    restart: always
    environment:
      - POSTGRES_USER=sammy
      - POSTGRES_PASSWORD=your_password
    volumes:
      - postgres:/var/lib/postgresql/data
    ports:
      - '5432:5432'
volumes:
  postgres:

Tệp Docker Compose này cấu hình một cơ sở dữ liệu PostgreSQL có thể được truy cập thông qua cổng 5432 của container Docker. Thông tin đăng nhập cơ sở dữ liệu hiện được đặt là sammy (tên người dùng) và your_password (mật khẩu). Bạn có thể điều chỉnh thông tin đăng nhập này thành tên người dùng và mật khẩu mà bạn muốn. Lưu và thoát khỏi tệp.

Với cấu hình này, hãy khởi chạy máy chủ cơ sở dữ liệu PostgreSQL bằng lệnh sau:

$ docker-compose up -d

Kết quả đầu ra của lệnh này sẽ tương tự như sau:

Output
Pulling postgres (postgres:10.3)...
10.3: Pulling from library/postgres
f2aa67a397c4: Pull complete
6de83ca23e55: Pull complete
. . .
Status: Downloaded newer image for postgres:10.3
Creating my-blog_postgres_1 ... done

Bạn có thể xác minh rằng máy chủ cơ sở dữ liệu đang chạy bằng lệnh sau:

$ docker ps

Lệnh này sẽ in ra nội dung tương tự như sau:

Output
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
8547f8e007ba        postgres:10.3       "docker-entrypoint.s…"   3 seconds ago       Up 2 seconds        0.0.0.0:5432->5432/tcp   my-blog_postgres_1

Khi máy chủ cơ sở dữ liệu đã chạy, bạn có thể tạo thiết lập Prisma của mình. Chạy lệnh sau từ Prisma CLI:

$ npx prisma init

Lệnh này sẽ in ra nội dung như sau:

Output
✔ Your Prisma schema was created at prisma/schema.prisma.
  You can now open it in your favorite editor.

Bạn nên thêm tiền tố npx trước mọi lệnh gọi Prisma CLI để đảm bảo rằng phiên bản Prisma cục bộ của bạn đang được sử dụng.

Sau khi bạn chạy lệnh, Prisma CLI sẽ tạo một thư mục mới có tên là prisma trong dự án của bạn. Bên trong đó, bạn sẽ tìm thấy một tệp schema.prisma, đây là tệp cấu hình chính cho dự án Prisma của bạn (bao gồm mô hình dữ liệu). Lệnh này cũng thêm một tệp dotenv có tên .env vào thư mục gốc, nơi bạn sẽ định nghĩa URL kết nối cơ sở dữ liệu của mình.

Để đảm bảo Prisma biết vị trí của cơ sở dữ liệu, hãy mở tệp .env và điều chỉnh biến môi trường DATABASE_URL.

Trước tiên, hãy mở tệp .env:

$ nano .env

Bây giờ bạn có thể cập nhật biến môi trường như sau:

DATABASE_URL="postgresql://sammy:your_password@localhost:5432/my-blog?schema=public"

Hãy đảm bảo bạn thay đổi thông tin đăng nhập cơ sở dữ liệu thành các thông tin đã chỉ định trong tệp Docker Compose. Để tìm hiểu thêm về định dạng của chuỗi kết nối, hãy truy cập tài liệu Prisma.

Sau khi hoàn tất, hãy lưu và thoát khỏi tệp.

Ở bước này, bạn đã thiết lập cơ sở dữ liệu PostgreSQL bằng Docker, cài đặt Prisma CLI và kết nối Prisma với cơ sở dữ liệu thông qua biến môi trường. Trong phần tiếp theo, bạn sẽ định nghĩa mô hình dữ liệu và tạo các bảng trong cơ sở dữ liệu.

Bước 3: Định nghĩa mô hình dữ liệu và tạo bảng cơ sở dữ liệu

Trong bước này, bạn sẽ định nghĩa mô hình dữ liệu trong tệp schema.prisma. Mô hình dữ liệu này sau đó sẽ được ánh xạ sang cơ sở dữ liệu bằng Prisma Migrate, công cụ sẽ tạo và gửi các câu lệnh SQL để tạo các bảng tương ứng với mô hình dữ liệu. Vì bạn đang xây dựng một ứng dụng blog, các thực thể chính của ứng dụng sẽ là người dùng (users) và bài viết (posts).

Prisma sử dụng ngôn ngữ mô hình hóa dữ liệu riêng của nó để định nghĩa cấu trúc dữ liệu ứng dụng.

Đầu tiên, mở tệp schema.prisma của bạn bằng lệnh sau:

$ nano prisma/schema.prisma

Bây giờ, hãy thêm các định nghĩa mô hình sau vào tệp schema.prisma. Bạn có thể đặt các mô hình này ở cuối tệp, ngay sau khối generator client:

. . .
model User {
  id    Int     @default(autoincrement()) @id
  email String  @unique
  name  String?
  posts Post[]
}

model Post {
  id        Int     @default(autoincrement()) @id
  title     String
  content   String?
  published Boolean @default(false)
  author    User?   @relation(fields: [authorId], references: [id])
  authorId  Int?
}

Bạn đang định nghĩa hai mô hình: UserPost. Mỗi mô hình này có một số trường đại diện cho các thuộc tính của mô hình. Các mô hình này sẽ được ánh xạ thành các bảng trong cơ sở dữ liệu; các trường tương ứng với các cột riêng lẻ.

Giữa hai mô hình có một quan hệ một-nhiều, được chỉ định bởi các trường quan hệ postsauthor trong UserPost. Điều này có nghĩa là một người dùng có thể liên kết với nhiều bài viết.

Hãy lưu và thoát tệp.

Với các mô hình này đã được thiết lập, giờ bạn có thể tạo các bảng tương ứng trong cơ sở dữ liệu bằng cách sử dụng Prisma Migrate. Trong terminal của bạn, hãy chạy lệnh sau:

$ npx prisma migrate dev --name init

Lệnh này sẽ tạo một bản ghi migration SQL mới trên hệ thống tệp của bạn và áp dụng nó vào cơ sở dữ liệu. Tùy chọn --name init được thêm vào lệnh dùng để đặt tên cho migration và sẽ được sử dụng làm tên thư mục chứa migration được tạo ra.

Kết quả đầu ra của lệnh sẽ tương tự như sau:

Output
Environment variables loaded from .env
Prisma schema loaded from prisma/schema.prisma
Datasource "db": PostgreSQL database "my-blog", schema "public" at "localhost:5432"

PostgreSQL database my-blog created at localhost:5432

The following migration(s) have been created and applied from new schema changes:

migrations/
  └─ 20201209084626_init/
    └─ migration.sql

Running generate... (Use --skip-generate to skip the generators)

✔ Generated Prisma Client (2.13.0) to ./node_modules/@prisma/client in 75ms

Tệp migration SQL trong thư mục prisma/migrations/20201209084626_init/migration.sql chứa các câu lệnh sau đã được thực thi trên cơ sở dữ liệu (phần được đánh dấu trong tên tệp có thể khác nhau tùy vào thiết lập của bạn):

-- CreateTable
CREATE TABLE "User" (
"id" SERIAL,
    "email" TEXT NOT NULL,
    "name" TEXT,

    PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "Post" (
"id" SERIAL,
    "title" TEXT NOT NULL,
    "content" TEXT,
    "published" BOOLEAN NOT NULL DEFAULT false,
    "authorId" INTEGER,

    PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "User.email_unique" ON "User"("email");

-- AddForeignKey
ALTER TABLE "Post" ADD FOREIGN KEY("authorId")REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;

Bạn cũng có thể tùy chỉnh tệp migration SQL được tạo nếu bạn thêm tùy chọn --create-only vào lệnh prisma migrate dev; ví dụ, bạn có thể thiết lập trigger hoặc sử dụng các tính năng khác của cơ sở dữ liệu bên dưới.

Trong bước này, bạn đã định nghĩa mô hình dữ liệu trong tệp schema của Prisma và tạo các bảng tương ứng trong cơ sở dữ liệu bằng Prisma Migrate. Trong bước tiếp theo, bạn sẽ cài đặt Prisma Client vào dự án để có thể truy vấn cơ sở dữ liệu.

Bước 4: Khám phá các truy vấn với Prisma Client trong một script đơn giản

Prisma Client là một trình xây dựng truy vấn được tạo tự động, an toàn theo kiểu dữ liệu, cho phép bạn lập trình việc đọc và ghi dữ liệu từ cơ sở dữ liệu trong ứng dụng Node.js hoặc TypeScript. Bạn sẽ sử dụng nó để truy cập cơ sở dữ liệu trong các route của REST API, thay thế cho ORM truyền thống, truy vấn SQL thuần, lớp truy cập dữ liệu tự viết, hoặc bất kỳ phương thức giao tiếp nào khác với cơ sở dữ liệu.

Trong bước này, bạn sẽ cài đặt Prisma Client và làm quen với các truy vấn mà bạn có thể thực hiện với nó. Trước khi triển khai các route cho REST API ở các bước tiếp theo, bạn sẽ khám phá một số truy vấn của Prisma Client thông qua một script có thể chạy được.

Đầu tiên, cài đặt Prisma Client trong thư mục dự án bằng gói npm của Prisma Client:

$ npm install @prisma/client

Tiếp theo, tạo một thư mục mới có tên là src, thư mục này sẽ chứa các tệp mã nguồn của bạn:

$ mkdir src

Tạo một tệp TypeScript bên trong thư mục mới đó:

$ nano src/index.ts

Tất cả các truy vấn của Prisma Client đều trả về promise, vì vậy bạn cần await chúng trong mã của mình. Điều này yêu cầu bạn phải đặt truy vấn bên trong một hàm async.

Trong tệp src/index.ts, hãy thêm đoạn mã khởi tạo sau với một hàm async được thực thi trực tiếp trong script:

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())

Giải thích nhanh về đoạn code mẫu:

  • Bạn import PrismaClient từ gói @prisma/client npm đã được cài đặt trước đó.
  • Bạn khởi tạo một instance của PrismaClient và gán nó vào biến prisma.
  • Bạn định nghĩa một hàm async có tên là main, nơi bạn sẽ viết các truy vấn Prisma Client.
  • Bạn gọi hàm main, đồng thời xử lý các lỗi có thể xảy ra và đảm bảo rằng Prisma Client sẽ đóng kết nối với cơ sở dữ liệu thông qua prisma.$disconnect().

Với hàm main đã sẵn sàng, bạn có thể bắt đầu thêm các truy vấn của Prisma Client vào script. Hãy cập nhật index.ts để thêm các dòng được đánh dấu trong hàm main như sau:

import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function main() {
  const newUser = await prisma.user.create({
    data: {
      name: 'Alice',
      email: 'alice@prisma.io',
      posts: {
        create: {
          title: 'Hello World',
        },
      },
    },
  })
  console.log('Created new user: ', newUser)

  const allUsers = await prisma.user.findMany({
    include: { posts: true },
  })
  console.log('All users: ')
  console.dir(allUsers, { depth: null })
}

main()
  .catch((e) => console.error(e))
  .finally(async () => await prisma.$disconnect())

Trong đoạn code này, bạn đang sử dụng hai truy vấn của Prisma Client:

  • create: Tạo một bản ghi mới trong bảng User. Bạn sử dụng một truy vấn ghi lồng nhau (nested write query) để tạo đồng thời cả bản ghi UserPost trong cùng một truy vấn.
  • findMany: Đọc tất cả các bản ghi hiện có trong bảng User từ cơ sở dữ liệu. Bạn sử dụng tùy chọn include để đồng thời tải thêm các bản ghi Post có liên quan đến mỗi bản ghi User.

Lưu và đóng tệp.

Bây giờ, hãy chạy script bằng lệnh sau:

$ npx ts-node src/index.ts

Bạn sẽ nhận được kết quả đầu ra như sau trong terminal:

Output
Created new user:  { id: 1, email: 'alice@prisma.io', name: 'Alice' }
[
  {
    id: 1,
    email: 'alice@prisma.io',
    name: 'Alice',
    posts: [
      {
        id: 1,
        title: 'Hello World',
        content: null,
        published: false,
        authorId: 1
      }
    ]
  }

Ghi chú: Nếu bạn đang sử dụng giao diện đồ họa cơ sở dữ liệu (GUI), bạn có thể xác minh dữ liệu đã được tạo bằng cách kiểm tra các bảng UserPost. Ngoài ra, bạn cũng có thể khám phá dữ liệu bằng Prisma Studio bằng cách chạy npx prisma studio.

Bạn vừa sử dụng Prisma Client để đọc và ghi dữ liệu trong cơ sở dữ liệu. Trong các bước tiếp theo, bạn sẽ triển khai các route cho một REST API mẫu.

Bước 5: Triển khai route đầu tiên cho REST API

Trong bước này, bạn sẽ cài đặt Express vào ứng dụng của mình. Express là một framework web phổ biến cho Node.js, sẽ được dùng để triển khai các route REST API. Route đầu tiên bạn sẽ xây dựng cho phép truy xuất tất cả người dùng thông qua yêu cầu GET. Dữ liệu người dùng sẽ được lấy từ cơ sở dữ liệu thông qua Prisma Client.

Cài đặt Express bằng lệnh sau:

$ npm install express

Vì bạn đang sử dụng TypeScript, bạn cũng nên cài đặt các kiểu định nghĩa (type definitions) tương ứng dưới dạng phụ thuộc phát triển. Chạy lệnh sau:

$ npm install @types/express --save-dev

Sau khi đã cài đặt các thư viện cần thiết, bạn có thể bắt đầu thiết lập ứng dụng Express của mình.

Mở lại tệp mã nguồn chính:

$ nano src/index.ts

Bây giờ hãy xóa toàn bộ nội dung trong index.ts và thay thế bằng đoạn mã sau để khởi động REST API:

import { PrismaClient } from '@prisma/client'
import express from 'express'

const prisma = new PrismaClient()
const app = express()

app.use(express.json())

// ... your REST API routes will go here

app.listen(3000, () =>
  console.log('REST API server ready at: <http://localhost:3000>'),
)

Dưới đây là phần tóm tắt nhanh đoạn mã:

  • Bạn import PrismaClientexpress từ các gói npm tương ứng.
  • Bạn khởi tạo PrismaClient bằng cách gọi hàm tạo và tạo một instance có tên là prisma.
  • Bạn tạo ứng dụng Express bằng cách gọi express().
  • Bạn thêm middleware express.json() để Express có thể xử lý dữ liệu JSON một cách chính xác.
  • Bạn khởi động server trên cổng 3000.

Bây giờ bạn có thể triển khai route đầu tiên của mình. Giữa lệnh gọi app.useapp.listen, hãy thêm các dòng được tô sáng để tạo một route app.get:

. . .
app.use(express.json())

app.get('/users', async (req, res) => {
  const users = await prisma.user.findMany()
  res.json(users)
})

app.listen(3000, () =>
console.log('REST API server ready at: <http://localhost:3000>'),
)

Sau khi thêm xong, hãy lưu và thoát khỏi tệp. Sau đó, khởi động máy chủ web cục bộ của bạn bằng lệnh sau:

$ npx ts-node src/index.ts

Bạn sẽ nhận được kết quả đầu ra như sau:

Output
REST API server ready at: <http://localhost:3000>

Để truy cập route /users, bạn có thể mở trình duyệt và truy cập địa chỉ http://localhost:3000/users hoặc sử dụng bất kỳ công cụ HTTP client nào khác.

Trong hướng dẫn này, bạn sẽ kiểm thử tất cả các route của REST API bằng công cụ curl, một HTTP client hoạt động trên dòng lệnh.

Lưu ý: Nếu bạn muốn sử dụng công cụ có giao diện đồ họa (GUI), bạn có thể chọn các lựa chọn như Hoppscotch hoặc Postman.

Để kiểm thử route của bạn, hãy mở một cửa sổ hoặc tab terminal mới (để máy chủ web cục bộ vẫn tiếp tục chạy) và thực thi lệnh sau:

$ curl <http://localhost:3000/users>

Bạn sẽ nhận được dữ liệu người dùng (User) đã tạo ở bước trước:

Output
[{"id":1,"email":"alice@prisma.io","name":"Alice"}]

Lần này, mảng posts sẽ không được trả về vì bạn không truyền tùy chọn include vào lệnh gọi findMany trong phần xử lý route /users.

Bạn vừa hoàn thành route REST API đầu tiên tại địa chỉ /users. Trong bước tiếp theo, bạn sẽ triển khai các route REST API còn lại để mở rộng chức năng cho API của mình.

Bước 6: Triển khai các Route REST API còn lại

Ở bước này, bạn sẽ triển khai các route REST API còn lại cho ứng dụng blog của mình. Sau khi hoàn tất, máy chủ web của bạn sẽ hỗ trợ các loại yêu cầu: GET, POST, PUTDELETE.

Các route bạn sẽ triển khai bao gồm những tùy chọn sau:

HTTP Method Route Description
GET /feed Lấy tất cả các bài viết đã được xuất bản.
GET /post/:id Lấy một bài viết cụ thể theo ID.
POST /user Tạo một người dùng mới.
POST /post Tạo một bài viết mới ở trạng tái nháp.
PUT /post/publish/:id Cập nhật bài viết để trường published chuyển thành true.
DELETE post/:id Xóa một bài viết theo ID.

Bạn sẽ triển khai hai route GET còn lại trước.

Bạn có thể dừng máy chủ bằng cách nhấn tổ hợp phím CTRL + C ****trên bàn phím. Sau đó, mở tệp index.ts để chỉnh sửa:

$ nano src/index.ts

Tiếp theo, hãy thêm các dòng được tô sáng ngay sau phần cài đặt route app.get:

. . .

app.get('/feed', async (req, res) => {
  const posts = await prisma.post.findMany({
    where: { published: true },
    include: { author: true }
  })
  res.json(posts)
})

app.get(`/post/:id`, async (req, res) => {
  const { id } = req.params
  const post = await prisma.post.findUnique({
    where: { id: Number(id) },
  })
  res.json(post)
})

app.listen(3000, () =>
  console.log('REST API server ready at: <http://localhost:3000>'),
)

Đoạn mã này triển khai các route API cho hai yêu cầu GET:

  • /feed: Trả về danh sách các bài viết đã được xuất bản.
  • /post/:id: Trả về một bài viết cụ thể dựa trên ID.

Prisma Client được sử dụng trong cả hai phần cài đặt.

Trong route /feed, truy vấn gửi đi thông qua Prisma Client sẽ lọc tất cả các bản ghi Post có cột published bằng true. Ngoài ra, truy vấn cũng sử dụng thuộc tính include để lấy thêm thông tin tác giả (author) liên quan cho từng bài viết được trả về.

Trong route /post/:id, bạn truyền ID lấy từ đường dẫn URL để truy xuất một bản ghi Post cụ thể từ cơ sở dữ liệu.

Hãy lưu và thoát khỏi tệp. Sau đó, khởi động lại máy chủ bằng lệnh sau:

$ npx ts-node src/index.ts

Để kiểm thử route /feed, bạn có thể sử dụng lệnh curl sau trong cửa sổ terminal thứ hai:

$ curl <http://localhost:3000/feed>

Vì hiện tại chưa có bài viết nào được xuất bản, nên phản hồi sẽ là một mảng trống:

Output
[]

Để kiểm thử route /post/:id, bạn có thể sử dụng lệnh curl sau:

$ curl <http://localhost:3000/post/1>

Lệnh này sẽ trả về bài viết mà bạn đã tạo trước đó:

Output
{"id":1,"title":"Hello World","content":null,"published":false,"authorId":1}

Tiếp theo, bạn sẽ triển khai hai route POST. Trong cửa sổ terminal ban đầu, hãy dừng máy chủ bằng tổ hợp phím CTRL+C, sau đó mở lại tệp index.ts để chỉnh sửa:

$ nano src/index.ts

Thêm các dòng mã được tô sáng vào index.ts, sau phần cài đặt của ba route GET:

. . .

app.post(`/user`, async (req, res) => {
  const result = await prisma.user.create({
    data: { ...req.body },
  })
  res.json(result)
})

app.post(`/post`, async (req, res) => {
  const { title, content, authorEmail } = req.body
  const result = await prisma.post.create({
    data: {
      title,
      content,
      published: false,
      author: { connect: { email: authorEmail } },
    },
  })
  res.json(result)
})

app.listen(3000, () =>
  console.log('REST API server ready at: <http://localhost:3000>'),
)

Đoạn mã này triển khai các route API cho hai yêu cầu POST:

  • /user: Tạo một người dùng mới trong cơ sở dữ liệu.
  • /post: Tạo một bài viết mới trong cơ sở dữ liệu.

Cũng như trước, Prisma Client được sử dụng trong cả hai phần cài đặt. Trong route /user, bạn truyền trực tiếp các giá trị từ phần thân (body) của yêu cầu HTTP vào truy vấn create của Prisma Client.

Route /post phức tạp hơn một chút. Bạn không thể truyền trực tiếp các giá trị từ body của yêu cầu HTTP, mà cần trích xuất thủ công chúng trước, sau đó mới truyền vào truy vấn của Prisma Client. Lý do là cấu trúc của JSON trong phần thân yêu cầu không khớp với cấu trúc mà Prisma Client mong đợi, vì vậy bạn cần tự tạo lại cấu trúc dữ liệu đúng định dạng.

Sau khi hoàn tất, lưu và thoát khỏi tệp.

Sau đó, khởi động lại máy chủ bằng lệnh:

$ npx ts-node src/index.ts

Để tạo một người dùng mới thông qua route /user, bạn có thể gửi yêu cầu POST bằng lệnh curl sau:

$ curl -X POST -H "Content-Type: application/json" -d '{"name":"Bob", "email":"bob@prisma.io"}' <http://localhost:3000/user>

Lệnh này sẽ tạo một người dùng mới trong cơ sở dữ liệu và in ra kết quả như sau:

Output
{"id":2,"email":"bob@prisma.io","name":"Bob"}

Để tạo một bài viết mới thông qua route /post, bạn có thể gửi yêu cầu POST bằng lệnh curl sau:

$ curl -X POST -H "Content-Type: application/json" -d '{"title":"I am Bob", "authorEmail":"bob@prisma.io"}' <http://localhost:3000/post>

Lệnh này sẽ tạo một bài viết mới trong cơ sở dữ liệu và liên kết nó với người dùng có email là bob@prisma.io. Sau đó, kết quả sẽ được in ra như sau:

Output
{"id":2,"title":"I am Bob","content":null,"published":false,"authorId":2}

Cuối cùng, bạn sẽ triển khai các route PUTDELETE. Hãy dừng máy chủ đang chạy, sau đó mở tệp index.ts bằng lệnh sau:

$ nano src/index.ts

Tiếp theo, sau phần triển khai của hai route POST, hãy thêm các dòng mã được tô sáng:

. . .

app.put('/post/publish/:id', async (req, res) => {
  const { id } = req.params
  const post = await prisma.post.update({
    where: { id: Number(id) },
    data: { published: true },
  })
  res.json(post)
})

app.delete(`/post/:id`, async (req, res) => {
  const { id } = req.params
  const post = await prisma.post.delete({
    where: { id: Number(id) },
  })
  res.json(post)
})

app.listen(3000, () =>
  console.log('REST API server ready at: <http://localhost:3000>'),
)

Đoạn mã này triển khai các route API cho một yêu cầu PUT và một yêu cầu DELETE:

  • /post/publish/:id (PUT): Xuất bản một bài viết dựa trên ID.
  • /post/:id (DELETE): Xoá một bài viết dựa trên ID.

Một lần nữa, Prisma Client được sử dụng trong cả hai phần cài đặt.

Trong route /post/publish/:id, ID của bài viết cần xuất bản được lấy từ URL và truyền vào truy vấn update của Prisma Client. Tương tự, trong route /post/:id, để xoá một bài viết khỏi cơ sở dữ liệu, ID cũng được lấy từ URL và truyền vào truy vấn delete của Prisma Client.

Sau khi hoàn tất, lưu và thoát khỏi tệp.

Khởi động lại máy chủ bằng lệnh:

$ npx ts-node src/index.ts

Bạn có thể kiểm thử route PUT bằng lệnh curl sau:

$ curl -X PUT <http://localhost:3000/post/publish/2>

Lệnh này sẽ xuất bản bài viết có ID là 2. Nếu bạn gửi lại yêu cầu GET đến route /feed, bài viết này giờ sẽ được hiển thị trong kết quả phản hồi.

Cuối cùng, bạn có thể kiểm thử route DELETE bằng lệnh curl sau:

$ curl -X DELETE <http://localhost:3000/post/1>

Lệnh này sẽ xoá bài viết có ID là 1. Để xác nhận rằng bài viết này đã bị xoá, bạn có thể gửi lại yêu cầu GET đến route /post/1 với lệnh curl sau:

$ curl <http://localhost:3000/post/1>

Trong bước này, bạn đã triển khai toàn bộ các route REST API còn lại cho ứng dụng blog của mình. Hiện tại, API đã có thể xử lý các yêu cầu GET, POST, PUTDELETE, đồng thời cho phép đọc và ghi dữ liệu vào cơ sở dữ liệu.

Kết luận

Trong bài viết này, bạn đã xây dựng một máy chủ REST API với nhiều route khác nhau để tạo, đọc, cập nhật và xoá dữ liệu người dùng và bài viết cho một ứng dụng blog mẫu. Bên trong các route API, bạn sử dụng Prisma Client để thực hiện các truy vấn tương ứng đến cơ sở dữ liệu.

Các bước tiếp theo, bạn có thể:

  • Triển khai thêm các route API mới.
  • Mở rộng schema cơ sở dữ liệu bằng Prisma Migrate.
  • Truy cập tài liệu Prisma để tìm hiểu sâu hơn về các tính năng khác.
  • Khám phá các dự án mẫu sẵn sàng sử dụng với GraphQL, gRPC và các API khác trong kho prisma-examples.

0 Bình luận

Đăng nhập để thảo luận

Chuyên mục Hướng dẫn

Tổng hợp các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.

Đăng ký nhận bản tin của chúng tôi

Hãy trở thành người nhận được các nội dung hữu ích của CyStack sớm nhất

Xem chính sách của chúng tôi Chính sách bảo mật.

Đăng ký nhận Newsletter

Nhận các nội dung hữu ích mới nhất