Prisma + StackRender:设计您的数据库并开始构建后端

发布: (2026年1月4日 GMT+8 03:38)
10 min read
原文: Dev.to

I’m happy to translate the article for you, but I’ll need the full text of the post (the content you’d like translated). Could you please paste the article’s body here? Once I have the text, I’ll provide a Simplified Chinese translation while preserving the original formatting, markdown, and code blocks.

我们将做什么

  • 使用 StackRender 直观地设计 PostgreSQL 数据库
  • 即时部署数据库
  • 自动将模式拉入 Prisma
  • 开始构建和测试后端端点

前置条件

  • Node.js 已安装
  • 一个 PostgreSQL 数据库(本地或远程)
  • 在你的 Node.js 项目中已安装并配置 Prisma

注意: 如果你是 Prisma 新手,请在继续之前先遵循官方设置指南。

Source:

从空项目开始

我们从一个已经设置好 Prisma 的全新 Node.js 项目开始。如果打开你的 schema.prisma 文件,你会发现它是完全空的——没有模型、没有关系、还没有任何定义。同时,已连接的 PostgreSQL 数据库也是空的(没有表或约束)。这正是理想的起点。

generator client {
  provider = "prisma-client-js"
  output   = "../src/generated/prisma"
}

datasource db {
  provider = "postgresql"
}

使用 StackRender 设计模式

我们将使用 StackRender——一个免费且开源的数据库模式生成器,而不是手动设计模式。

  1. 在 StackRender 中创建新数据库

    • 名称设为 ecommerce_db
    • 选择 PostgreSQL 作为数据库类型
  2. 你会看到一个空白的图表。此时可以手动设计模式,也可以导入已有数据库。这里我们让 AI 完成初始设计。

  3. AI 提示 – 在 StackRender 的 AI 提示功能中输入:

    Design a multi‑vendor ecommerce database
  4. 几秒钟后,StackRender 生成完整的数据库图表,包括表、字段和关系。所有内容均可编辑——你可以重命名表、调整列、微调关系,然后再继续。

  5. 当模式满意后:

    • 打开 StackRender 中的 Code(代码)部分
    • 导出 生成的 SQL 脚本
  6. 将导出的 SQL 脚本在你的 PostgreSQL 数据库上运行(例如使用 pgAdmin)。执行后,所有表、约束和关系会自动创建。你的数据库已经在无需手写 SQL 的情况下上线。

将实时数据库同步回 Prisma

数据库已经就绪后,我们可以把模式拉取到 Prisma:

npx prisma db pull

Prisma 会对数据库进行 introspection,并基于现有的表和关系自动生成 schema.prisma 文件。以下是生成的模式(为简洁起见已截断)。

generator client {
  provider = "prisma-client-js"
  output   = "../src/generated/prisma"
}

datasource db {
  provider = "postgresql"
}

