Skip to content
Back to skills

Db Sqlite

ASecurity

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.

  • 666 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added February 8, 2026
databasestypescriptshellbashsqlnodedebuggingdatabaseci/cddocumentation

Works with

  • cli

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned February 12, 2026

npx -y skills add majiayu000/claude-skill-registry --skill db-sqlite --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Db Sqlite?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Db Sqlite
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-db-sqlite/badge)](https://www.skillsdirectory.com/skills/majiayu000-db-sqlite)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: db-sqlite
description: 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.
---

# SQLite Database Skill

Comprehensive patterns for SQLite database management in Node.js/TypeScript projects using Prisma ORM, including schema-first development and Railway deployment with Litestream backup.

## When to Use This Skill

- Setting up SQLite in a new project
- Defining database schemas with Prisma
- Running migrations with `prisma migrate`
- Deploying SQLite to Railway with persistent volumes
- Backing up production databases with Litestream
- Troubleshooting database issues

## Core Concepts

### SQLite vs Network Databases

SQLite is appropriate when:
- Single server/container deployment
- Read-heavy workloads (or moderate writes)
- Simplicity is valued over horizontal scaling
- Local-first or embedded scenarios
- Cost-sensitive deployments

Consider PostgreSQL when:
- Multiple servers need database access
- Remote database inspection is required
- High write concurrency is expected
- Team needs direct database access for debugging

### Railway Deployment Constraints

SQLite on Railway requires understanding these constraints:

1. **Volume mounting** - Database file must live on a Railway volume (not container filesystem)
2. **No remote access** - Cannot connect database GUI tools directly to production
3. **Single container** - Only one instance can write to the database
4. **Backup strategy** - Use Litestream for continuous backup to Railway Bucket

### Critical: Railway Volume Path vs Container Path

**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
```

## Database Setup Pattern

### Package Installation

```bash
npm install @prisma/client
npm install -D prisma
```

### Directory Structure

```
prisma/
├── schema.prisma    # Database schema (source of truth)
└── migrations/      # Generated SQL migrations

src/lib/db/
└── index.ts         # Prisma client singleton
```

### Environment Configuration

```bash
# .env.local (development)
DATABASE_URL="file:./prisma/dev.db"

# Railway (production) — REQUIRED
# Must point to volume mount path
DATABASE_URL="file:/data/app.db"
```

### Prisma Schema Setup

Create `prisma/schema.prisma`:

```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
}
```

### Prisma Client Singleton

Create `src/lib/db/index.ts`:

```typescript
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
```

### Package.json Scripts

```json
{
  "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"
  }
}
```

## Schema Patterns

### Primary Keys

Use CUID or UUID for user-facing entities:

```prisma
model User {
  id String @id @default(cuid())
  // or: id String @id @default(uuid())
}
```

### Timestamps

Always include created/updated timestamps:

```prisma
model Post {
  id        String   @id @default(cuid())
  title     String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}
```

### Relations

One-to-many:

```prisma
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):

```prisma
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])
}
```

### Enums

```prisma
enum Role {
  ADMIN
  USER
  GUEST
}

model User {
  id   String @id @default(cuid())
  role Role   @default(USER)
}
```

### Indexes

```prisma
model Post {
  id        String   @id @default(cuid())
  authorId  String
  published Boolean  @default(false)
  createdAt DateTime @default(now())

  @@index([authorId])
  @@index([published, createdAt])
}
```

### Unique Constraints

```prisma
model BookCollaborator {
  id     String @id @default(cuid())
  bookId String
  userId String

  @@unique([bookId, userId])
}
```

## Migration Workflow

### Development

```bash
# 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
```

### Production

```bash
# Apply pending migrations (run in CI/CD or startup)
npm run db:migrate:prod
```

### Migration Best Practices

1. **Never edit applied migrations** - They may have run in production
2. **Name migrations descriptively** - `add_user_avatar`, `create_posts_table`
3. **Review generated SQL** - Check `prisma/migrations/` before deploying
4. **Use db:push for prototyping** - Switch to migrations when schema stabilizes
5. **Commit migrations** - They're part of your codebase

## Query Patterns

### Basic CRUD

```typescript
import { prisma } from '@/lib/db'

// Create
const user = await prisma.user.create({
  data: {
    email: 'user@example.com',
    name: 'John Doe',
  },
})

// Read one
const user = await prisma.user.findUnique({
  where: { email: 'user@example.com' },
})

// 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 },
})
```

### With Relations

```typescript
// 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: 'author@example.com',
    posts: {
      create: {
        title: 'My First Post',
      },
    },
  },
  include: { posts: true },
})
```

### Transactions

```typescript
// Sequential operations
const [user, post] = await prisma.$transaction([
  prisma.user.create({ data: { email: 'user@example.com' } }),
  prisma.post.create({ data: { title: 'Hello', authorId: 'temp' } }),
])

// Interactive transaction
await prisma.$transaction(async (tx) => {
  const user = await tx.user.create({
    data: { email: 'user@example.com' },
  })
  
  await tx.post.create({
    data: {
      title: 'My Post',
      authorId: user.id,
    },
  })
})
```

### Upsert

```typescript
const user = await prisma.user.upsert({
  where: { email: 'user@example.com' },
  update: { name: 'Updated Name' },
  create: { email: 'user@example.com', name: 'New User' },
})
```

## Railway Operations

### Environment Variables

Required Railway configuration:

```
DATABASE_URL=file:/data/app.db
```

Ensure volume is mounted at `/data`.

### Shell Access

```bash
# 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
```

### Check Migration Status

```bash
railway shell
npx prisma migrate status
```

## Continuous Backup with Litestream

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.**

### Quick Overview

1. Litestream monitors SQLite WAL changes
2. Streams changes to Railway Bucket every ~10 seconds
3. On container restart, restores from bucket if local DB is missing
4. ~10 second recovery point objective (RPO)

### Minimal Setup

```bash
# 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
```

## Troubleshooting

### "Cannot find module '@prisma/client'"

Generate the client after schema changes:
```bash
npm run db:generate
```

### "Migration failed"

Check migration status and pending migrations:
```bash
npx prisma migrate status
```

For stuck migrations, you may need to mark as applied or reset:
```bash
# Mark a migration as applied (use with caution)
npx prisma migrate resolve --applied <migration_name>

# Reset database (development only)
npx prisma migrate reset
```

### "Database is locked"

SQLite allows only one writer at a time. Solutions:
1. Keep transactions short
2. Avoid long-running queries during writes
3. Use connection pooling sparingly (usually singleton is fine)

### Production Database Issues

1. Check Litestream backup status
2. Restore from backup if needed
3. Review `references/litestream.md` for restore procedures

## References

- `references/boilerplate.md` - Complete Prisma setup code
- `references/migrations.md` - Migration patterns and examples
- `references/litestream.md` - Continuous backup setup with Railway Buckets
- [Prisma Docs](https://www.prisma.io/docs) - Official documentation
- [Railway SQLite Guide](https://docs.railway.com/guides/sqlite) - Railway-specific patterns

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.