skills/db-sqlite/SKILL.md
SQLite database management with Prisma ORM, type-safe queries, and Railway deployment with Litestream backup. This skill should be used when creating database schemas, writing migrations, managing SQLite on Railway volumes, or troubleshooting database issues.
npx skillsauth add aussiegingersnap/cursor-skills db-sqliteInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Comprehensive patterns for SQLite database management in Node.js/TypeScript projects using Prisma ORM, including schema-first development and Railway deployment with Litestream backup.
prisma migrateSQLite is appropriate when:
Consider PostgreSQL when:
SQLite on Railway requires understanding these constraints:
This is the #1 cause of data loss on Railway SQLite deployments.
Railway volumes mount at a specific path (e.g., /data). But your app runs in /app/ by default. If your code writes to ./prisma/app.db, it creates the file at /app/prisma/app.db — which is NOT on the volume and gets destroyed on every deploy.
Solution: Set DATABASE_URL to use the volume path in production.
# Wrong (data lost on each deploy):
file:/app/prisma/app.db ← Container filesystem, not persistent
# Correct (data persists):
file:/data/app.db ← Railway volume, persistent + backed up
npm install @prisma/client
npm install -D prisma
prisma/
├── schema.prisma # Database schema (source of truth)
└── migrations/ # Generated SQL migrations
src/lib/db/
└── index.ts # Prisma client singleton
# .env.local (development)
DATABASE_URL="file:./prisma/dev.db"
# Railway (production) — REQUIRED
# Must point to volume mount path
DATABASE_URL="file:/data/app.db"
Create prisma/schema.prisma:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
// Define your models here
model User {
id String @id @default(cuid())
email String @unique
name String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
Create src/lib/db/index.ts:
import { PrismaClient } from '@prisma/client'
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined
}
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 default prisma
{
"scripts": {
"db:generate": "prisma generate",
"db:migrate": "prisma migrate dev",
"db:migrate:prod": "prisma migrate deploy",
"db:push": "prisma db push",
"db:studio": "prisma studio",
"db:reset": "prisma migrate reset"
}
}
Use CUID or UUID for user-facing entities:
model User {
id String @id @default(cuid())
// or: id String @id @default(uuid())
}
Always include created/updated timestamps:
model Post {
id String @id @default(cuid())
title String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
One-to-many:
model User {
id String @id @default(cuid())
posts Post[]
}
model Post {
id String @id @default(cuid())
authorId String
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
}
Many-to-many (explicit junction table):
model Post {
id String @id @default(cuid())
tags PostTag[]
}
model Tag {
id String @id @default(cuid())
name String @unique
posts PostTag[]
}
model PostTag {
postId String
tagId String
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
@@id([postId, tagId])
}
enum Role {
ADMIN
USER
GUEST
}
model User {
id String @id @default(cuid())
role Role @default(USER)
}
model Post {
id String @id @default(cuid())
authorId String
published Boolean @default(false)
createdAt DateTime @default(now())
@@index([authorId])
@@index([published, createdAt])
}
model BookCollaborator {
id String @id @default(cuid())
bookId String
userId String
@@unique([bookId, userId])
}
# Make schema changes in prisma/schema.prisma
# Create and apply migration
npm run db:migrate
# Prompts for migration name, e.g., "add_posts_table"
# Quick iteration (no migration file, just sync)
npm run db:push
# Apply pending migrations (run in CI/CD or startup)
npm run db:migrate:prod
add_user_avatar, create_posts_tableprisma/migrations/ before deployingimport { prisma } from '@/lib/db'
// Create
const user = await prisma.user.create({
data: {
email: '[email protected]',
name: 'John Doe',
},
})
// Read one
const user = await prisma.user.findUnique({
where: { email: '[email protected]' },
})
// Read many with filters
const users = await prisma.user.findMany({
where: {
role: 'ADMIN',
createdAt: { gte: new Date('2024-01-01') },
},
orderBy: { createdAt: 'desc' },
take: 10,
})
// Update
const updated = await prisma.user.update({
where: { id: userId },
data: { name: 'Jane Doe' },
})
// Delete
await prisma.user.delete({
where: { id: userId },
})
// Include related data
const userWithPosts = await prisma.user.findUnique({
where: { id: userId },
include: {
posts: {
where: { published: true },
orderBy: { createdAt: 'desc' },
},
},
})
// Select specific fields
const userEmail = await prisma.user.findUnique({
where: { id: userId },
select: {
email: true,
posts: {
select: { title: true },
},
},
})
// Nested create
const userWithPost = await prisma.user.create({
data: {
email: '[email protected]',
posts: {
create: {
title: 'My First Post',
},
},
},
include: { posts: true },
})
// Sequential operations
const [user, post] = await prisma.$transaction([
prisma.user.create({ data: { email: '[email protected]' } }),
prisma.post.create({ data: { title: 'Hello', authorId: 'temp' } }),
])
// Interactive transaction
await prisma.$transaction(async (tx) => {
const user = await tx.user.create({
data: { email: '[email protected]' },
})
await tx.post.create({
data: {
title: 'My Post',
authorId: user.id,
},
})
})
const user = await prisma.user.upsert({
where: { email: '[email protected]' },
update: { name: 'Updated Name' },
create: { email: '[email protected]', name: 'New User' },
})
Required Railway configuration:
DATABASE_URL=file:/data/app.db
Ensure volume is mounted at /data.
# Open interactive shell in Railway container
railway shell
# Inside container - use Prisma Studio (opens web UI)
npx prisma studio
# Or use sqlite3 directly
sqlite3 /data/app.db
.tables
.schema User
SELECT * FROM User LIMIT 5;
.quit
railway shell
npx prisma migrate status
Litestream provides real-time SQLite replication to S3-compatible storage. Combined with Railway Buckets, this gives you continuous backups without external providers.
See references/litestream.md for complete setup guide.
# 1. Create Railway Bucket
railway add --service bucket
# 2. Add litestream.yml to project root
# 3. Update nixpacks.toml to install litestream
# 4. Update railway.toml start command
# 5. Add restore script for empty volumes
Generate the client after schema changes:
npm run db:generate
Check migration status and pending migrations:
npx prisma migrate status
For stuck migrations, you may need to mark as applied or reset:
# Mark a migration as applied (use with caution)
npx prisma migrate resolve --applied <migration_name>
# Reset database (development only)
npx prisma migrate reset
SQLite allows only one writer at a time. Solutions:
references/litestream.md for restore proceduresreferences/boilerplate.md - Complete Prisma setup codereferences/migrations.md - Migration patterns and examplesreferences/litestream.md - Continuous backup setup with Railway Bucketstools
# Versioning Skill Semantic versioning automation based on conventional commits. Automatically manages version bumps, changelogs, and git tags using `standard-version`. ## When to Use - Before releasing a new version - When preparing a deployment - To generate/update CHANGELOG.md - When the user asks about version management - Setting up versioning for a new project ## Prerequisites - Conventional commits enforced (recommended: lefthook) - Node.js project with package.json ## Setup (One-Ti
tools
Theme generation with tweakcn for shadcn/ui and Magic UI animations. Use when setting up project themes, customizing color schemes, adding dark mode, or integrating animated components.
tools
shadcn/studio component library with MCP integration, theme generation, and block patterns. This skill should be used when building UI with shadcn components, selecting dashboard layouts, or generating landing pages. Canonical source for all shadcn-based work.
development
Enforce a precise, minimal design system inspired by Linear, Notion, and Stripe. Use this skill when building dashboards, admin interfaces, or any UI that needs Jony Ive-level precision - clean, modern, minimalist with taste. Every pixel matters.