Prisma + StackRender:设计您的数据库并开始构建后端
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——一个免费且开源的数据库模式生成器,而不是手动设计模式。
-
在 StackRender 中创建新数据库
- 名称设为
ecommerce_db - 选择 PostgreSQL 作为数据库类型
- 名称设为
-
你会看到一个空白的图表。此时可以手动设计模式,也可以导入已有数据库。这里我们让 AI 完成初始设计。
-
AI 提示 – 在 StackRender 的 AI 提示功能中输入:
Design a multi‑vendor ecommerce database -
几秒钟后,StackRender 生成完整的数据库图表,包括表、字段和关系。所有内容均可编辑——你可以重命名表、调整列、微调关系,然后再继续。
-
当模式满意后:
- 打开 StackRender 中的 Code(代码)部分
- 导出 生成的 SQL 脚本
-
将导出的 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 客户端。
下一步
-
生成 Prisma 客户端
npx prisma generate -
开始构建 API 路由(例如,使用 Express、Fastify 或 NestJS)并从
../src/generated/prisma导入生成的客户端。 -
编写集成测试,针对实时数据库验证你的端点。
-
迭代 – 如果需要调整模式,返回 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 端点,以实现:
- 从客户端接收产品数据。
- 使用 Zod 验证负载。
- 使用 Prisma 持久化产品(及其图片)。
- 返回新创建的产品以及其供应商、类别和图片。
路由实现 (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。 |
| 返回 | 返回完整的产品记录,包含关联的 vendor、category 和 product_images。 |
端到端验证
- 初始状态 –
products表为空。 - 发送测试请求(例如通过 Postman 或 curl)并提供有效负载。
- 结果 –
products中出现新行,product_images中出现相应行。 - 响应 – API 返回创建的产品及其关联数据。
这表明:
- Prisma 模式已正确映射到数据库。
- 生成的 Prisma 客户端按预期工作。
- 验证层(Zod)在输入到达数据库前捕获了格式错误的请求。
为什么此工作流有价值
- 可视化数据库设计 – 从图表或模式文件开始;无需手写 SQL。
- 自动模型生成 – Prisma 读取模式并为您创建类型安全的模型。
- 快速原型 – 在几分钟内从模式跳转到功能端点。
- 一致性 – 类型、验证和数据库约束保持同步。
如果您使用 Prisma 和 PostgreSQL 构建后端,将它们与可视化设计工具(例如 StackRender)结合使用,可以为您的设置时间节省数小时,甚至数天。
资源
- StackRender – 可视化数据库设计与 Prisma 架构生成。
- Prisma Docs – https://www.prisma.io/docs
- Zod – https://github.com/colinhacks/zod
编码愉快!🚀
- 网站
- Github