prisma/prisma
Patterns and conventions
Coding conventions, style rules, and cross-cutting patterns enforced in this repo. Most of this is codified in AGENTS.md and eslint.config.cjs plus eslint-local-rules/.
File names
- kebab-case for new file names:
query-utils.ts,filter-operators.test.ts. Existing PascalCase files (Migrate.ts,ClientEngine.ts) stay as they are unless renamed for another reason.
Imports
Avoid creating new barrel files (
index.tsthat re-export from sibling modules). Import from the source directly:// ✅ Preferred // ❌ Avoid for new code import { foo } from './utils'; import { foo } from './utils/query-utils';If
utils/index.tsalready exists, keep using it for consistency with surrounding code.Use type-only imports (
import type { … }) when the symbol is only used in type position. The build's tree-shaking depends on this.The simple-import-sort ESLint plugin enforces a deterministic import order.
Comments
From AGENTS.md:
Inline code comments can be broadly categorized as answering one of three questions:
- What does this code do? — never write these. They duplicate the code and rot.
- Why was this code written (in this particular way or at all)? — this is what inline comments are for. Use them for context, GitHub issue references, decision rationale.
- How does this code work? — these should be exceedingly rare. Prefer rewriting to be clear, unless the complexity is intrinsic (algorithm names, references to papers, external systems).
Documentation comments on exported items are encouraged. Use them to describe the contract of the function/class/method, not the internal implementation details.
Class members
Prefer native JavaScript private syntax (#field, #method()) over the TypeScript private keyword. Native private fields are real runtime privacy; the TypeScript private keyword is only erased by the type checker.
"Wasm", not "WASM"
The official spelling is Wasm. There are stale "WASM" usages in the codebase, but they're considered errors. Fix them whenever you incidentally touch the surrounding code.
Error handling
Across the runtime, errors flow through several stages before reaching user code. The pipeline is:
- Adapter level — the underlying driver throws (e.g.,
pg'serror.code === '23505'). convertDriverError()— each adapter (packages/adapter-pg/src/errors.ts,packages/adapter-mysql/src/errors.ts, …) maps the DB code to aMappedErrorkind from@prisma/driver-adapter-utils.rethrowAsUserFacing()—packages/client-engine-runtime/src/user-facing-error.tsmapsMappedErrorkinds to public Prisma error codes (P2xxx).- Client surface — the user sees
PrismaClientKnownRequestError(with.code,.meta,.message) orPrismaClientUnknownRequestError.
To add a new error mapping:
- Add the kind to
MappedErrorinpackages/driver-adapter-utils/src/types.ts. - Map the database error code in each adapter that can produce it.
- Add Prisma code mapping in
getErrorCode()and message inrenderErrorMessage()(packages/client-engine-runtime/src/user-facing-error.ts).
Error code conventions:
- P2000–P2037 are documented in the public error reference.
- P2038 —
PrismaClientInitializationErrorfromClientEnginewhen no driver adapter is configured. - P2039 — fallback code for unmapped database-specific driver-adapter errors. The format is
Database error. Code: <originalCode>. Message: <originalMessage>. Used so unknown DB errors show up as HTTP 400 from the query plan executor instead of 500 (Accelerate strips 500s). - Pick the next available code (P2040, …) for new additions and document it in
AGENTS.md.
Raw queries ($executeRaw, $queryRaw) use a different path — rethrowAsUserFacingRawError() always returns P2010 with the format Raw query failed. Code: <originalCode>. Message: <originalMessage>.
Asserting on errors in tests
From AGENTS.md:
// ✅ Preferred — works across realms (e.g., generated client in subdirectory)
expect(result.name).toBe('PrismaClientKnownRequestError');
expect(result.code).toBe('P2002');
// ❌ Brittle — instanceof check fails when the constructor comes from a different realm
expect(result).toBeInstanceOf(PrismaClientKnownRequestError);Tests
- Test files use
*.test.ts(Jest) or*.vitest.ts(Vitest where the package is mid-migration). Both are excluded from build output by esbuild config. - Test files live alongside source files (under
__tests__/or directly next to the source — package-by-package convention). - Inline snapshots are sensitive to formatting. Prefer concise expectations unless the exact message is part of the contract.
- Functional generated clients in
packages/client/tests/functional/**/.generatedimportpackages/client/runtime/client.jsdirectly. Runtime changes insrc/runtimemay need corresponding bundle rebuilds before functional tests pick them up.
Schema fixtures
- Test fixtures live in
packages/<pkg>/src/__tests__/fixtures(most packages) andpackages/<pkg>/fixtures(a few). - Many fixtures pair a schema with a
prisma.config.ts(sometimes multiple variants, e.g.,invalid-url.config.tsnext toinvalid-url.prisma). - Tests swap configs with
ctx.setConfigFile(name)from__helpers__/prismaConfig.ts. Reset is automatic between tests.
Cross-cutting hygiene
- Stay ASCII unless a file already uses Unicode.
- Keep
AGENTS.mdupdated when you discover something new about the codebase. It's the canonical agent-facing knowledge base; staleness costs everyone. - Build from root after multi-package changes:
pnpm build(orpnpm dev) from the repo root respects Turborepo's dependency graph. Per-package builds can miss upstream changes.
ESLint local rules
eslint-local-rules/ defines project-specific rules. They're loaded by the eslint-plugin-local-rules plugin from the root eslint.config.cjs. Read them as a reference for "what does Prisma's own code review actually enforce".
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.