Skip to content

Migration Guide ​

This guide helps you migrate between Kanel versions. Choose the section that applies to your upgrade path.

Migrating from V3 to V4 ​

Version 4 introduces a generator architecture that makes Kanel more flexible and extensible. The most important change is that Kanel is now a code generation framework, not just a "PostgreSQL to TypeScript" tool.

V3 Configs Still Work! ​

Good news: V3 configs continue to work in V4 with a deprecation warning. You can upgrade to V4 and migrate your config gradually.

What Changed in V4 ​

The core change is architectural: Kanel is now a generator framework where PgTsGenerator is one of many possible generators.

Key architectural changes:

  1. Generators array - You now specify what generators to run
  2. Separated concerns - Core Kanel config vs generator-specific config
  3. Composable metadata - Functions receive builtin implementations as parameters
  4. Global vs generator-specific hooks - Different hook types for different scopes

Step-by-Step Migration ​

1. Wrap Your Config in makePgTsGenerator() ​

The biggest change: most V3 top-level config options move inside makePgTsGenerator().

V3 config:

javascript
module.exports = {
  connection: {
    /* ... */
  },
  outputPath: "./src/models",

  // These were top-level in V3
  customTypeMap: {
    /* ... */
  },
  getMetadata: (details, generateFor, config) => {
    /* ... */
  },
  preRenderHooks: [makeKyselyHook()],
};

V4 config:

javascript
const { makePgTsGenerator } = require("kanel");

module.exports = {
  connection: {
    /* ... */
  },
  outputPath: "./src/models",

  // New: generators array
  generators: [
    makePgTsGenerator({
      // PgTs-specific config moves here
      customTypeMap: {
        /* ... */
      },
      getMetadata: (details, generateFor, builtinMetadata) => {
        /* ... */
      },
      preRenderHooks: [makeKyselyHook()],
    }),
  ],
};

2. Update TypeScript Configuration ​

enumStyle and module format settings move to typescriptConfig:

V3:

javascript
module.exports = {
  enumStyle: "type",
  tsModuleFormat: "esm",
};

V4:

javascript
module.exports = {
  typescriptConfig: {
    enumStyle: "literal-union", // 'type' renamed to 'literal-union'
    tsModuleFormat: "esm",
  },
};

Note: V4 default is 'literal-union' (was 'enum' in V3).

3. Update Metadata Functions ​

Metadata functions now receive builtin implementations as their last parameter instead of instantiatedConfig.

V3 pattern:

javascript
const { defaultGetMetadata } = require("kanel");

module.exports = {
  getMetadata: (details, generateFor, instantiatedConfig) => {
    const defaults = defaultGetMetadata(
      details,
      generateFor,
      instantiatedConfig,
    );
    return {
      ...defaults,
      comment: ["Custom comment"],
    };
  },
};

V4 pattern:

javascript
const { makePgTsGenerator } = require("kanel");

module.exports = {
  generators: [
    makePgTsGenerator({
      getMetadata: (details, generateFor, builtinMetadata) => ({
        ...builtinMetadata,
        comment: ["Custom comment"],
      }),
    }),
  ],
};

Key differences:

  • No more importing defaultGetMetadata - you receive it as builtinMetadata
  • Same pattern for getPropertyMetadata, generateIdentifierType, getRoutineMetadata
  • No more instantiatedConfig parameter - use useKanelContext() if needed

4. Update Hooks to Use useKanelContext() ​

The biggest change for hooks: V3 hooks received instantiatedConfig as a parameter. V4 hooks do not receive config - instead, use useKanelContext() when you need access to context.

Important

In V4, hooks don't receive configuration as a parameter. The "two types of hooks" (global vs PgTs-specific) only exist in V3 compatibility mode. In pure V4, all hooks use useKanelContext() to access context.

Pre-render hooks:

V3:

javascript
module.exports = {
  preRenderHooks: [
    makeKyselyHook(),
    generateZodSchemas,
    (outputAcc, instantiatedConfig) => {
      // Had direct access to config
      const outputPath = instantiatedConfig.outputPath;
      return outputAcc;
    },
  ],
};

V4:

javascript
const { makePgTsGenerator, useKanelContext } = require("kanel");

module.exports = {
  generators: [
    makePgTsGenerator({
      // Extension hooks go here (Kysely, Zod, etc.)
      preRenderHooks: [makeKyselyHook(), generateZodSchemas],
    }),
  ],

  // Global hooks use useKanelContext() if they need config
  preRenderHooks: [
    (outputAcc) => {
      const context = useKanelContext();
      const outputPath = context.config.outputPath;
      return outputAcc;
    },
  ],
};

Post-render hooks:

V3:

javascript
module.exports = {
  postRenderHooks: [
    (path, lines, instantiatedConfig) => {
      const outputPath = instantiatedConfig.outputPath;
      return lines;
    },
  ],
};

V4:

javascript
const { useKanelContext } = require("kanel");

module.exports = {
  postRenderHooks: [
    (path, lines) => {
      const context = useKanelContext();
      const outputPath = context.config.outputPath;
      return lines;
    },
  ],
};

See useKanelContext for complete documentation on accessing context in V4.

5. Update Filter Function ​

typeFilter renamed to filter:

V3:

