repository_prisma
v1.0.6
Published
This repository demonstrates an **Implicit Transaction Pattern** for Prisma in Node.js, inspired by "Repository Pattern + SQLAlchemy" in Python.
Downloads
268
Readme
Repository Prisma Pattern
This repository demonstrates an Implicit Transaction Pattern for Prisma in Node.js, inspired by "Repository Pattern + SQLAlchemy" in Python.
It solves the common problem of "Prop-Drilling" transaction objects (tx) through your service layers.
📖 Read the Full Design Document for architecture details.
The Problem: Explicit Transactions (Standard Pattern)
In standard Prisma, you must pass the transaction client explicitly:
// Standard Prisma
await prisma.$transaction(async (tx) => {
await tx.user.create(...); // MUST use 'tx', not 'prisma'
await tx.post.create(...); // MUST use 'tx', not 'prisma'
})If you forget to use tx and use the global prisma instead, your query runs OUTSIDE the transaction. This is error-prone.
The Solution: Implicit Transactions
We use Node.js AsyncLocalStorage to store the transaction context globally for the request.
We provide Two Patterns to use this.
Pattern 1: Decorators (Python/NestJS Style)
Best if you like clean, declarative code and are using TypeScript with experimentalDecorators.
import { Transactional } from 'repository_prisma';
class UserService {
@Transactional()
async createUserAndPost() {
// Works automatically! no 'tx' argument needed.
await this.userRepo.create(...)
await this.postRepo.create(...)
}
}Pattern 2: Higher-Order Function (Node.js/Functional Style)
Best if you prefer explicit scoping and want to avoid experimental decorators.
import { runInTransaction } from 'repository_prisma';
class UserService {
async createUserAndPost() {
// Explicit wrapper
await runInTransaction(async () => {
await this.userRepo.create(...)
await this.postRepo.create(...)
});
}
}Optional: Context-Aware Prisma Client (No Repository)
If you want to skip repositories for a quick script or advanced Prisma queries, use the exported
prisma proxy. It automatically uses the transaction client if one is active.
import { prisma, runInTransaction } from 'repository_prisma';
await runInTransaction(async () => {
await prisma.user.create({ data: { email: '[email protected]' } });
});Initialization (Optional)
If you want to eagerly connect or enable SQLite WAL mode, call:
import { initializePrisma } from 'repository_prisma';
await initializePrisma({ enableWAL: true });Advanced: Root Client Access (Use Carefully)
The exported rootPrismaClient is intended for app-level tasks (migrations, health checks, cleanup scripts).
Avoid using it inside transactional flows, or you will bypass ALS and lose the implicit transaction behavior.
Example
See examples/implicit-transaction-demo.ts for a runnable demo.
Optional: No-Quote Repository Helper
If you prefer to avoid string literals like 'User', you can use BaseRepository.forModel:
import { BaseRepository, Models } from 'repository_prisma';
export class UserRepository extends BaseRepository.forModel(Models.User) {}
export class PostRepository extends BaseRepository.forModel(Models.Post) {}defineRepository is still available as a short alias if you prefer it.
Case-Insensitive Filters (SQLite-Safe)
SQLite does not support Prisma's mode: "insensitive" string filters. Use the helpers below
to avoid runtime errors and optionally apply a fallback in memory.
import { buildContainsFilter, filterContainsCaseInsensitive, supportsCaseInsensitiveMode } from 'repository_prisma';
const where = { name: buildContainsFilter("Alice", { caseInsensitive: true }) };
const rows = await repo.findMany({ where });
// If you need true case-insensitive behavior on SQLite, apply in-memory fallback:
const filtered = supportsCaseInsensitiveMode()
? rows
: filterContainsCaseInsensitive(rows, "Alice", (row) => row.name);If you want explicit provider selection, set PRISMA_DATASOURCE_PROVIDER=sqlite|postgresql|mysql|...
to override auto-detection (which uses DATABASE_URL).
Testing
npm test automatically syncs the Prisma schema to a dedicated SQLite file (test.db).
You can override it by setting DATABASE_URL_TEST.
Release (Tag-Based)
We use a tag-based release flow. Create a version tag and push it:
npm version patch -m "1.0.6" # or minor/major
git push origin main --follow-tagsIf you prefer to tag manually (or via a Git UI), create an annotated tag:
git tag -a v1.0.6 -m "1.0.6"
git push origin main --tagsPushing a tag like v1.2.3 triggers GitHub Actions to build and publish.
Trusted Publishing is enabled for this package.
How it Works
src/lib/context.ts: Holds theAsyncLocalStorage.src/lib/prisma-manager.ts: ThegetPrismaClient()function checks the storage. If a transaction is active, it returns the transaction client. If not, it returns the global client.src/lib/base-repository.ts: UsesgetPrismaClient()internally, so every query automatically uses the correct state.
