Database
Multi-database support via Prisma ORM — PostgreSQL, MySQL, and SQLite. Switch providers with one command, type-safe queries, and automated setup.
Supported Databases
| Database | Recommended for |
|---|---|
| PostgreSQL | Production (recommended) |
| MySQL | Production (alternative) |
| SQLite | Local development only |
SQLite is not suitable for production. Use PostgreSQL or MySQL when deploying.
Switching Providers
# Interactively select a provider and update schema + migrations
npm run db:update-providerThis script (at scripts/update-db-provider.ts) updates the datasource db.provider in schema.prisma and points migrations to the correct folder (migrations-postgresql, migrations-mysql, or migrations-sqlite).
Never edit the provider field in schema.prisma manually — always use npm run db:update-provider.
Configuration
PostgreSQL
# .env
DATABASE_URL="postgresql://user:password@localhost:5432/tikship"createdb tikship
npx prisma migrate deployMySQL
# .env
DATABASE_URL="mysql://user:password@localhost:3306/tikship"CREATE DATABASE tikship;npx prisma migrate deploySQLite
# .env
DATABASE_URL="file:./dev.db"SQLite works out of the box — no server setup needed.
Automated Setup
For a clean environment, a single command handles everything:
npm run initThis runs:
db:update-provider— detects provider fromDATABASE_URLdb:generate— regenerates the Prisma clientprisma migrate deploy— applies all pending migrationsdb:seed— seeds initial data (superuser, sample products)
Prisma Client
All database access goes through the singleton in src/lib/db/prisma.ts:
import { prisma } from '@/lib/db/prisma'Never instantiate new PrismaClient() elsewhere.
CRUD Examples
// Create
const user = await prisma.user.create({
data: { email: 'user@example.com', name: 'Jane' },
})
// Read — prefer select over include for performance
const user = await prisma.user.findUnique({
where: { id: userId },
select: { id: true, name: true, email: true },
})
// List + count in one round trip
const [users, total] = await Promise.all([
prisma.user.findMany({ where, skip, take, orderBy }),
prisma.user.count({ where }),
])
// Update
await prisma.user.update({
where: { id: userId },
data: { name: 'John' },
})
// Delete
await prisma.user.delete({ where: { id: userId } })Migrations
# Apply pending migrations (production)
npx prisma migrate deploy
# Create and apply a new migration (development)
npx prisma migrate dev
# Reset (drop all tables, re-migrate, re-seed) — development only
npx prisma migrate reset
# Regenerate Prisma client after schema changes
npm run db:generateMigration SQL files are stored per engine:
prisma/
├── migrations-postgresql/
├── migrations-mysql/
├── migrations-sqlite/
└── schema.prismaTransactions
Wrap multiple writes in a transaction inside the service layer:
await prisma.$transaction(async (tx) => {
const user = await tx.user.create({
data: { email: 'user@example.com' },
})
await tx.profile.create({
data: { userId: user.id, bio: '' },
})
})Schema Conventions
- Always add
@@indexfor fields used inwherefilters - Always specify
onDeleteon foreign key relations - Use
cuid()for primary keys - New models should follow the same naming and field conventions as existing core models
model Post {
id String @id @default(cuid())
title String
slug String @unique
status String @default("draft")
authorId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
author AdminUser @relation(fields: [authorId], references: [id], onDelete: Cascade)
@@index([status])
@@index([authorId])
}Seeding
The seed script (prisma/seed.ts) creates the default superuser and sample products:
npm run db:seedEdit prisma/seed.ts to add your own initial data.
Prisma Studio
Visual browser for your database:
npm run db:studio
# Open http://localhost:5555Troubleshooting
| Problem | Fix |
|---|---|
| Connection refused | Ensure the database server is running |
| Migration drift | Run npx prisma migrate reset |
| Type errors after schema change | Run npm run db:generate |
Unique constraint error (P2002) | Check for duplicate values before insert |
Next Steps
- Authentication — user and admin user models
- Payments — order and product models
- Content Management — post, category, tag models
Transactional email with Mailgun and Resend dual-service support — verification codes, order notifications, and pre-built HTML templates.
Internationalization
Full multi-language support with next-intl — English, Chinese, and Spanish out of the box, auto language detection, runtime switcher, and an i18n consistency check script.