npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hodfords/typeorm-migrations

v1.1.2

Published

TypeORM migrations

Downloads

1,996

Readme

Description

@hodfords/typeorm-migrations wraps TypeORM's QueryRunner and schema-builder classes in a small, readable API so migrations are easier to write and review. Instead of constructing Table, TableColumn, TableIndex and TableForeignKey objects by hand, you describe your schema with chainable methods:

await this.create('User', (table) => {
    table.primaryUuid('id');
    table.string('email').index();
    table.integer('age').nullable();
    table.uuid('enterpriseId').index().foreign('Enterprise');
    table.baseTime();
});

It is designed for PostgreSQL but works with any database TypeORM supports (a few helpers such as unsigned() or zerofill() are driver-specific and noted as such below).

Installation 🤖

npm install @hodfords/typeorm-migrations --save

typeorm is a peer dependency and must be installed in your project.

Usage

Extend BaseMigration instead of implementing MigrationInterface directly. Implement run() (executed on up) and optionally rollback() (executed on down):

import { BaseMigration } from '@hodfords/typeorm-migrations';
import { QueryRunner } from 'typeorm';

export class CreateUserTable1626749239046 extends BaseMigration {
    async run(queryRunner: QueryRunner): Promise<void> {
        await this.create('User', (table) => {
            table.primaryUuid('id');
            table.string('email').index();
            table.string('firstName').nullable();
            table.string('lastName').nullable();
            table.string('password').nullable();
            table.integer('role');
            table.string('language').length(10).default("'en'");
            table.timestamp('lastLoginAt').nullable();
            table.uuid('enterpriseId').nullable().index().foreign('Enterprise');
            table.createdAt().index();
            table.updatedAt();
            table.deletedAt();
        });
    }

    async rollback(queryRunner: QueryRunner): Promise<void> {
        await this.drop('User');
    }
}

Updating an existing table

update() lets you add columns, drop columns and index existing columns in one place:

export class UpdateUserTable1726749239046 extends BaseMigration {
    async run(queryRunner: QueryRunner): Promise<void> {
        await this.update('User', (table) => {
            table.string('phone').nullable();
            table.dropColumn('legacyColumn');
            table.addIndexAlreadyColumn('email');
        });
    }
}

Table methods

Inside create() and update() callbacks you receive a BaseTable. Every column method returns a BaseColumn, so you can chain the column modifiers below.

Primary keys

| Method | Description | | --- | --- | | id(name?) | Auto-incrementing integer primary key (default name id) | | increments(name) | Auto-incrementing integer primary key | | smallIncrements(name) | Auto-incrementing smallint primary key | | bigIncrements(name) | Auto-incrementing bigint primary key | | primaryUuid(name?) | uuid primary key with uuid_generate_v4() default | | primaryUuidV7(name?) | uuid primary key with uuidv7() default (Postgres 18+) |

Character types

| Method | Description | | --- | --- | | string(name, length = 255) | character varying | | varchar(name, length = 255) | Alias of string() | | char(name, length) | char | | text(name) | text | | citext(name) | citext (case-insensitive text) |

Numeric types

| Method | Description | | --- | --- | | integer(name) | integer | | smallint(name) | smallint | | bigint(name) | bigint | | decimal(name, precision = 10, scale = 2) | decimal | | numeric(name, precision = 10, scale = 2) | numeric | | money(name) | money | | real(name) / float4(name) | real | | double(name) / doublePrecision(name) / float(name) / float8(name) | double precision |

Date and time types

| Method | Description | | --- | --- | | timestamp(name) | timestamp | | timestamptz(name) | timestamp with time zone | | date(name) | date | | time(name) | time | | timetz(name) | time with time zone | | interval(name) | interval | | createdAt() | timestamp named createdAt, defaults to now() | | updatedAt() | timestamp named updatedAt, defaults to now() | | deletedAt() | Nullable timestamp named deletedAt | | baseTime() | Shortcut for createdAt() + updatedAt() |

JSON, UUID and other types

| Method | Description | | --- | --- | | uuid(name = 'id') | uuid | | json(name) / jsonb(name) | json / jsonb | | boolean(name) | boolean | | enum(name, enumName, values) | enum — accepts a string array or a Record<string, string> (e.g. a TS enum) | | bytea(name) | bytea | | bit(name, length = 1) / varbit(name, length?) | bit / bit varying | | xml(name) | xml | | hstore(name) | hstore | | tsvector(name) / tsquery(name) | Full-text search types | | cube(name) | cube | | ltree(name) | ltree | | column(name, type, options?) | Escape hatch — any type supported by your database driver |

Array types

| Method | Description | | --- | --- | | strings(name) | character varying[] | | texts(name) | text[] | | uuids(name) | uuid[] | | integers(name) | integer[] | | smallints(name) / bigints(name) | smallint[] / bigint[] | | booleans(name) | boolean[] | | jsonbs(name) | jsonb[] |

Any column can also be made an array with the .array() modifier.

Network and geometric types

| Method | Description | | --- | --- | | inet(name) / cidr(name) | IP address types | | macaddr(name) / macaddr8(name) | MAC address types | | point(name), line(name), lseg(name), box(name), path(name), polygon(name), circle(name) | Geometric types | | geography(name, spatialFeatureType, srid = 4326) | PostGIS geography | | geometry(name, spatialFeatureType, srid = 0) | PostGIS geometry |

Range types

