Connect to Postgres local vs Neon with Kysely
By Flavio Copes
Set up Kysely with a local Postgres database using the pg Pool, then connect to Neon with kysely-neon for serverless or PostgresDialect when you need transactions.
I switched a codebase from a local Postgres database to Vercel Postgres, which came with its own optimized package.
Vercel Postgres does not exist anymore. Between late 2024 and early 2025 Vercel moved every store to Neon, sold through the Vercel Marketplace, and the @vercel/postgres-kysely package is now marked deprecated on npm. The local half of this post is unchanged. For the hosted half I kept the snippet I used back then, and added the Neon packages you’d use today.
For the local database, Kysely sits on top of the standard pg driver. You give it a dialect wrapping a connection pool:
import {
Kysely,
PostgresDialect,
} from 'kysely'
import pg from 'pg'
const POSTGRES_URL = process.env.POSTGRES_URL
const dialect = new PostgresDialect({
pool: new pg.Pool({
connectionString: POSTGRES_URL,
max: 10,
}),
})
export const db = new Kysely({
dialect
})
The connection string is a normal Postgres URL:
postgresql://user:password@localhost:5432/mydb
Keep it in an environment variable, out of the repository. max: 10 caps how many connections this process can hold, which matters once several instances of the app share one database.
What I used with Vercel Postgres
With Vercel Postgres, I used:
import { createKysely } from '@vercel/postgres-kysely'
export const db = createKysely({
connectionString: process.env.POSTGRES_URL
})
You could even drop the connectionString option entirely: the db instance knew how to look up the environment variable POSTGRES_URL, which Vercel set for you when you linked the database to the project.
If you maintain a project from that era, this still runs (the Neon integration keeps the legacy POSTGRES_* variables around for compatibility), but the package gets no more updates. Move to one of the two Neon setups below.
Neon on serverless or edge
On Vercel (or any serverless / edge runtime), use Neon’s HTTP driver with kysely-neon and @neondatabase/serverless:
import { Kysely } from 'kysely'
import { NeonDialect } from 'kysely-neon'
import { neon } from '@neondatabase/serverless'
export const db = new Kysely({
dialect: new NeonDialect({
neon: neon(process.env.DATABASE_URL),
}),
})
When you attach a Neon database from the Vercel Marketplace, Vercel sets DATABASE_URL (a pooled connection string) plus DATABASE_URL_UNPOOLED and the legacy POSTGRES_* variables on the project.
The HTTP driver is stateless, so it’s fine for single queries but it can’t run interactive transactions (db.transaction() with several statements that depend on each other). When you need those, use Neon’s WebSocket Pool with Kysely’s normal PostgresDialect:
import { Kysely, PostgresDialect } from 'kysely'
import { Pool } from '@neondatabase/serverless'
export const db = new Kysely({
dialect: new PostgresDialect({
pool: new Pool({ connectionString: process.env.DATABASE_URL }),
}),
})
On Node 22 and newer the Pool uses the built-in WebSocket. On older Node you have to install ws and set neonConfig.webSocketConstructor = ws before creating the pool.
For more hosting options beyond Neon, see where to run Postgres or SQLite for a side project, which compares ten hosts with their free tiers and costs, or the older where to host a PostgreSQL database.
Everything downstream of db stays identical in both setups. That is the point of Kysely’s dialect layer: queries like
const rows = await db
.selectFrom('notes')
.select(['id', 'title'])
.execute()
do not change when the database moves.
To verify which database you are actually talking to, run a quick check at startup:
const result = await sql`select current_database()`.execute(db)
console.log(result.rows)
(sql comes from kysely.) I did this after the switch because the classic failure mode here is an environment variable pointing at the old local database in one environment and at the hosted one in another. Everything works, just against the wrong data. If the printed database name is not what you expect, fix the environment before debugging anything else.
Want me to talk about your product? You can sponsor this site.
Related posts about database: