Skip to main content
Drizzle ORM is a TypeScript ORM with a SQL-like query builder, and drizzle-kit is its companion CLI for generating and running migrations. In this guide, you define a todos table in TypeScript, create it with a drizzle-kit migration, and serve create, read, update, and delete queries from a small Node.js HTTP server. Every connection uses TLS with full certificate verification.

Prerequisites

  • Node.js 20 or later
  • A ClickHouse Cloud account
  • psql, to create the database. You can also run the CREATE DATABASE statement in the SQL console.

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. The modal shows your username, password, server, and port, and it has a Directly / via PgBouncer toggle. This guide uses two connection strings:
  • Direct (port 5432) for drizzle-kit. Migrations run DDL, can run for a long time, and may depend on session state such as SET lock_timeout, so they should talk to Postgres directly.
  • PgBouncer (port 6432) for the application. The bundled PgBouncer runs in transaction pooling mode and lets many app instances, including serverless functions, share a small number of Postgres connections.
If you run a single long-lived Node.js server, node-postgres already keeps its own connection pool, so you can also point the app at the direct connection. Use the PgBouncer connection when you scale out to many processes or run on serverless platforms.
With Directly selected, turn on Use SSL. The connection URL on the url tab now ends in sslmode=verify-full&sslrootcert=<service-name>-ca-certificate.pem, and the port is 5432. Select via PgBouncer to see the pooled connection details: the port changes to 6432, and the URL always includes sslmode=verify-full. In the modal, click Download CA certificate. You can also download it from Settings → CA Certificate. The certificate is unique to your instance, so the driver can use it to verify that it’s talking to your server.

Set up the project

Create a project and install Drizzle ORM, the node-postgres driver (pg), and drizzle-kit:
Adding to an existing app?Skip the mkdir, npm init, and npm pkg set type=module commands. In your project, run npm install drizzle-orm pg dotenv and npm install -D drizzle-kit tsx @types/pg, then continue with Configure the connection.

Configure the connection

Move the CA certificate you downloaded into the project directory and rename it to ca-certificate.pem. Create a database for the app. Replace <PASSWORD> and the host with the values from the Connect modal:
Add both connection strings to a .env file in the project root, or to your existing .env. They differ only in the port, and both point at the guide_drizzle database:
.env
node-postgres reads sslmode=verify-full and sslrootcert from the URL. It loads the CA file and checks both the certificate chain and the hostname, so you don’t need any extra TLS code. The sslrootcert path is resolved relative to the directory you run commands from. Keep .env out of version control.
If the CA is missing or wrong, the connection fails with unable to verify the first certificate. If the hostname doesn’t match the certificate, it fails with ERR_TLS_CERT_ALTNAME_INVALID. If your password contains special characters such as @, /, or #, URL-encode them.

Define the schema

Create src/db/schema.ts:
src/db/schema.ts
Create drizzle.config.ts in the project root. It points drizzle-kit at the schema and at the direct connection:
drizzle.config.ts

Run migrations

Generate a SQL migration from the schema:
The generated drizzle/0000_init.sql contains the CREATE TABLE statement. Review it and commit the drizzle folder with your code. Then apply it:
drizzle-kit migrate records applied migrations in the drizzle.__drizzle_migrations table and only runs new ones on later calls. Each time you change schema.ts, run generate and then migrate again.
drizzle-kit migrate exits with status 1 and no error message when it can’t connect, for example when the sslrootcert path or the password is wrong. If that happens, test DIRECT_DATABASE_URL with psql first.
drizzle-kit push applies schema changes without migration files. It’s handy for prototyping, but generate and migrate give you reviewable SQL files and a history for production.

Query the database

Create src/db/index.ts. It creates the Drizzle client over a node-postgres pool using the PgBouncer connection:
src/db/index.ts
Create src/server.ts, a small HTTP server that runs one Drizzle query per route. In an existing app, you can call the same db queries from your own route handlers instead:
src/server.ts
No driver settings are needed for PgBouncer. Drizzle’s queries use unnamed statements by default, and the bundled PgBouncer also supports named prepared statements, such as those created with Drizzle’s .prepare, in transaction pooling mode. Session-level features such as LISTEN and session advisory locks don’t work through PgBouncer. See the FAQ.

Run and verify

Start the server:
In a second terminal, create two todos, mark the first one done, and delete the second:
The PATCH request returns the updated row:
Open http://localhost:3101/todos in your browser to list the remaining todos. The response looks like this:
To see the data in the console, open SQL console in the left sidebar of your service, expand guide_drizzle and then public, and click the todos table. The drizzle schema holds the migration history table. The table contains the remaining row:

Next steps

Last modified on September 30, 2026