prisma-orm

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Prisma ORM Best Practices (2025-2026)

Prisma ORM 最佳实践(2025-2026版)

This skill provides guidelines and snippets for using Prisma ORM effectively in Shopify Apps built with Remix.
本指南提供了在基于Remix构建的Shopify应用中高效使用Prisma ORM的准则及代码片段。

1. Setup & Configuration

1. 安装与配置

Installation

安装步骤

bash
npm install prisma --save-dev
npm install @prisma/client
npx prisma init
bash
npm install prisma --save-dev
npm install @prisma/client
npx prisma init

Recommended
schema.prisma
Config

推荐的
schema.prisma
配置

Use the
postgresql
provider (standard for Shopify apps) or
mysql
. Enable
improving-tracing
and other preview features if needed, but stick to stable for production.
prisma
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
  // Useful features for 2025+
  previewFeatures = ["driverAdapters", "metrics"]
}

// Standard Shopify Session Model
model Session {
  id          String    @id
  shop        String
  state       String
  isOnline    Boolean   @default(false)
  scope       String?
  expires     DateTime?
  accessToken String
  userId      BigInt?
}
使用
postgresql
驱动(Shopify应用的标准选择)或
mysql
。若有需要可启用
improving-tracing
等预览功能,但生产环境请使用稳定特性。
prisma
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
  // 2025+实用特性
  previewFeatures = ["driverAdapters", "metrics"]
}

// 标准Shopify会话模型
model Session {
  id          String    @id
  shop        String
  state       String
  isOnline    Boolean   @default(false)
  scope       String?
  expires     DateTime?
  accessToken String
  userId      BigInt?
}

2. Singleton Pattern for Remix

2. Remix中的单例模式

In development, Remix reloads the server, which can exhaust database connections if you create a new
PrismaClient
on every reload. Use the singleton pattern.
typescript
// app/db.server.ts
import { PrismaClient } from "@prisma/client";

const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };

export const prisma = globalForPrisma.prisma || new PrismaClient();

if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
在开发环境中,Remix会重载服务器,若每次重载都创建新的
PrismaClient
会耗尽数据库连接。请使用单例模式。
typescript
// app/db.server.ts
import { PrismaClient } from "@prisma/client";

const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };

export const prisma = globalForPrisma.prisma || new PrismaClient();

if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;

3. Schema Design Best Practices

3. Schema设计最佳实践

Use BigInt for Shopify IDs

为Shopify ID使用BigInt

Shopify IDs (Product ID, Order ID) exceed 32-bit integers.
  • BAD:
    Int
  • GOOD:
    BigInt
    (or
    String
    if you don't need math operations)
prisma
model Product {
  id          BigInt   @id // Matches Shopify's ID
  shop        String
  title       String
  // ...
  @@index([shop]) // Always index by shop for multi-tenancy
}
Shopify的ID(如产品ID、订单ID)超出了32位整数范围。
  • 不推荐
    Int
  • 推荐
    BigInt
    (若无需数学运算也可使用
    String
prisma
model Product {
  id          BigInt   @id // 匹配Shopify的ID格式
  shop        String
  title       String
  // ...
  @@index([shop]) // 多租户场景下务必按shop字段建立索引
}

Multi-tenancy

多租户支持

Every query MUST filter by
shop
. This is non-negotiable for security.
  • Tip: Use Prisma Middleware or Extensions to enforce this, or just be disciplined in your Service layer.
所有查询必须按
shop
字段过滤,这是安全性的硬性要求。
  • 提示:可使用Prisma中间件或扩展来强制实现,或在服务层严格执行该规则。

4. Migrations Workflow

4. 迁移工作流

NEVER use
prisma db push
in production.
  • Development:
    bash
    npx prisma migrate dev --name init_tables
  • Production (CI/CD):
    bash
    npx prisma migrate deploy
生产环境绝不要使用
prisma db push
  • 开发环境
    bash
    npx prisma migrate dev --name init_tables
  • 生产环境(CI/CD)
    bash
    npx prisma migrate deploy

5. Performance Optimization

5. 性能优化

Select Specific Fields

选择特定字段

Don't use
findMany
without
select
if you only need a few fields.
typescript
// BAD: Fetches massive JSON blobs
const products = await prisma.product.findMany();

// GOOD
const products = await prisma.product.findMany({
  select: { id: true, title: true }
});
若仅需部分字段,请勿在未指定
select
的情况下使用
findMany
typescript
// 不推荐:会获取大量JSON数据
const products = await prisma.product.findMany();

// 推荐
const products = await prisma.product.findMany({
  select: { id: true, title: true }
});

Transactions

事务处理

Use
$transaction
for dependent writes (e.g., creating an Order and its LineItems).
typescript
await prisma.$transaction(async (tx) => {
  const order = await tx.order.create({ ... });
  await tx.lineItem.create({ ... });
});
对于依赖型写入操作(如创建订单及其订单项),请使用
$transaction
typescript
await prisma.$transaction(async (tx) => {
  const order = await tx.order.create({ ... });
  await tx.lineItem.create({ ... });
});

Connection Pooling

连接池

For serverless or high-concurrency environments (like Remix on Vercel/Fly), use a connection pooler (Supabase Pooler, PgBouncer, or Prisma Accelerate).
对于无服务器或高并发环境(如部署在Vercel/Fly上的Remix应用),请使用连接池工具(如Supabase Pooler、PgBouncer或Prisma Accelerate)。

6. Common Recipes

6. 常用示例

Upsert (Create or Update)

Upsert(创建或更新)

Perfect for syncing data from Webhooks.
typescript
await prisma.user.upsert({
  where: { id: 1 },
  update: { email: "new@example.com" },
  create: { id: 1, email: "new@example.com" },
});
非常适用于从Webhook同步数据的场景。
typescript
await prisma.user.upsert({
  where: { id: 1 },
  update: { email: "new@example.com" },
  create: { id: 1, email: "new@example.com" },
});