Skip to content
VibORM
Esc
navigateopen⌘Jpreview
On this page

Storage Drivers

Abstract storage for migration files, journals, and snapshots

What Storage Drivers Do

  • Store migration SQL files - Up and down migrations
  • Store the journal - Index of all migrations with checksums
  • Store schema snapshots - Current schema state for diffing
  • Handle backups - Archive migrations before modification

Available Drivers

Driver Storage Best For
Filesystem Local files Development, CI/CD, version control

Abstract Interface

All storage drivers implement three core methods:

abstract class MigrationStorageDriver {
  abstract get(path: string): Promise<string | null>;
  abstract put(path: string, content: string): Promise<void>;
  abstract delete(path: string): Promise<void>;
}
Method Description
get(path) Read content from path, returns null if not found
put(path, content) Write content to path, creates directories as needed
delete(path) Delete file at path, no-op if not found

The base class builds everything else (journal, snapshot, migration, and backup handling) on top of these three methods, so a custom driver only needs to implement them.

Directory Structure

The filesystem driver uses this structure:

migrations/
├── 0000_initial.sql              # Up migrations
├── 0001_add-users.sql
├── 0002_add-posts.sql
└── meta/
    ├── _journal.json             # Migration index
    ├── _snapshot.json            # Current schema
    ├── _down/                    # Down migrations
    │   ├── 0000_initial.sql
    │   ├── 0001_add-users.sql
    │   └── 0002_add-posts.sql
    ├── _backup/                  # Backups before modification
    └── _archive/                 # Archived migrations

Custom Storage Drivers

To implement a custom storage driver, extend MigrationStorageDriver:

import { MigrationStorageDriver } from "viborm/migrations";

class S3StorageDriver extends MigrationStorageDriver {
  constructor(
    private bucket: string,
    private prefix: string = "migrations"
  ) {
    super("s3");
  }

  async get(path: string): Promise<string | null> {
    const key = `${this.prefix}/${path}`;
    try {
      const response = await s3.getObject({ Bucket: this.bucket, Key: key });
      return response.Body?.toString("utf-8") ?? null;
    } catch (e) {
      if (e.name === "NoSuchKey") return null;
      throw e;
    }
  }

  async put(path: string, content: string): Promise<void> {
    const key = `${this.prefix}/${path}`;
    await s3.putObject({
      Bucket: this.bucket,
      Key: key,
      Body: content,
      ContentType: "text/plain",
    });
  }

  async delete(path: string): Promise<void> {
    const key = `${this.prefix}/${path}`;
    try {
      await s3.deleteObject({ Bucket: this.bucket, Key: key });
    } catch (e) {
      if (e.name !== "NoSuchKey") throw e;
    }
  }
}

Use your custom driver:

const storage = new S3StorageDriver("my-bucket", "migrations");

const migrations = createMigrationClient(client, {
  storageDriver: storage,
});

Next Steps

Was this page helpful?