docs(skills): update Prisma and Zod API patterns for cross-version compatibility (#2336)

* docs(skills): update Prisma and Zod API patterns for cross-version compatibility

- skills/prisma-patterns: show both adapter-based and direct PrismaClient
  initialization side-by-side; update import paths with conditional notes;
  rewrite version header to be release-agnostic
- skills/backend-patterns: fix ZodError.errors -> ZodError.issues
- skills/coding-standards: fix ZodError.errors -> ZodError.issues
- skills/security-review: fix ZodError.errors -> ZodError.issues

These API differences were discovered during implementation of a
full-stack health assessment project. The updated code samples show
both the new and old API forms so the skill remains useful regardless
of which Prisma or Zod version is installed.

Closes #2335

* fix(skills): revert Prisma client imports to '@prisma/client'

The 'prisma' npm package is the CLI tool, not the runtime client.
Using it as an import source would cause compile-time failures on all
versions. '@prisma/client' remains the correct import source for the
generated PrismaClient and Prisma namespace types.

Found by Greptile during PR review.
This commit is contained in:
weizhiyuan 2026-06-30 09:38:30 +08:00 committed by GitHub
parent 8c75abf02e
commit 3a46c82b0c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 50 additions and 21 deletions

View File

@ -290,7 +290,7 @@ export function errorHandler(error: unknown, req: Request): Response {
return NextResponse.json({ return NextResponse.json({
success: false, success: false,
error: 'Validation failed', error: 'Validation failed',
details: error.errors details: error.issues
}, { status: 400 }) }, { status: 400 })
} }

View File

@ -324,7 +324,7 @@ export async function POST(request: Request) {
return NextResponse.json({ return NextResponse.json({
success: false, success: false,
error: 'Validation failed', error: 'Validation failed',
details: error.errors details: error.issues
}, { status: 400 }) }, { status: 400 })
} }
} }

View File

@ -8,15 +8,18 @@ metadata:
# Prisma Patterns # Prisma Patterns
Production patterns and non-obvious traps for Prisma ORM in TypeScript backends. Production patterns and non-obvious traps for Prisma ORM in TypeScript backends.
Tested against Prisma 5.x and 6.x. Some behaviors differ from Prisma 4.
Check the Prisma version before applying version-specific patterns: > **Check your version before applying patterns.** The Prisma API surface has evolved across major releases:
>
```bash > ```bash
npx prisma --version > npx prisma --version
``` > ```
>
Prisma 5 introduced `relationJoins`, which can load relations via JOIN rather than separate queries depending on query strategy and configuration. The `omit` field modifier and `prisma.$extends` Client Extensions API were also added. Note: `relationJoins` can cause row explosion on large 1:N relations or deep nested `include` — benchmark both approaches when relations may return many rows per parent. > Notable API differences across versions:
> - `relationJoins` can load relations via JOIN rather than separate queries, but may cause row explosion on large 1:N relations or deep `include` — benchmark both approaches
> - `omit` field modifier and `prisma.$extends` Client Extensions API were added
> - **Newer installs**: the package may be named `prisma` instead of `@prisma/client`; `PrismaClient` may require a driver adapter (e.g. `@prisma/adapter-pg`); `datasource.url` may live in `prisma.config.ts` instead of `schema.prisma`
> - CLI commands (`migrate dev`, `migrate deploy`, `generate`) are unchanged across versions
## When to Activate ## When to Activate
@ -122,19 +125,35 @@ Each `PrismaClient` instance opens its own connection pool. Instantiate once.
```ts ```ts
// lib/prisma.ts // lib/prisma.ts
import { PrismaClient } from '@prisma/client';
// Option A — adapter-based initialization (required by newer Prisma installs)
import { PrismaClient } from '@prisma/client'; // or the generated client path for your setup
import { PrismaPg } from '@prisma/adapter-pg';
function createPrismaClient() {
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL!,
});
return new PrismaClient({
adapter,
log: process.env.NODE_ENV === 'development' ? ['query', 'error'] : ['error'],
});
}
const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient }; const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient };
export const prisma = export const prisma = globalForPrisma.prisma ?? createPrismaClient();
globalForPrisma.prisma ??
new PrismaClient({
log: process.env.NODE_ENV === 'development' ? ['query', 'error'] : ['error'],
});
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma; if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
// Option B — direct initialization (older installs, no adapter needed)
// import { PrismaClient } from '@prisma/client';
// export const prisma = globalForPrisma.prisma ?? new PrismaClient({ ... });
``` ```
Use Option A if your Prisma install requires an `adapter` argument in the `PrismaClient` constructor.
Use Option B if `new PrismaClient()` works without arguments. Let the compiler tell you which is correct.
The `globalThis` pattern prevents duplicate instances during hot reload (Next.js, nodemon, ts-node-dev). The `globalThis` pattern prevents duplicate instances during hot reload (Next.js, nodemon, ts-node-dev).
### N+1 Problem ### N+1 Problem
@ -192,7 +211,7 @@ await prisma.user.update({ where: { id }, data: { deletedAt: null } }); // resto
### Error Handling ### Error Handling
```ts ```ts
import { Prisma } from '@prisma/client'; import { Prisma } from '@prisma/client'; // or the generated client path for your setup
try { try {
await prisma.user.create({ data: { email } }); await prisma.user.create({ data: { email } });
@ -223,9 +242,19 @@ DATABASE_URL="postgresql://user:pass@host/db?pgbouncer=true&connection_limit=1"
``` ```
```ts ```ts
// Vercel, AWS Lambda, and similar serverless runtimes: cap pool to 1 per instance // Vercel, AWS Lambda, and similar serverless runtimes:
// connection_limit and pool_timeout are controlled via DATABASE_URL // cap pool to 1 per instance; connection_limit and pool_timeout controlled via DATABASE_URL
const prisma = new PrismaClient();
// Adapter-based setup (if your Prisma install requires an adapter):
import { PrismaClient } from '@prisma/client';
import { PrismaPg } from '@prisma/adapter-pg';
const prisma = new PrismaClient({
adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }),
});
// Direct setup (if your Prisma install does not require an adapter):
// const prisma = new PrismaClient();
``` ```
## Anti-Patterns ## Anti-Patterns

View File

@ -67,7 +67,7 @@ export async function createUser(input: unknown) {
return await db.users.create(validated) return await db.users.create(validated)
} catch (error) { } catch (error) {
if (error instanceof z.ZodError) { if (error instanceof z.ZodError) {
return { success: false, errors: error.errors } return { success: false, errors: error.issues }
} }
throw error throw error
} }