@repo/db-prisma
Type-safe database access with Prisma ORM and Neon Postgres. Autocomplete for every query. Connection pooling for serverless. Migrations tracked in git. Works in edge runtime.
Quick Start
Set up your database in 10 minutes:
pnpm add @repo/db-prismaDefine your schema, generate types, and query with full autocomplete. Skip to Quick Start →
Why @repo/db-prisma?
Writing SQL is error-prone and tedious. Typos like user.emial crash at runtime. No autocomplete means guessing field
names. Managing database migrations across environments is complex. Serverless functions need connection pooling or
you'll exhaust database connections.
@repo/db-prisma solves this with type-safe queries, automatic migrations, and optimized connection pooling for serverless.
Production-ready with Neon Postgres (serverless, auto-scaling), edge runtime support, and zero-downtime migrations.
Use cases
- User management — Store profiles, authentication data, preferences, settings
- Content platforms — Blog posts, articles, products with categories and tags
- Multi-tenant SaaS — Organizations, teams, members with role-based access
- E-commerce — Products, variants, orders, inventory, customers
- Analytics — Event logs, metrics, usage data for dashboards
How it works
@repo/db-prisma provides a type-safe Prisma client for database operations:
import { prisma } from "@repo/db-prisma";
// Create user with full type safety
const user = await prisma.user.create({
data: {
email: "user@example.com",
name: "John Doe"
}
});
// TypeScript knows all fields and types
console.log(user.id, user.email, user.name);Schema defined in schema.prisma, migrations managed with prisma migrate, connections pooled via Neon.
Key features
Type-safe queries — Autocomplete for every field, compile-time errors for typos
Connection pooling — Optimized for serverless, handles traffic spikes automatically
Migrations — Version-controlled database changes, zero-downtime updates
Edge compatible — Works in Vercel Edge Functions and Cloudflare Workers
Neon Postgres — Serverless database with auto-scaling and global replication
Transactions — Multiple operations succeed or fail together
Quick Start
1. Install dependencies
pnpm add @repo/db-prisma @prisma/client
pnpm add -D prisma2. Configure database connection
DATABASE_URL="postgresql://user:pass@ep-xxx-pooler.neon.tech/db?sslmode=require"
DATABASE_URL_UNPOOLED="postgresql://user:pass@ep-xxx.neon.tech/db?sslmode=require"3. Define your schema
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
directUrl = env("DATABASE_URL_UNPOOLED")
}
generator client {
provider = "prisma-client-js"
}
model User {
id String @id @default(cuid())
email String @unique
name String
createdAt DateTime @default(now())
}4. Run migrations and query
# Generate Prisma client
pnpm prisma generate
# Create migration
pnpm prisma migrate dev --name init"use server";
import { prisma } from "@repo/db-prisma";
export async function createUser(email: string, name: string) {
const user = await prisma.user.create({
data: { email, name }
});
return user;
}That's it! You now have type-safe database access with autocomplete and migrations.
Complex queries
Use include to join related data:
const user = await prisma.user.findUnique({
where: { email: "user@example.com" },
include: { posts: true } // Include user's posts
});Technical Details
For Developers: Technical implementation details
Prisma ORM integration configured for Neon Postgres with connection pooling, edge compatibility, and type-safe database access.
Installation
pnpm add @repo/db-prismaOverview
| Property | Value |
|---|---|
| Location | packages/db-prisma |
| Dependencies | @prisma/client, @neondatabase/serverless |
| Database | Neon Postgres |
Export Paths
| Path | Description |
|---|---|
@repo/db-prisma | Prisma client instance |
@repo/db-prisma/edge | Edge-compatible client |
@repo/db-prisma/types | Generated Prisma types |
Basic Usage
Query Data
import { prisma } from "@repo/db-prisma";
// Find many
const users = await prisma.user.findMany({
// highlight-start
where: { isActive: true },
include: { posts: true }
// highlight-end
});
// Find unique
const user = await prisma.user.findUnique({
where: { email: "user@example.com" }
});
// Create
const newUser = await prisma.user.create({
data: {
email: "new@example.com",
name: "New User"
}
});
// Update
const updated = await prisma.user.update({
where: { id: "user_123" },
data: { name: "Updated Name" }
});
// Delete
await prisma.user.delete({
where: { id: "user_123" }
});Transactions
Atomic Operations
Use transactions when you need multiple operations to succeed or fail together. If any operation fails, all changes are rolled back.
import { prisma } from "@repo/db-prisma";
// highlight-start
const result = await prisma.$transaction(async (tx) => {
const user = await tx.user.create({
data: { email: "user@example.com", name: "User" }
});
const post = await tx.post.create({
data: {
title: "First Post",
authorId: user.id
}
});
return { user, post };
});
// highlight-endRaw Queries
SQL Injection
Always use parameterized queries with $queryRaw and $executeRaw. Never concatenate user input into SQL strings.
import { prisma } from "@repo/db-prisma";
// Raw query
const users = await prisma.$queryRaw`
SELECT * FROM "User" WHERE "createdAt" > ${startDate}
`;
// Raw execute
await prisma.$executeRaw`
UPDATE "User" SET "lastLogin" = NOW() WHERE "id" = ${userId}
`;Edge Runtime
Vercel Edge
Use the edge client for Vercel Edge Functions and Middleware. It uses Neon's serverless driver for HTTP-based connections.
import { prismaEdge } from "@repo/db-prisma/edge";
// highlight-next-line
export const runtime = "edge";
export async function GET() {
const users = await prismaEdge.user.findMany();
return Response.json(users);
}Connection Pooling
Performance
Always use the pooled connection URL for your application and the direct (unpooled) URL for migrations.
# Pooled connection (default) - for serverless
DATABASE_URL="postgresql://user:pass@ep-xxx.neon.tech/db?sslmode=require"
# Direct connection - for migrations
DATABASE_URL_UNPOOLED="postgresql://user:pass@ep-xxx.neon.tech/db?sslmode=require"Schema Management
Generate Client
# Generate Prisma client
pnpm --filter @repo/db-prisma generate
# Or from package directory
cd packages/db-prisma
pnpm prisma generateMigrations
Production Migrations
Always use DATABASE_URL_UNPOOLED for migrations. Pooled connections can cause migration issues.
# Create migration
pnpm --filter @repo/db-prisma migrate:dev --name migration_name
# Apply migrations (production)
pnpm --filter @repo/db-prisma migrate:deploy
# Reset database (development only)
pnpm --filter @repo/db-prisma migrate:resetSchema Example
// schema.prisma
datasource db {
provider = "postgresql"
// highlight-start
url = env("DATABASE_URL")
directUrl = env("DATABASE_URL_UNPOOLED")
// highlight-end
}
generator client {
provider = "prisma-client-js"
previewFeatures = ["driverAdapters"]
}
model User {
id String @id @default(cuid())
email String @unique
name String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
posts Post[]
}
model Post {
id String @id @default(cuid())
title String
content String?
published Boolean @default(false)
authorId String
author User @relation(fields: [authorId], references: [id])
createdAt DateTime @default(now())
}Type Safety
Using Generated Types
Full Type Inference
Prisma generates TypeScript types from your schema. Use them for complete type safety across your application.
import { prisma } from "@repo/db-prisma";
// highlight-next-line
import type { User, Post, Prisma } from "@repo/db-prisma/types";
// Type-safe query
const createUser = async (data: Prisma.UserCreateInput): Promise<User> => {
return prisma.user.create({ data });
};
// Type-safe include
// highlight-start
type UserWithPosts = Prisma.UserGetPayload<{
include: { posts: true };
}>;
// highlight-endSelect Types
import type { Prisma } from "@repo/db-prisma/types";
// Define what to select
const userSelect = {
id: true,
email: true,
name: true
} satisfies Prisma.UserSelect;
// Get the type
// highlight-start
type UserBasic = Prisma.UserGetPayload<{
select: typeof userSelect;
}>;
// highlight-endSoft Deletes
import { prisma } from "@repo/db-prisma";
// Middleware for soft deletes
// highlight-start
prisma.$use(async (params, next) => {
if (params.model === "User") {
if (params.action === "delete") {
params.action = "update";
params.args.data = { deletedAt: new Date() };
}
if (params.action === "findMany") {
params.args.where = {
...params.args.where,
deletedAt: null
};
}
}
return next(params);
});
// highlight-endLogging
Debug Queries
Enable query logging in development to debug performance issues and see the actual SQL being executed.
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient({
// highlight-start
log: [
{ emit: "event", level: "query" },
{ emit: "stdout", level: "error" },
{ emit: "stdout", level: "warn" }
]
// highlight-end
});
prisma.$on("query", (e) => {
console.log("Query:", e.query);
console.log("Duration:", e.duration, "ms");
});Environment Variables
# Pooled connection (for app)
DATABASE_URL="postgresql://user:pass@ep-xxx-pooler.neon.tech/db?sslmode=require"
# Direct connection (for migrations)
DATABASE_URL_UNPOOLED="postgresql://user:pass@ep-xxx.neon.tech/db?sslmode=require"Related Packages
- @repo/db-upstash-redis - Redis caching
- @repo/db-upstash-vector - Vector database