JCC Express

Database

Migrations

Introduction

Migrations are version-controlled schema changes for your database. In JCC Express MVC, migrations are generated and executed through artisanNode.

Migration files live in database/migrations.


Generate a migration

Bash
bun artisanNode make:migration create_users_table

This creates a timestamped migration file in database/migrations.

You can also generate queue-related tables:

Bash
bun artisanNode make:queue-table

Typical migration structure

A migration defines schema changes in up/down style methods (depending on your migration template in this project).

Common operations include:

  • creating tables
  • adding/removing columns
  • indexes/constraints
  • dropping tables

Keep each migration focused on one schema change.


Migration schema examples

Create table schema

TypeScript
import { Schema } from "jcc-express-mvc/Eloquent";

export class Migration {
  up() {
    return Schema.create("posts", (table) => {
      table.id();
      table.string("title");
      table.string("slug").unique();
      table.text("excerpt").nullable();
      table.longText("content");
      table.boolean("is_published", false);
      table.integer("sort_order").unsigned().default(0);
      table.json("meta").nullable();
      table.date("published_at").nullable();
      table.timestamps();
      table.softDeletes();
    });
  }

  down() {
    return Schema.dropTable("posts");
  }
}

Foreign keys and composite unique

TypeScript
import { Schema } from "jcc-express-mvc/Eloquent";

export class Migration {
  up() {
    return Schema.create("post_comments", (table) => {
      table.id();
      table.foreignId("post_id").references("id").on("posts");
      table.foreignId("user_id").references("id").on("users");
      table.text("body");
      table.timestamps();

      table.compositeUnique(
        ["post_id", "user_id", "created_at"],
        "uk_post_comments_post_user_created"
      );
    });
  }

  down() {
    return Schema.dropTable("post_comments");
  }
}

Alter existing table

TypeScript
import { Schema } from "jcc-express-mvc/Eloquent";

export class Migration {
  up() {
    return Schema.table("users", (table) => {
      table.string("phone").nullable();
      table.boolean("is_verified", false);
    });
  }

  down() {
    return Schema.table("users", (table) => {
      table.dropColumns("phone", "is_verified");
    });
  }
}

Common schema helpers seen in the current codebase:

  • id()
  • string()
  • text()
  • longText()
  • integer(), smallInteger(), unsignedBigInteger()
  • boolean()
  • json()
  • date()
  • timestamps()
  • softDeletes()
  • foreignId(...).references(...).on(...)
  • foreign(...).references(...).on(...)
  • compositeUnique(columns, indexName)

Full migration API

Schema methods

  • create(tableName, callback)
  • table(tableName, callback)
  • dropTable(tableName)
  • dropIfExists(tableName)
  • truncate(tableName)
  • drop(tableName)
  • rename(from, to)
  • hasTable(tableName)
  • hasColumn(tableName, columnName)

Blueprint column types

  • id()
  • unsignedBigInteger(column)
  • string(column, length?)
  • integer(column)
  • bigInteger(column)
  • tinyInteger(column, size?)
  • smallInteger(column)
  • mediumInteger(column)
  • unsignedSmallInteger(column)
  • unsignedMediumInteger(column)
  • unsignedInteger(column)
  • unsignedTinyInteger(column)
  • char(column, length?)
  • text(column)
  • mediumText(column)
  • longText(column)
  • tinyText(column)
  • binary(column, size?)
  • blob(column)
  • tinyBlob(column)
  • mediumBlob(column)
  • longBlob(column)
  • dateTime(column)
  • date(column)
  • timestamp(column)
  • time(column)
  • year(column)
  • enum(column, values?)
  • boolean(column, defaultValue?)
  • float(column, total?, places?)
  • double(column, total?, places?)
  • decimal(column, total?, places?)
  • json(column)
  • jsonb(column)
  • uuid(column)
  • set(column, values?)
  • foreignId(column)
  • uuidPrimaryKey(column?)
  • ulid(column)
  • ulidPrimaryKey(column?)
  • ipAddress(column)
  • macAddress(column)
  • geometry(column)
  • point(column)
  • polygon(column)
  • lineString(column)

Blueprint relationships and morphs

  • foreign(column)
  • references(column)
  • on(table)
  • foreignIdFor(model, column?, table?)
  • constrained(table, column?)
  • morphs(column)
  • nullableMorphs(column)
  • uuidMorphs(column)
  • nullableUuidMorphs(column)
  • dropMorphs(column)

Blueprint modifiers and column controls

  • default(value)
  • nullable()
  • unique()
  • change()
  • after(column)
  • first()
  • autoIncrement()
  • unsigned()
  • useCurrent()
  • useCurrentOnUpdate()
  • storedAs(expression)
  • virtualAs(expression)
  • charset(charset)
  • collation(collation)
  • check(expression)
  • renameColumn(from, to)
  • dropColumns(...columns)

Blueprint indexes and constraints

  • index(indexNameOrColumns?)
  • dropIndex(indexName)
  • dropUnique(indexName)
  • dropForeign(indexName)
  • dropPrimary()
  • fulltext(columns)
  • spatialIndex(columns)
  • compositeIndex(columns, indexName?)
  • compositeUnique(columns, indexName?)

Blueprint timestamps, soft deletes, and token helpers

  • timestamps(createdAt?, updatedAt?)
  • nullableTimestamps()
  • softDeletes(column?)
  • dropTimestamps()
  • dropSoftDeletes(column?)
  • rememberToken()
  • dropRememberToken()

Blueprint table-level options

  • engine(engine)
  • tableCharset(charset)
  • tableCollation(collation)
  • comment(comment)
  • temporary()
  • toSQL(actionType?, tableName)

Foreign key action helpers

  • cascade()
  • onDelete(action?)
  • onUpdate(action?)
  • cascadeOnDelete()
  • cascadeOnUpdate()

Run migrations

Bash
bun artisanNode migrate

Runs all pending migrations.


Rollback and reset

Bash
bun artisanNode migrate:rollback
bun artisanNode migrate:rollback step=2
bun artisanNode migrate:reset
bun artisanNode migrate:fresh
  • migrate:rollback -> rolls back the last batch (or given steps)
  • migrate:reset -> rolls back all migrations
  • migrate:fresh -> drops all tables, then reruns migrations

Best practices

  • Keep migrations small and reviewable.
  • Never edit old migrations that already ran in shared environments; add a new migration instead.
  • Test rollback paths for critical schema changes.
  • Run migrations in CI/CD before app starts serving traffic.

Summary

  • Generate with make:migration.
  • Apply with migrate.
  • Revert with rollback/reset/fresh commands.
  • Keep migration history append-only and environment-safe.