Migration storage
Filesystem Storage
Store migrations as files on the local filesystem (default storage driver)
Quick Start
import { createMigrationClient } from "viborm/migrations";
import { createFsStorageDriver } from "viborm/migrations/storage/fs";
const storage = createFsStorageDriver("./migrations");
const migrations = createMigrationClient(client, {
storageDriver: storage,
});Tree-Shaking
The filesystem storage driver is imported separately from viborm/migrations/storage/fs to enable tree-shaking. This allows edge environments (like Cloudflare Workers) to use push() without bundling node:fs.
Configuration
const storage = createFsStorageDriver(baseDir);| Parameter | Type | Description |
|---|---|---|
baseDir | string | Base directory for migration files |
The baseDir should be an absolute path or relative to the current working directory.
Characteristics
| Property | Value |
|---|---|
| Storage | Local filesystem |
| Persistence | Permanent (until deleted) |
| Version control | Yes (commit to git) |
| Team collaboration | Via git |
| Performance | Fast (local I/O) |
Directory Structure
migrations/ # baseDir
├── 0000_initial.sql # First migration
├── 0001_add-users.sql # Second migration
├── 0002_add-posts.sql # Third migration
└── meta/ # Metadata directory
├── _journal.json # Migration index
├── _snapshot.json # Current schema snapshot
├── _down/ # Down migrations
│ ├── 0000_initial.sql
│ ├── 0001_add-users.sql
│ └── 0002_add-posts.sql
├── _backup/ # Backups before modification
└── _archive/ # Archived (squashed) migrationsFile Formats
Migration Files
SQL files with statements separated by semicolons:
-- 0001_add-users.sql
CREATE TABLE "users" (
"id" serial PRIMARY KEY,
"email" text NOT NULL UNIQUE,
"name" text,
"created_at" timestamp DEFAULT NOW()
);
CREATE INDEX "users_email_idx" ON "users" ("email");Journal File
JSON file tracking all migrations:
{
"version": "1",
"dialect": "postgresql",
"entries": [
{
"idx": 0,
"version": "20240115120000",
"name": "initial",
"when": 1705320000000,
"checksum": "sha256:abc123..."
},
{
"idx": 1,
"version": "20240116140000",
"name": "add-users",
"when": 1705413600000,
"checksum": "sha256:def456..."
}
]
}Snapshot File
JSON representation of the current schema:
{
"tables": [
{
"name": "users",
"columns": [
{ "name": "id", "type": "serial", "nullable": false },
{ "name": "email", "type": "text", "nullable": false }
],
"primaryKey": { "columns": ["id"] },
"indexes": [],
"foreignKeys": [],
"uniqueConstraints": []
}
],
"enums": []
}Use Cases
Local Development
import { createMigrationClient } from "viborm/migrations";
import { createFsStorageDriver } from "viborm/migrations/storage/fs";
const migrations = createMigrationClient(client, {
storageDriver: createFsStorageDriver("./migrations"),
});
// Generate migrations as you develop
await migrations.generate({ name: "add-feature" });
// Apply to local database
await migrations.apply();CI/CD Pipelines
# .github/workflows/migrate.yml
- name: Apply migrations
run: |
npx ts-node scripts/migrate.ts// scripts/migrate.ts
import { createMigrationClient } from "viborm/migrations";
import { createFsStorageDriver } from "viborm/migrations/storage/fs";
const migrations = createMigrationClient(client, {
storageDriver: createFsStorageDriver("./migrations"),
});
const pending = await migrations.pending();
if (pending.length > 0) {
console.log(`Applying ${pending.length} migrations...`);
await migrations.apply();
}Version Control
Migration files are designed to be committed to git:
# .gitignore - don't ignore migrations!
# migrations/ # DO NOT ADD THIS
# Commit migrations
git add migrations/
git commit -m "Add user table migration"Error Handling
The filesystem driver handles common scenarios:
- Missing directory: Creates directories automatically on
put() - Missing file: Returns
nullonget(), no-op ondelete() - Permission errors: Throws standard Node.js errors
try {
await migrations.apply();
} catch (error) {
if (error.code === "EACCES") {
console.error("Permission denied - check file permissions");
}
}