> ## Documentation Index
> Fetch the complete documentation index at: https://www.activepieces.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Database Migrations

> Guide for creating database migrations in Activepieces

Activepieces uses TypeORM as its database driver in Node.js. We support two database types across different editions of our platform.

The database migration files contain both what to do to migrate (up method) and what to do when rolling back (down method).

> [!TIP]
>
> Read more about TypeORM migrations here:
> [https://orkhan.gitbook.io/typeorm/docs/migrations](https://orkhan.gitbook.io/typeorm/docs/migrations)

## Database Support

* PostgreSQL
* PGlite

> [!TIP]
>
> **Why Do we have PGlite?**
> We support PGlite to simplify development and self-hosting. It's particularly helpful for:
>
> * Developers creating pieces who want a quick setup
> * Self-hosters using platforms to manage docker images but doesn't support docker compose.
>
> PGlite is a lightweight PostgreSQL implementation that runs embedded, so migrations are compatible with PostgreSQL.

## Editions

* **Enterprise & Cloud Edition** (Must use PostgreSQL)
* **Community Edition** (Can use PostgreSQL or PGlite)

### How To Generate

**1. Setup AP\_DB\_TYPE**

Set the `AP_DB_TYPE` environment variable to `POSTGRES` after making sure have latest state by running Activepieces first.

**2. Generate Migration**

Run the migration generation command:

```bash theme={null}
npx turbo run db-migration --filter=api -- --name=<MIGRATION_NAME>
```

Replace `<MIGRATION_NAME>` with a descriptive name for your migration.

**3. Update Migration File**

The command will generate a new migration file in `packages/server/api/src/app/database/migration/postgres/`.

The generated file uses `MigrationInterface` — you need to update it:

1. Change `implements MigrationInterface` to `implements Migration`
2. Update the import from `typeorm` to import `Migration` from `../../migration`
3. Add `breaking = false` (or `true` if the migration drops columns/tables or transforms data irreversibly)
4. Add `release = '<version>'` matching the upcoming release version (check `package.json` in the repo root)
5. Implement the `down()` method with queries that reverse the `up()` changes (unless `breaking = true`)
6. Register it in `postgres-connection.ts`

```typescript theme={null}
import { QueryRunner } from 'typeorm'
import { Migration } from '../../migration'

export class AddMyColumn1234567890 implements Migration {
    name = 'AddMyColumn1234567890'
    breaking = false
    release = '0.78.0'

    public async up(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`ALTER TABLE "project" ADD COLUMN "description" text`)
    }

    public async down(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`ALTER TABLE "project" DROP COLUMN "description"`)
    }
}
```

CI will fail if `breaking`, `release`, or `down()` are missing on new migrations.

## PGlite Compatibility

While PGlite is mostly PostgreSQL-compatible, some features are not supported. When using features like `CONCURRENTLY` for index operations, you need to conditionally handle PGlite:

```typescript theme={null}
import { QueryRunner } from 'typeorm'
import { Migration } from '../../migration'
import { system } from '../../../helper/system/system'
import { AppSystemProp } from '../../../helper/system/system-props'
import { DatabaseType } from '../../database-type'

const databaseType = system.get(AppSystemProp.DB_TYPE)
const isPGlite = databaseType === DatabaseType.PGLITE

export class AddMyIndex1234567890 implements Migration {
    name = 'AddMyIndex1234567890'
    breaking = false
    release = '0.78.0'
    transaction = false // Required when using CONCURRENTLY

    public async up(queryRunner: QueryRunner): Promise<void> {
        if (isPGlite) {
            await queryRunner.query(`CREATE INDEX "idx_name" ON "table" ("column")`)
        } else {
            await queryRunner.query(`CREATE INDEX CONCURRENTLY "idx_name" ON "table" ("column")`)
        }
    }

    public async down(queryRunner: QueryRunner): Promise<void> {
        if (isPGlite) {
            await queryRunner.query(`DROP INDEX "idx_name"`)
        } else {
            await queryRunner.query(`DROP INDEX CONCURRENTLY "idx_name"`)
        }
    }
}
```

> [!WARNING]
>
> `CREATE INDEX CONCURRENTLY` and `DROP INDEX CONCURRENTLY` are not supported in PGlite because PGLite is a single user/connection database. Always add a check for PGlite when using these operations.

> [!TIP]
>
> Always test your migrations by running them both up and down to ensure they work as expected.

## Cleaning Up After a Background Migration

A background migration often leaves temporary code behind: a fallback read from the old shape, a dual write, a legacy column or index, special handling until the backfill finishes. Plan its removal when the migration ships, not later.

**1. Open a Linear ticket when the migration merges**

Create a ticket assigned to the migration's owner (the author of the PR that adds it). Name the migration and the target releases in the title, for example "Remove the `piece_metadata.i18n` fallback after `BackfillPieceTranslation` (code 0.97.0, column 0.98.0)".

In the body, list every piece of code and schema that becomes removable (symbol and file), link the migration PR, and copy in the two steps below. Link the ticket from the migration PR's description.

**2. Remove the code in release N+4 or later**

N is the minor version in the migration's `release` field, so `release = '0.93.0'` means the code goes in `0.97.0` or later. We support the latest five releases, so once N+4 is the newest, every supported release (N to N+4) already ships the backfill. N+3 is too early, because N-1 is still supported then.

Ship code only. Remove the fallbacks and dual writes, take the field off the entity, and add a blocking migration whose `up()` only calls `migrationHelpers.assertBackgroundMigrationComplete(...)`. That guard makes an older install that skips releases run the backfill at boot before it serves traffic.

**3. Drop the schema in release N+5 or later**

Drop the column, table or index in release N+5 or later, and always at least one release after the code removal, in a blocking migration guarded the same way, with `breaking = true`. For `release = '0.93.0'` that is `0.98.0` or later. A dropped column is a technical breaking change: it needs the `⛓️‍💥 breaking-change` label and an entry in `docs/install/reference/breaking-changes-unreleased.mdx`.

> [!WARNING]
>
> Never remove the code and drop the schema in the same release. A Cloud rollback runs the previous image on the new schema and does not run `down()`, so the previous release must already work without the dropped object. If it still reads the old column, every request that touches it fails after the rollback.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.