model cart_items {
  id         Int       @id @default(autoincrement())
  cart_id    Int
  product_id Int
  quantity   Int
  added_at   DateTime? @default(now()) @db.Timestamptz(6)
  carts      carts     @relation(fields: [cart_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
  products   products  @relation(fields: [product_id], references: [id], onDelete: NoAction, onUpdate: NoAction)
}

model carts {
  id         Int          @id @default(autoincrement())
  user_id    Int          @unique
  created_at DateTime?    @default(now()) @db.Timestamptz(6)
  updated_at DateTime?    @default(now()) @db.Timestamptz(6)
  cart_items cart_items[]
  users      users        @relation(fields: [user_id], references: [id], onDelete: NoAction, onUpdate: NoAction)
}

/* ... additional models omitted for brevity ... */

提示: 生成的模型包含所有关系、枚举和默认值,让你拥有一个可直接使用的 Prisma 客户端。

下一步

  1. 生成 Prisma 客户端

    npx prisma generate
  2. 开始构建 API 路由(例如,使用 Express、Fastify 或 NestJS)并从 ../src/generated/prisma 导入生成的客户端。

  3. 编写集成测试,针对实时数据库验证你的端点。

  4. 迭代 – 如果需要调整模式,返回 StackRender,修改图表,重新导出 SQL,运行它,并再次运行 npx prisma db pull

摘要

通过将 StackRender 的可视化设计器Prisma 的 introspection 相结合,您可以:

  • 跳过手动编写 SQL
  • 保持 Prisma 模型与数据库完美同步
  • 加速 “后端优先” 的开发周期

在下一个项目中尝试此工作流,节省数小时的搭建时间!

Prisma 模式(已清理)

model cart_items {
  id          Int      @id @default(autoincrement())
  cart_id     Int
  product_id  Int
  quantity    Int
  cart        carts    @relation(fields: [cart_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
  product     products @relation(fields: [product_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
}

model categories {
  id          Int        @id @default(autoincrement())
  name        String
  description String?
  products    products[]
}

model order_items {
  id          Int      @id @default(autoincrement())
  order_id    Int
  product_id  Int
  quantity    Int
  price       Decimal  @db.Decimal(10, 2)
  orders      orders   @relation(fields: [order_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
  products    products @relation(fields: [product_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
}

model orders {
  id               Int                         @id @default(autoincrement())
  user_id          Int
  total_amount     Decimal                     @db.Decimal(10, 2)
  currency         orders_currency_enum
  status           orders_order_status_enum   @default(pending)
  placed_at        DateTime?                  @default(now()) @db.Timestamptz(6)
  shipped_at       DateTime?                  @db.Timestamptz(6)
  delivered_at     DateTime?                  @db.Timestamptz(6)
  cancelled_at     DateTime?                  @db.Timestamptz(6)
  users            users                       @relation(fields: [user_id], references: [id], onDelete: NoAction, onUpdate: NoAction)
  order_items      order_items[]
  shipments        shipments?
  payments         payments?
}

model payments {
  id               Int                         @id @default(autoincrement())
  order_id         Int                         @unique
  amount           Decimal                     @db.Decimal(10, 2)
  method           String?
  status           payments_payment_status_enum @default(unpaid)
  transaction_id   String?                     @unique
  payment_date     DateTime?                   @default(now()) @db.Timestamptz(6)
  orders           orders                      @relation(fields: [order_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
}

model product_images {
  id          Int      @id @default(autoincrement())
  product_id  Int
  url         String
  alt_text    String?
  products    products @relation(fields: [product_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
}

model products {
  id              Int              @id @default(autoincrement())
  vendor_id       Int
  category_id     Int?
  name            String
  description     String?
  price           Decimal          @db.Decimal(10, 2)
  stock_quantity  Int
  sku             String?          @unique
  created_at      DateTime?        @default(now()) @db.Timestamptz(6)
  updated_at      DateTime?        @default(now()) @db.Timestamptz(6)
  cart_items      cart_items[]
  order_items     order_items[]
  product_images  product_images[]
  categories      categories?      @relation(fields: [category_id], references: [id], onDelete: NoAction, onUpdate: NoAction)
  vendors         vendors          @relation(fields: [vendor_id], references: [id], onDelete: NoAction, onUpdate: NoAction)
  reviews         reviews[]
}

model reviews {
  id          Int       @id @default(autoincrement())
  product_id  Int
  user_id     Int
  rating      Int
  comment     String?
  review_date DateTime? @default(now()) @db.Timestamptz(6)
  products    products  @relation(fields: [product_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
  users       users     @relation(fields: [user_id], references: [id], onDelete: NoAction, onUpdate: NoAction)
}

model shipments {
  id                 Int                               @id @default(autoincrement())
  order_id           Int                               @unique
  shipment_date      DateTime?       
@db.Timestamptz(6)
  tracking_number    String?                           @unique
  carrier            String?
  fulfillment_status shipments_fulfillment_status_enum @default(not_fulfilled)
  created_at         DateTime?                         @default(now()) @db.Timestamptz(6)
  updated_at         DateTime?                         @default(now()) @db.Timestamptz(6)
  orders             orders                            @relation(fields: [order_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
}

model users {
  id               Int       @id @default(autoincrement())
  username         String    @unique
  email            String    @unique
  password_hash    String
  first_name       String?
  last_name        String?
  shipping_address String?
  billing_address  String?
  created_at       DateTime? @default(now()) @db.Timestamptz(6)
  updated_at       DateTime? @default(now()) @db.Timestamptz(6)
  carts            carts?
  orders           orders[]
  reviews          reviews[]
}

model vendor_payouts {
  id             Int       @id @default(autoincrement())
  vendor_id      Int
  payout_date    DateTime? @default(now()) @db.Timestamptz(6)
  amount         Decimal   @db.Decimal(10, 2)
  transaction_id String?   @unique
  vendors        vendors   @relation(fields: [vendor_id], references: [id], onDelete: NoAction, onUpdate: NoAction)
}

model vendors {
  id             Int              @id @default(autoincrement())
  name           String
  email          String           @unique
  phone_number   String?
  address        String?
  created_at     DateTime?        @default(now()) @db.Timestamptz(6)
  updated_at     DateTime?        @default(now()) @db.Timestamptz(6)
  order_items    order_items[]
  products       products[]
  vendor_payouts vendor_payouts[]
}

/* ---------- Enums ---------- */

enum orders_currency_enum {
  USD
  EUR
  GBP
}

enum orders_order_status_enum {
  pending
  processing
  shipped
  delivered
  cancelled
}

enum payments_payment_status_enum {
  unpaid
  paid
  refunded
}

enum shipments_fulfillment_status_enum {
  not_fulfilled
  fulfilled
  partially_fulfilled
}

简单后端功能 (POST /product)

以下示例展示了如何创建一个 POST 端点,以实现:

  1. 从客户端接收产品数据。
  2. 使用 Zod 验证负载。
  3. 使用 Prisma 持久化产品(及其图片)。
  4. 返回新创建的产品以及其供应商、类别和图片。

路由实现 (TypeScript)

import { Request, Response } from "express";
import { prisma } from "./prismaClient"; // adjust the import to your setup
import { createProductSchema, CreateProductImageInput } from "./schemas";

router.post("/product", async (request: Request, response: Response) => {
  try {
    // 1️⃣ Validate request body
    const result = createProductSchema.safeParse(request.body);
    if (!result.success) {
      response.status(400).json(result);
      return;
    }

    const { data } = result;

    // 2️⃣ Create product + nested images
    const product = await prisma.products.create({
      data: {
        ...data,
        product_images: {
          create: data.product_images.map(
            (productImage: CreateProductImageInput) => productImage
          ),
        },
      },
      include: {
        vendors: true,
        categories: true,
        product_images: true,
      },
    });

    // 3️⃣ Respond with the created entity
    response.status(200).json({ success: true, data: product });
  } catch (error) {
    console.error(error);
    response.status(500).json({ error, success: false });
  }
});

端点功能说明

步骤描述
接收接受描述产品及其图片数组的 JSON 负载。
验证使用 Zod (createProductSchema) 确保负载符合预期结构。
持久化调用 prisma.products.create,并对 product_images 使用嵌套的 create
返回返回完整的产品记录,包含关联的 vendorcategoryproduct_images

端到端验证

  1. 初始状态products 表为空。
  2. 发送测试请求(例如通过 Postman 或 curl)并提供有效负载。
  3. 结果products 中出现新行,product_images 中出现相应行。
  4. 响应 – API 返回创建的产品及其关联数据。

这表明:

  • Prisma 模式已正确映射到数据库。
  • 生成的 Prisma 客户端按预期工作。
  • 验证层(Zod)在输入到达数据库前捕获了格式错误的请求。

为什么此工作流有价值

  • 可视化数据库设计 – 从图表或模式文件开始;无需手写 SQL。
  • 自动模型生成 – Prisma 读取模式并为您创建类型安全的模型。
  • 快速原型 – 在几分钟内从模式跳转到功能端点。
  • 一致性 – 类型、验证和数据库约束保持同步。

如果您使用 PrismaPostgreSQL 构建后端,将它们与可视化设计工具(例如 StackRender)结合使用,可以为您的设置时间节省数小时,甚至数天。

资源

编码愉快!🚀

  • 网站
  • Github
Back to Blog

相关文章

阅读更多 »