javascript
module.exports = {
  typeFilter: (pgType) => pgType.schemaName === "public",
};

V4:

javascript
module.exports = {
  filter: (pgType) => pgType.schemaName === "public",
};

You can also add generator-specific filters:

javascript
generators: [
  makePgTsGenerator({
    filter: (pgType) => !pgType.name.startsWith("_"),
  }),
];

6. Explicitly Add Default Hooks (If Desired) ​

V3 automatically applied applyTaggedComments and markAsGenerated. V4 does not.

If you want V3 behavior:

javascript
const { makePgTsGenerator, markAsGenerated } = require("kanel");

module.exports = {
  generators: [makePgTsGenerator()],
  postRenderHooks: [markAsGenerated],
};

Note: applyTaggedComments is deprecated in V4. Use tagged comments in your database schema instead, or write a custom metadata function.


Complete Migration Example ​

V3 config:

javascript
const { makeKyselyHook } = require("kanel-kysely");
const { generateZodSchemas } = require("kanel-zod");

module.exports = {
  connection: {
    host: "localhost",
    database: "mydb",
  },

  outputPath: "./src/models",
  preDeleteOutputFolder: true,
  enumStyle: "type",

  customTypeMap: {
    "pg_catalog.tsvector": "string",
  },

  getMetadata: (details, generateFor, config) => {
    const { defaultGetMetadata } = require("kanel");
    const defaults = defaultGetMetadata(details, generateFor, config);
    return { ...defaults, comment: ["Custom"] };
  },

  preRenderHooks: [makeKyselyHook(), generateZodSchemas],
};

V4 config:

javascript
const { makePgTsGenerator } = require("kanel");
const { makeKyselyHook } = require("kanel-kysely");
const { generateZodSchemas } = require("kanel-zod");

module.exports = {
  connection: {
    host: "localhost",
    database: "mydb",
  },

  outputPath: "./src/models",
  preDeleteOutputFolder: true,

  typescriptConfig: {
    enumStyle: "literal-union", // 'type' renamed
  },

  generators: [
    makePgTsGenerator({
      customTypeMap: {
        "pg_catalog.tsvector": "string",
      },

      getMetadata: (details, generateFor, builtinMetadata) => ({
        ...builtinMetadata,
        comment: ["Custom"],
      }),

      preRenderHooks: [makeKyselyHook(), generateZodSchemas],
    }),
  ],
};

Breaking Changes Summary ​

ChangeV3V4
Config structureFlatGenerators array
PgTs config locationTop-levelInside makePgTsGenerator()
Enum style option'type' or 'enum''literal-union' or 'enum'
Enum style default'enum''literal-union'
Metadata parameterinstantiatedConfigbuiltinMetadata / builtinType
Hook parameterinstantiatedConfigUse useKanelContext()
Filter nametypeFilterfilter
PgTs hooks locationTop-level preRenderHooksPgTsGeneratorConfig.preRenderHooks
Default hooksAuto-appliedExplicit opt-in

Options That Moved ​

These options moved from top-level config to PgTsGeneratorConfig (inside makePgTsGenerator()):

  • customTypeMap
  • getMetadata
  • getPropertyMetadata
  • generateIdentifierType
  • getRoutineMetadata
  • propertySortFunction
  • Pre-render hooks that use PgTs context (Kysely, Zod, Knex, etc.)

Deprecated Functions ​

These V3 exports are deprecated in V4:

  • defaultGetMetadata - Use builtinMetadata parameter instead
  • defaultGetPropertyMetadata - Use builtinMetadata parameter instead
  • defaultGenerateIdentifierType - Use builtinType parameter instead
  • defaultGetRoutineMetadata - Use builtinMetadata parameter instead
  • applyTaggedComments - Use database comments or custom metadata functions
  • usePgTsGeneratorContext() - Use the context parameter in PgTs hooks instead

Why These Changes? ​

V4's architecture enables:

  1. Multiple generators - Generate TypeScript, Markdown, Python, etc. from one schema
  2. Cleaner separation - Core Kanel config vs generator-specific config
  3. Better composability - Builtin metadata makes customization easier
  4. Type safety - PgTs hooks receive typed context as a parameter
  5. Extensibility - Write custom generators for any language or format

Migrating from V2 to V3 ​

If you're still on V2, migrate to V3 first, then to V4.

index.ts ​

Kanel no longer generates an index.ts file by default. Use generateIndexFile if you need it:

javascript
const { generateIndexFile } = require("kanel");

module.exports = {
  connection: "...",
  preRenderHooks: [generateIndexFile],
};

Nominators ​

Nominators were replaced by getMetadata, getPropertyMetadata, and generateIdentifierType.

Ignoring Entities ​

schema.ignore was replaced by typeFilter:

V2:

javascript
ignore: [/^celery/, /^djcelery/];

V3:

javascript
typeFilter: (d) => ![/^celery/, /^djcelery/].some((v) => v.test(d.name));

customTypeMap ​

Keys must now be schema-qualified:

V2:

javascript
customTypeMap: {
  'float8': 'number'
}

V3:

javascript
customTypeMap: {
  'pg_catalog.float8': 'number'
}

Array types no longer need explicit entries.

External Types ​

externalTypesFolder was removed. Use TypeImport in tagged comments or custom metadata functions instead.


Need Help? ​