accessibility
Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android…
Prisma ORM patterns for TypeScript backends — schema design, query optimization, transactions, pagination, and critical traps like updateMany returning count not records, $transaction timeouts, migrate dev resetting the DB, @updatedAt skipped on bulk writes, and serverless
$ npx -y skills add affaan-m/ECC --skill prisma-patterns --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/prisma-patternsContext preview
The summary Claude sees to decide when to auto-load this skill.
Prisma ORM patterns for TypeScript backends — schema design, query optimization, transactions, pagination, and critical traps like updateMany returning count not records, $transaction timeouts, migrate dev resetting the DB, @updatedAt skipped on bulk writes, and serverless
name: prisma-patterns description: Prisma ORM patterns for TypeScript backends — schema design, query optimization, transactions, pagination, and critical traps like updateMany returning count not records, $transaction timeouts, migrate dev resetting the DB, @updatedAt skipped on bulk writes, and serverless connection exhaustion. Use when writing a Prisma schema or query, or debugging transactions, migrations, or serverless connection limits. metadata: origin: ECC
Production patterns and non-obvious traps for Prisma ORM in TypeScript backends.
> **Check your version before applying patterns.** The Prisma API surface has evolved across major releases: > > ```bash > npx prisma --version > ``` > > 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
| Strategy | Use When | Avoid When | |---|---|---| | `@default(cuid())` | Default choice — URL-safe, sortable, no collisions | Sequential IDs needed for external systems | | `@default(uuid())` | Interoperability with non-Prisma systems required | High-write tables (random UUIDs fragment B-tree indexes) | | `@default(autoincrement())` | Internal join tables, audit logs | Public-facing IDs (exposes record count) |
model User {
id String @id @default(cuid())
email String @unique // @unique already creates an index — no @@index needed
name String
role Role @default(USER)
posts Post[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
@@index([createdAt])
@@index([deletedAt, createdAt]) // composite for soft-delete + sort queries
}| | `include` | `select` | |---|---|---| | Returns | All scalar fields + specified relations | Only specified fields | | Use when | You need most fields plus a relation | Hot paths, large tables, avoiding over-fetch | | Performance | May over-fetch on wide tables | Minimal payload, faster on large datasets | | Prisma 5 note | Uses JOIN by default (`relationJoins`) | Same |
// include — all columns + relation
const user = await prisma.user.findUnique({
where: { id },
include: { posts: { select: { id: true, title: true } } },
});
// select — explicit allowlist
const user = await prisma.user.findUnique({
where: { id },
select: { id: true, email: true, name: true },
});Never return raw Prisma entities from API responses — map to response DTOs to control exposed fields:
// BAD: leaks passwordHash, deletedAt, internal fields
return await prisma.user.findUniqueOrThrow({ where: { id } });
// GOOD: explicit DTO mapping
const user = await prisma.user.findUniqueOrThrow({ where: { id } });
return { id: user.id, name: user.name, email: user.email };| Situation | Use | |---|---| | Independent operations, no inter-dependency | Array form | | Later step depends on earlier result | Interactive form | | External calls (email, HTTP) involved | Outside transaction entirely |
// Array form — batched in one round trip
const [user, post] = await prisma.$transaction([
prisma.user.update({ where: { id }, data: { name } }),
prisma.post.create({ data: { title, authorId: id } }),
]);
// Interactive form — use tx client only, never the outer prisma client
const post = await prisma.$transaction(async (tx) => {
const user = await tx.user.findUniqueOrThrow({ where: { id } });
if (user.role !== 'ADMIN') throw new Error('Forbidden');
return tx.post.create({ data: { title, authorId: user.id } });
});Each `PrismaClient` instance opens its own connection pool. Instantiate once.
// lib/prisma.ts
// 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 };
export const prisma = globalForPrisma.prisma ?? createPrismaClient();
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
Your agent can write code, but ECC gives it a coordinated engineering system and toolbox: it plans before it builds, verifies changes with tests, reviews its own work from a fresh context, remembers what matters, and turns repeated wins into reusable skills
Repo: affaan-m/ECC
Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android…
Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for…
Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks…
Design and optimize AI agent action spaces, tool definitions, and observation formatting for…
Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained…
Use after completing any non-trivial task. The agent self-rates its output on 5 axes —…