Drizzle is an ORM for JavaScript and TypeScript applications. This guide sets up Drizzle to work with both a local Postgres database and a hosted Postgres database on Neon, and runs schema migrations against each.
Prerequisites
- Docker Desktop: To run a local Postgres database, make sure you have Docker Desktop installed on your machine.
- A Neon account to set up a hosted Postgres database.
Create a new Next.js application
Create a new Next.js project with the following command:
npx create-next-app@latest my-appWhen prompted, choose:
Yeswhen prompted to use TypeScript.Nowhen prompted to use ESLint.Yeswhen prompted to use Tailwind CSS.Nowhen prompted to usesrc/directory.Yeswhen prompted to use App Router.Nowhen prompted to customize the default import alias (@/*).
Once that is done, move into the project directory, and start the application in development mode with the following command:
cd my-app
npm run devSet up a local Postgres database
You will use Docker to run a local Postgres instance. First, create a docker-compose.yml file in the root directory with the following code:
services:
postgres:
image: 'postgres:latest'
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
ports:
- '5432:5432'
pg_proxy:
image: ghcr.io/neondatabase/wsproxy:latest
environment:
APPEND_PORT: 'postgres:5432'
ALLOW_ADDR_REGEX: '.*'
LOG_TRAFFIC: 'true'
ports:
- '5433:80'
depends_on:
- postgresIn the YAML configuration file above, you have set up two services using Docker: a Postgres database and a WebSocket proxy for the Neon serverless driver. The postgres service uses the latest Postgres image and configures the necessary environment variables for the database user, password, and database name. It exposes port 5432 for database connections. The pg_proxy service uses a WebSocket proxy image, allowing connections to the Postgres service through port 5433.
Next, start the services in Docker with the following command:
docker-compose up -dAdd the local instance's connection string (postgres://postgres:postgres@localhost:5432/postgres) to your .env file as LOCAL_POSTGRES_URL.
Set up a Postgres database on Neon
Go to the Neon Console and create a new project. Then click Connect on your project dashboard to get the connection string for your database. It looks like this:
postgresql://<user>:<password>@<endpoint_hostname>.neon.tech:<port>/<dbname>?sslmode=require&channel_binding=requireReplace <user>, <password>, <endpoint_hostname>, <port>, and <dbname> with your specific details.
Add this connection string to your .env file as POSTGRES_URL.
Integrate Drizzle with Next.js
To use Drizzle with Next.js and Neon, install the necessary packages via the following command:
npm install ws postgres drizzle-orm @neondatabase/serverless
npm install -D @types/ws drizzle-kitInstall the postgres package even though your code doesn't import it: in local environments, Drizzle uses it to apply schema migrations to your local Postgres. In production, Drizzle uses the Neon serverless driver to apply schema migrations to your database on Neon.
Then, create a file named drizzle.server.ts with the following code:
// File: drizzle.server.ts
import { neonConfig, Pool } from '@neondatabase/serverless';
import { drizzle } from 'drizzle-orm/neon-serverless';
import { WebSocket } from 'ws';
const connectionString =
process.env.NODE_ENV === 'production' ? process.env.POSTGRES_URL : process.env.LOCAL_POSTGRES_URL;
if (process.env.NODE_ENV === 'production') {
neonConfig.webSocketConstructor = WebSocket;
neonConfig.poolQueryViaFetch = true;
} else {
neonConfig.wsProxy = (host) => `${host}:5433/v1`;
neonConfig.useSecureWebSocket = false;
neonConfig.pipelineTLS = false;
neonConfig.pipelineConnect = false;
}
const pool = new Pool({ connectionString });
export default drizzle(pool);The code above picks the connection string based on the environment (production or local). In production, it configures the WebSocket settings for Neon. In local development, it routes connections through the local WebSocket proxy. Finally, it creates a connection pool and exports a Drizzle instance for database interactions.
Next, create a file named drizzle.config.ts with the following code:
// File: drizzle.config.ts
import { defineConfig } from 'drizzle-kit';
const url =
process.env.NODE_ENV === 'production' ? process.env.POSTGRES_URL : process.env.LOCAL_POSTGRES_URL;
if (!url)
throw new Error(
`Connection string to ${process.env.NODE_ENV ? 'Neon' : 'local'} Postgres not found.`
);
export default defineConfig({
dialect: 'postgresql',
dbCredentials: { url },
schema: './lib/schema.ts',
});The code above picks the Postgres connection string for the current environment (production or local), which drizzle-kit uses for operations such as schema migrations.
Run schema migrations
Run the Drizzle migrations with the following commands. NODE_ENV decides whether they run against your local Postgres or your database on Neon:
npx drizzle-kit generate
npx drizzle-kit migrateSource code
You can find the source code for the application described in this guide on GitHub.
Need help?
Join our Discord Server to ask questions or see what others are doing with Neon. For paid plan support options, see Support.








