Good Webhooks

PostgreSQL migrations

Fresh-install and upgrade SQL, component selection, custom schemas, and exported SQL assets.

good-webhooks/migrations generates the fresh-install and upgrade SQL for management and delivery tables; your migration tool applies it.

import {
  getPostgresMigration,
  getPostgresUpgrade,
  POSTGRES_SCHEMA_VERSION,
} from 'good-webhooks/migrations'
import type { PostgresMigrationOptions, PostgresUpgradeOptions } from 'good-webhooks/migrations'

The migrations guide covers applying, upgrading, and splitting storage.

getPostgresMigration

getPostgresMigration(options?: PostgresMigrationOptions): string
scripts/write-migration.ts
import { writeFile } from 'node:fs/promises'
import { getPostgresMigration } from 'good-webhooks/migrations'

await writeFile(
  'migrations/001-webhooks.sql',
  getPostgresMigration({ schema: 'webhooks', component: 'all' }),
)

Returns fresh-install SQL. It never connects to a database or applies DDL. The host applies the SQL once in a transaction and records it in its migration ledger.

Prop

Type

OptionDefaultMeaning
schema'public'Namespace for all tables emitted by this call.
component'all''management', 'delivery', or 'all'.

Every generated script includes CREATE SCHEMA IF NOT EXISTS for its target namespace before its tables. Table creation is not idempotent. Reapplying initial SQL is not an upgrade procedure. Invalid names or components produce INVALID_CONFIG.

Components and SQL assets

ComponentCreated tablesEquivalent shipped asset for public
managementwebhook_schema_version (if absent), webhook_endpointsgood-webhooks/migrations/management.sql
deliverywebhook_schema_version (if absent), webhook_endpoint_state, webhook_events, webhook_deliveries, webhook_attemptsgood-webhooks/migrations/delivery.sql
allBoth groupsgood-webhooks/migrations/001-initial.sql

Each component records its version in webhook_schema_version; components that share a namespace share the table. The .sql files are public package exports. They are text assets, not executable JavaScript imports. Resolve them with import.meta.resolve or require.resolve and read them with your migration tool:

import { readFile } from 'node:fs/promises'

const sql = await readFile(
  new URL(import.meta.resolve('good-webhooks/migrations/delivery.sql')),
  'utf8',
)

The current schema version is 4, exported as POSTGRES_SCHEMA_VERSION. Every check() expects exactly that version for its component. Fresh-install assets never upgrade existing data; use getPostgresUpgrade for that.

getPostgresUpgrade

getPostgresUpgrade(options: PostgresUpgradeOptions): string
scripts/write-upgrade.ts
import { writeFile } from 'node:fs/promises'
import { getPostgresUpgrade } from 'good-webhooks/migrations'

await writeFile(
  'migrations/002-webhooks-schema-4.sql',
  getPostgresUpgrade({ schema: 'webhooks', component: 'all', from: 3 }),
)

Returns SQL that moves storage recorded at version from to the current version. It never connects to a database. The SQL first checks the recorded version and raises inside your transaction if it differs, so a wrong or repeated application rolls back cleanly.

Prop

Type

OptionDefaultMeaning
schema'public'Namespace of the installation being upgraded.
component'all''management', 'delivery', or 'all' for a shared namespace.
fromrequiredThe version recorded in the database. 3 is the only upgradable one.

from: 4 throws INVALID_CONFIG because no upgrade is needed. Versions before 3 were never published and need fresh storage.

UpgradeShipped asset for publicWhat it changes
3 to 4good-webhooks/migrations/upgrades/004-all.sqlRecords a version per component. Delivery rewrites its version row; management gains one.
good-webhooks/migrations/upgrades/004-management.sqlManagement in its own namespace: creates the version table and records management.
good-webhooks/migrations/upgrades/004-delivery.sqlDelivery in its own namespace: rewrites the version row.

In a shared namespace apply all, or delivery before management; the management step needs the rewritten table and fails otherwise. No data tables change in this upgrade, so processes on the previous package keep working until they restart. The migrations guide gives the rollout order.

Schema names

A schema must be a nonblank, well-formed string of at most 63 UTF-8 bytes without null bytes. The generator and runtime quote names as PostgreSQL identifiers. Invalid names produce INVALID_CONFIG.

The schema passed to a runtime must match the corresponding migration. Management and delivery can use separate namespaces by generating each component separately. An injected management provider keeps its own configured namespace regardless of delivery options.

Better Auth management follows its adapter's schema workflow. These migrations cover the standalone PostgreSQL provider and delivery only. goodWebhooks({ schema: { modelName, fields } }) changes model and field names, not PostgreSQL placement.

On this page