Create FasterCreate Faster

Prisma

Prisma is a next-generation ORM with an intuitive data model, automated migrations, and powerful type safety.

→ Prisma Documentation

Requires: Database (PostgreSQL or MySQL)

What create-faster adds

Beyond the official setup, we include:

Development Scripts:

  • db:generate - Generate Prisma Client from schema
  • db:migrate - Create and apply migrations
  • db:push - Push schema to database (dev)
  • db:studio - Launch Prisma Studio visual editor
  • db:seed - Populate database with sample data

Files created (Turborepo):

packages/db/
├── prisma/
│   └── schema.prisma       # Database schema with User, Post, auth models
├── src/
│   └── index.ts            # PrismaClient singleton with type helpers
├── generated/              # Auto-generated Prisma Client (gitignored)
│   └── prisma/
├── scripts/
│   └── seed.ts             # Seed script with sample data
├── package.json            # Dependencies and db:* scripts
├── tsconfig.json           # TypeScript config
└── .env.example            # DATABASE_URL template

Files created (Single repo):

src/lib/db/
└── index.ts                # PrismaClient singleton

prisma/
└── schema.prisma           # Database schema

scripts/
└── seed.ts                 # Seed script

.env.example                # DATABASE_URL template

schema.prisma

Database schema with User and Post models. Shown here with PostgreSQL:

prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
  output   = "../generated/prisma" // turborepo only
}

datasource db {
  provider = "postgresql" // or "mysql" based on database selection
  url      = env("DATABASE_URL")
}

model User {
  id            String   @id @default(uuid())
  username      String   @unique
  email         String   @unique
  emailVerified Boolean  @default(false) @map("email_verified")
  avatarUrl     String?  @map("avatar_url") @db.Text

  phone         String?
  firstName     String?  @map("first_name")
  lastName      String?  @map("last_name")
  createdAt     DateTime @default(now()) @map("created_at")
  updatedAt     DateTime @updatedAt @map("updated_at")

  posts         Post[]

  @@map("users")
}

model Post {
  id        String   @id @default(uuid())
  title     String
  content   String   @db.Text
  published Boolean  @default(false)
  authorId  String   @map("author_id")
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @updatedAt @map("updated_at")

  author    User     @relation(fields: [authorId], references: [id], onDelete: Cascade)

  @@map("posts")
}

With Better Auth, the schema adds Session, Account, and Verification models:

prisma/schema.prisma (Better Auth additions)
model User {
  // ... base fields
  accounts      Account[]
  sessions      Session[]
  posts         Post[]
}

model Session {
  id        String   @id
  userId    String   @map("user_id")
  token     String   @unique
  expiresAt DateTime @map("expires_at")
  ipAddress String?  @map("ip_address")
  userAgent String?  @map("user_agent")
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @updatedAt @map("updated_at")

  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@map("sessions")
}

model Account {
  id                      String    @id
  userId                  String    @map("user_id")
  accountId               String    @map("account_id")
  providerId              String    @map("provider_id")
  accessToken             String?   @map("access_token") @db.Text
  refreshToken            String?   @map("refresh_token") @db.Text
  accessTokenExpiresAt    DateTime? @map("access_token_expires_at")
  refreshTokenExpiresAt   DateTime? @map("refresh_token_expires_at")
  scope                   String?
  idToken                 String?   @map("id_token") @db.Text
  password                String?
  createdAt               DateTime  @default(now()) @map("created_at")
  updatedAt               DateTime  @updatedAt @map("updated_at")

  user                    User      @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@map("accounts")
}

model Verification {
  id         String   @id
  identifier String
  value      String
  expiresAt  DateTime @map("expires_at")
  createdAt  DateTime @default(now()) @map("created_at")
  updatedAt  DateTime @updatedAt @map("updated_at")

  @@map("verifications")
}

index.ts

PrismaClient singleton with global caching to prevent hot-reload issues:

src/index.ts
import { PrismaClient } from '../generated/prisma'; // or '@prisma/client' in single repo

const globalForPrisma = global as unknown as { prisma: PrismaClient };

export const prisma =
  globalForPrisma.prisma ||
  new PrismaClient({
    log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
  });

if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;

export * from '../generated/prisma'; // or '@prisma/client' in single repo

export type User = Awaited<ReturnType<typeof prisma.user.findUnique>>;
export type Post = Awaited<ReturnType<typeof prisma.post.findUnique>>;
export type UserWithPosts = Awaited<
  ReturnType<typeof prisma.user.findUnique<{ where: { id: string }; include: { posts: true } }>>
>;
export type PostWithAuthor = Awaited<
  ReturnType<typeof prisma.post.findUnique<{ where: { id: string }; include: { author: true } }>>
>;

With Better Auth, additional type helpers are exported:

src/index.ts (Better Auth additions)
export type Session = Awaited<ReturnType<typeof prisma.session.findUnique>>;
export type Account = Awaited<ReturnType<typeof prisma.account.findUnique>>;
export type Verification = Awaited<ReturnType<typeof prisma.verification.findUnique>>;

seed.ts

Seed script with sample data using .create() and .createMany():

scripts/seed.ts
import { prisma } from '@repo/db'; // or '../src/lib/db' in single repo

async function seed() {
  console.log('🌱 Seeding database...');

  await prisma.post.deleteMany();
  await prisma.user.deleteMany();

  const alice = await prisma.user.create({
    data: {
      name: 'Alice Johnson',
      email: 'alice@example.com',
      bio: 'Software engineer and open source enthusiast',
    },
  });

  const bob = await prisma.user.create({
    data: {
      name: 'Bob Smith',
      email: 'bob@example.com',
      bio: 'Tech writer and developer advocate',
    },
  });

  await prisma.post.createMany({
    data: [
      {
        title: 'Getting Started with Prisma',
        content: 'Prisma is a next-generation ORM...',
        published: true,
        authorId: alice.id,
      },
      {
        title: 'Building Modern APIs',
        content: 'Modern API development requires...',
        published: true,
        authorId: alice.id,
      },
      {
        title: 'Draft: Upcoming Features',
        content: 'This is a draft post...',
        published: false,
        authorId: bob.id,
      },
    ],
  });

  console.log('🎉 Seeding completed!');
}

seed()
  .catch((error) => {
    console.error('❌ Seeding failed:', error);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
    process.exit(0);
  });

On this page