Skip to Content

Kanel (type generation)

The files in the types directory are generated by running pnpm --filter db make-kysely-types, which connects to the database specified by the DATABASE_URL var, introspects the schema, and then writes updated types to your local filesystem. These types are committed to the repo and must be updated alongside any migration. Since we are currently still using Prisma to manage our schema, this means you should always generate these types from your local database after creating and running a new migration.

We use Kanel to generate these types, which can be configured via .kanelrc.js. Configuration options can be found in the Kanel docs .

Generating the types

Make sure the database is running on localhost:54322 and the user is postgres.

The default way of doing this is by following the instruction in core/README.md.

Or, if you don’t have it run there, create an .env.local file with the following content:

DATABASE_URL="your postgres url"

To generate just the types, run

pnpm --filter db make-kysely-types

If you want to regenerate the types after updating core/prisma/schema.prisma, run

pnpm --filter core migrate-dev

You might need to run pnpm --filter db migrate-dev twice to get the types to update properly.

Generating types for JSON fields

It is possible to generate more specific types for JSON fields defined.

This is done in the Prisma schema, by adding an annotation to the field which will add a comment to the database.

model ApiAccessPermission { id String @id @default(dbgenerated("gen_random_uuid()")) apiAccessTokenId String apiAccessToken ApiAccessToken @relation(fields: [apiAccessTokenId], references: [id], onDelete: Cascade) scope ApiAccessScope accessType ApiAccessType constraints Json? /// @type(ApiAccessPermissionConstraints, '../types', true, false, true) createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt // this ensures that only one permission per token and scope and access type exists @@index([apiAccessTokenId, scope, accessType], name: "api_access_permissions_idx") @@map(name: "api_access_permissions") }

Adding a /// will add the string as a comment to that column in the database using a custom generator defined here 

generator comments { provider = "pnpm exec tsx prisma/scripts/comment-generator.mts" }

This will lead to the following migration:

-- Model api_access_permissions comments COMMENT ON COLUMN "api_access_permissions"."constraints" IS '@type(ApiAccessPermissionConstraints, ''../types'', true, false, true)';

This comment then gets picked up by Kanel and turned into a type definition for the field.

The syntax (largely undocumented, except for here https://github.com/kristiandupont/kanel/issues/429#issuecomment-1636126264 ), is:

@type(name, path, isAbsolute, isDefault, importAsType)

So

config Json? /// @type(RuleConfigs, '~/actions/types', true, false, true)

will generate the following type:

import type { RuleConfigs } from "~/actions/types"; // ... config: RuleConfigs | null; // ...

Alernatively, you can also specify a type directly:

config Json? /// @type:string

or use global types

config Json? /// @type:DBTypes.RuleConfig

and then specify those types in some file like globals.ts:

declare global { namespace DBTypes { type RuleConfig = { // ... }; } } // necessary to make `declare global` work with or without using `import`s export {};

It is also possible to use these comments to provide nicer documentation for other elements, such as enums.

/// @property generic - For most use-cases. This will just authenticate you with a regular session. /// @property passwordReset - For resetting your password only /// @property signup - For signing up, but also when you're invited to a community /// @property verifyEmail - For verifying your email address enum AuthTokenType { generic passwordReset signup verifyEmail }

This will generate the following enum

/** * Represents the enum public.AuthTokenType * @property generic - For most use-cases. This will just authenticate you with a regular session. * @property passwordReset - For resetting your password only * @property signup - For signing up, but also when you're invited to a community * @property verifyEmail - For verifying your email address */ export enum AuthTokenType { generic = "generic", passwordReset = "passwordReset", signup = "signup", verifyEmail = "verifyEmail", }

Adding a table

If you have added a new table, make sure to add

/packages/db/src/public.ts
export * from "./tableName";

to /packages/db/src/public.ts, otherwise it won’t be properly exported.

Special hooks

Because kanel is a bit messy, we have a few special hooks to make it easier to work with.

See packages/db/src/kanel for more details.

Adding extra types you want to use

You can add/export extra types in src/types. This can be useful if you want to augment the default types that kanel generates, for instance for JSON columns.

Last updated on