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.
- Stability
- Stable
- Scope
- Global
- Boundary
- packages/platform/db
Query · packages/platform/db
RLSenforcedapps/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
| id | title | tenant_id | published_at |
|---|---|---|---|
| pst_01HF…a3k | Launching multi-tenant RLS | tenant_a | 2026-05-21 |
| pst_01HF…b7m | Migrating off shared schemas | tenant_a | 2026-05-18 |
| pst_01HF…c2q | Internal roadmap (draft) | tenant_b | 2026-05-17 |
| pst_01HF…d9s | Customer-only changelog | tenant_c | 2026-05-15 |
| pst_01HF…e4w | RFC: schema-per-tenant | tenant_b | 2026-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
typescriptdb.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});