| Method | Description | | --- | --- | | int4range(name) / int8range(name) | Integer ranges | | numrange(name) | Numeric range | | tsrange(name) / tstzrange(name) | Timestamp ranges | | daterange(name) | Date range |

Other table helpers

| Method | Description | | --- | --- | | dropColumn(name) | Drop a column (only meaningful inside update()) | | addIndexAlreadyColumn(name) | Create an index on a column that already exists |

Every column method also accepts a final options argument (Partial<TableColumnOptions>) that is merged into the underlying TypeORM column definition:

table.string('email', 255, { isUnique: true, comment: 'login email' });

Column modifiers

Methods on BaseColumn are chainable and cover all of TypeORM's TableColumnOptions:

| Modifier | Description | | --- | --- | | nullable() / notNullable() | Set NULL / NOT NULL | | unique() | Add a unique constraint | | index() | Create an index for this column | | primary(constraintName?) | Mark as primary key | | default(value) | Set the default value (e.g. default("'en'")) | | useCurrent() | Default to now() | | length(length) | Set the column length | | precision(precision, scale?) / scale(scale) | Numeric precision and scale | | array() | Store an array of the column type | | comment(text) | Column comment | | collation(collation) | Column collation | | charset(charset) | Column character set | | enum(values, enumName?) | Enum values and the exact enum type name | | autoIncrement() | Auto-increment generation | | generated(strategy?) | Generation strategy: 'increment', 'uuid', 'rowid' or 'identity' | | generatedIdentity(value?) | Identity column, 'ALWAYS' or 'BY DEFAULT' (Postgres 10+) | | asExpression(expression, type?) | Generated (computed) column, 'STORED' or 'VIRTUAL' | | spatial(featureType, srid?) | Spatial feature type and SRID | | width(width) | Display width (MySQL only) | | unsigned() / zerofill() | Numeric attributes (MySQL only) | | onUpdate(value) | ON UPDATE trigger (MySQL only) | | foreign(table, column = 'id', onDelete = 'CASCADE', onUpdate = 'CASCADE', name?) | Add a foreign key referencing another table |

Example:

table.decimal('price', 12, 4).notNullable().default(0).comment('unit price');
table.uuid('ownerId').index().foreign('User', 'id', 'SET NULL');
table.string('fullName').asExpression('"firstName" || \' \' || "lastName"');

Migration methods

BaseMigration exposes the full QueryRunner surface as small helpers. All of them can be called from run() and rollback().

Tables

| Method | Description | | --- | --- | | create(tableName, callback) | Create a table, its indexes and foreign keys | | update(tableName, callback) | Add/drop columns and create indexes/foreign keys on an existing table | | drop(table) | Drop a table | | dropIfExists(table) | Drop a table only if it exists | | rename(oldTable, newTableName) | Rename a table | | hasTable(tableName) | Check whether a table exists | | getTable(tableName) | Load the full Table definition | | clearTable(tableName) | Truncate a table | | changeTableComment(table, comment?) | Change the table comment |

Columns

| Method | Description | | --- | --- | | hasColumn(tableName, columnName) | Check whether a column exists | | addColumn(table, column) / addColumns(table, columns) | Add raw TableColumns | | renameColumn(table, oldName, newName) | Rename a column | | changeColumn(table, oldColumn, newColumn) / changeColumns(table, changes) | Change column definitions | | dropColumn(table, columnName) / dropColumns(table, columnNames) | Drop columns |

Keys, constraints and indexes

| Method | Description | | --- | --- | | createPrimaryKey(table, columnNames, constraintName?) | Create a primary key | | updatePrimaryKeys(table, columns) | Update primary keys | | dropPrimaryKey(table, constraintName?) | Drop the primary key | | createUnique(table, columnName, name?) | Create a unique constraint (name defaults to `${table}-${column}Unique`) | | dropUnique(table, uniqueOrName) | Drop a unique constraint | | dropUniqueColumn(table, columnName) | Drop a unique constraint by its generated name | | createCheck(table, expression, name?) | Create a check constraint | | dropCheck(table, checkOrName) | Drop a check constraint | | createExclusion(table, expression, name?) | Create an exclusion constraint (Postgres) | | dropExclusion(table, exclusionOrName) | Drop an exclusion constraint | | addIndex(tableName, columnNames, options?) | Create an index (name defaults to `${table}-${columns}Index`) | | dropIndex(table, indexOrName) / dropIndices(table, indices) | Drop indexes | | addForeign(tableName, columnName, referencedTable, referencedColumn = 'id', onDelete = 'CASCADE', onUpdate = 'CASCADE') | Create a foreign key | | dropForeign(table, foreignKeyOrName) | Drop a foreign key |

Views, schemas and databases

| Method | Description | | --- | --- | | createView(name, expression, materialized = false) | Create a (materialized) view | | dropView(viewOrName) | Drop a view | | createSchema(schemaPath, ifNotExist = true) | Create a schema | | dropSchema(schemaPath, ifExist = true, isCascade = false) | Drop a schema | | hasSchema(schema) | Check whether a schema exists | | createDatabase(database, ifNotExist = true) | Create a database | | dropDatabase(database, ifExist = true) | Drop a database | | hasDatabase(database) | Check whether a database exists |

Raw queries

await this.query('UPDATE "User" SET "role" = $1 WHERE "role" IS NULL', [1]);

You also always have direct access to the QueryRunner passed into run()/rollback() for anything not covered above.

Testing

npm test

The test suite covers BaseTable, BaseColumn and BaseMigration against a mocked QueryRunner, so it does not require a database.

License 📝

This project is licensed under the MIT License