Skip to content
All features
platformpackages/platform/db

db package

Typed Prisma client wrapper — extension-aware, RLS-friendly, with per-tenant connection pinning when the deployment splits writes from reads.

Open docs
Stability
Stable
Scope
Global
Boundary
packages/platform/db
Query · packages/platform/db
RLSenforced
apps/web/app/posts/page.tsx
// Tenant context resolved by @nebutra/tenant middleware
const ctx = getCurrentTenant();

const posts = await prisma.post.findMany({
  where: {
    tenantId: ctx.tenant.id,
    published: true,
  },
  orderBy: { publishedAt: "desc" },
  take: 5,
});
// → Prisma client wrapped with withRls(prisma, ctx.tenant.id)
// → PostgreSQL enforces RLS via SET LOCAL app.tenant_id

Query results

tenant-scoped via row-level security

2 / 5
idtitletenant_idpublished_at
pst_01HF…a3kLaunching multi-tenant RLStenant_a2026-05-21
pst_01HF…b7mMigrating off shared schemastenant_a2026-05-18
pst_01HF…c2qInternal roadmap (draft)tenant_b2026-05-17
pst_01HF…d9sCustomer-only changelogtenant_c2026-05-15
pst_01HF…e4wRFC: schema-per-tenanttenant_b2026-05-12

RLS hides rows belonging to other tenants — they never leave the database.

Visible rows

847

in tenant_a

Hidden by RLS

12,401

other tenants

Usagedb.ts
typescript
db.ts
1import { prisma } from "@nebutra/db";
2
3// Wrapped Prisma client — RLS enforced via withRls() per request.
4const posts = await prisma.post.findMany({
5  where: { tenantId: ctx.tenant.id, published: true },
6  include: { author: true },
7  orderBy: { publishedAt: "desc" },
8  take: 20,
9});
10
11await prisma.$transaction(async (tx) => {
12  await tx.post.update({ where: { id }, data: { published: true } });
13  await tx.auditLog.create({ data: { action: "post.publish", postId: id } });
14});