OneApp Docs
PackagesDatabase

@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-prisma

Define 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 prisma

2. Configure database connection

.env.local
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

prisma/schema.prisma
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
app/actions/user.ts
"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-prisma

Overview

PropertyValue
Locationpackages/db-prisma
Dependencies@prisma/client, @neondatabase/serverless
DatabaseNeon Postgres

Export Paths

PathDescription
@repo/db-prismaPrisma client instance
@repo/db-prisma/edgeEdge-compatible client
@repo/db-prisma/typesGenerated 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-end

Raw 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 generate

Migrations

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:reset

Schema 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-end

Select 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-end

Soft 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-end

Logging

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"

On this page