pg driver with verified TLS, create a table with Kysely migrations, and serve a small to-do API from Node.js.
Prerequisites
- Node.js 22.18 or later, which runs TypeScript files directly without a build step. The guide’s files use the
.mtsextension, so Node.js treats them as ES modules in both CommonJS and ES module projects. - A ClickHouse Cloud account.
psql, to create the database. You can also run theCREATE DATABASEstatement in the SQL console.curlto call the API.
pg 8.23.1, TypeScript 7.0.2, and PostgreSQL 18.
Create a ClickHouse Managed Postgres service
In the ClickHouse Cloud console, click New service and select Postgres. The instance is ready in a few minutes. See the quickstart for a walkthrough.Get your connection details
Click Connect in the left sidebar of your service. Keep Directly selected and turn on Use SSL: the url connection string now ends withsslmode=verify-full&sslrootcert=.... Click Download CA certificate to download your service’s CA certificate, named <service-name>-ca-certificate.pem, and copy the connection string.
This guide connects directly to Postgres on port 5432, for two reasons:
- A Node.js server is a long-lived process. The
pgPoolalready keeps a small set of connections open and reuses them, so a server-side pooler adds little. - Kysely’s
Migratorholds a session-level advisory lock (pg_advisory_lock) while it runs migrations. PgBouncer runs in transaction pooling mode, so taking the lock, running the migrations, and releasing the lock can each land on a different backend connection. Through PgBouncer, the lock can stay held on a pooled connection, and later migration runs block waiting for it.
6432, same host). Kysely with pg works in transaction pooling mode because pg uses unnamed prepared statements by default. Keep running migrations against port 5432. See PgBouncer connection pooling.
Set up the project
Create a project and install Kysely, thepg driver, and TypeScript:
Configure the connection
Move the CA certificate you downloaded into your project directory and rename it toca-certificate.pem. Then create a database for the app:
.env file, or to your existing .env. Replace <PASSWORD> with your password, the database name with guide_kysely, and set sslrootcert to ca-certificate.pem:
.env
pg reads sslmode and sslrootcert from the connection string: verify-full makes it check that the server certificate is signed by your service’s CA and matches the hostname. sslrootcert is resolved relative to the working directory, which is the project root when you use npm run.
Add a tsconfig.json so that tsc type-checks the same files that Node.js runs. If your app already has a tsconfig.json, merge these compilerOptions and the migrations folder into it:
tsconfig.json
Define the database interface
Kysely types every query from an interface that describes your tables. Createsrc/db.mts with the interface and a Kysely instance that uses PostgresDialect with a pg Pool:
src/db.mts
Generated marks columns that the database fills in, so they’re optional on insert.
Create and run a migration
This guide uses Kysely’s built-inMigrator with FileMigrationProvider, which is part of the core kysely package. The separate kysely-ctl CLI is optional and wraps the same Migrator.
Create a migration file. Kysely runs migrations in alphanumeric order of their file names, so prefix them with a date:
migrations/2026-09-30_create_todo.mts
src/migrate.mts to apply all pending migrations. In Kysely 0.29, import Migrator and FileMigrationProvider from kysely/migration:
src/migrate.mts
kysely_migration table, so running the command again does nothing.
Query the database
Createsrc/server.mts, a small HTTP server that creates, reads, updates, and deletes to-dos with Kysely. In an existing app, you can use the same queries in your own route handlers:
src/server.mts
Database interface, a misspelled table or column name, or an insert without the required title, fails type-checking. Check the project:
Verify
Start the server:npm start runs your own app, start the example server with node --env-file=.env src/server.mts instead.
In another terminal, create, update, and delete a few to-dos:
guide_kysely and the public schema, and click the todo table. The kysely_migration and kysely_migration_lock tables that Kysely created are listed next to it:
Next steps
- Connection: connection strings, PgBouncer, and TLS.
- Settings: tune PostgreSQL and PgBouncer parameters such as
max_connections. - Read replicas: scale read-heavy workloads.
- Kysely documentation: joins, transactions, and type generation.