Updated Oct 27, 202615 min read

Neon Serverless Postgres: Complete Guide for TypeScript Developers

How to use Neon's serverless PostgreSQL with modern TypeScript backends

Most TypeScript backends need a database, and most of the time that database is PostgreSQL. The question is how you run it. Traditional managed Postgres (RDS, Cloud SQL) means picking an instance size upfront, paying for it whether you use it or not, and scaling manually when traffic grows. Neon takes a different approach: serverless Postgres that scales to zero when idle and branches like Git.

This guide covers what Neon is, how to use it with common TypeScript tools (Drizzle, Prisma), and how Encore.ts makes it even simpler with automatic database provisioning and zero connection string management.

Already familiar with Neon and just want to set it up? Skip to Using Neon with Encore.ts for the fastest path from zero to production Postgres.

What Is Neon?

Neon is a serverless PostgreSQL provider. It runs standard Postgres (the latest stable Postgres version) with a storage layer rewritten from scratch to separate compute from storage. This architecture enables three things that traditional managed Postgres can't do:

  1. Scale to zero. Compute shuts down after a period of inactivity. You don't pay for idle databases. When a connection comes in, compute starts up again (cold starts are typically 300-500ms).

  2. Database branching. Create a copy-on-write branch of your database in seconds, regardless of size. Branches share the underlying storage pages, so a branch of a 100GB database costs almost nothing until you start writing new data to it.

  3. Instant point-in-time restore. The storage engine keeps a history of changes. You can restore to any point in the last 6 hours on the Free plan, up to 7 days on Launch, and up to 30 days on Scale, without maintaining manual backups.

Under the hood, it's still Postgres. Your existing queries, extensions, ORMs, and migration tools all work. Neon adds a connection pooler (built on PgBouncer), a web console, and APIs for managing branches and compute endpoints.

Neon's Free Tier

The Free plan is permanent and needs no credit card. As of October 27, 2026, it includes:

  • 100 projects, each with up to 10 branches
  • 100 CU-hours of compute per project per month, autoscaling up to 2 CU (8 GB RAM)
  • 1 GB of Postgres storage per project, 20 GB across all projects
  • Scale to zero after 5 minutes of inactivity (always on, cannot be disabled)
  • A 6-hour restore window and 5 GB of egress per project

Running out of CU-hours suspends compute until the next billing period, and going over the storage cap blocks writes. Neither deletes your data. For hobby projects and early-stage apps, you can build without a database bill until you outgrow these limits.

Why Neon for TypeScript Backends

TypeScript backend developers typically care about three things when choosing a database provider: how fast they can get started, how the database works in their development workflow, and how it scales when real traffic shows up.

Development Speed

Neon databases are created in seconds through the web console or API. There's no instance provisioning, no VPC configuration, no waiting for a cluster to boot. You get a connection string immediately and start writing queries.

For TypeScript specifically, Neon provides a @neondatabase/serverless driver that works over HTTP and WebSocket. This matters for serverless runtimes (Cloudflare Workers, Vercel Edge Functions) where traditional TCP-based Postgres drivers don't work. If you're running a standard Node.js server, the regular pg driver works fine too.

Branching for Preview Environments

Database branching is where Neon stands out for development workflows. When you open a pull request, you can create a database branch that contains a copy of your production data (or staging data). The branch is isolated, so you can run migrations against it without affecting anyone else. When you merge or close the PR, you delete the branch.

This solves a common problem in backend development: either everyone shares a staging database (and steps on each other), or you maintain separate databases for each developer (which drift out of sync). Branches give you the isolation of separate databases with the data fidelity of a shared one.

Connection Pooling

Neon includes built-in connection pooling through PgBouncer. Each connection string comes in two variants: a direct connection (for migrations and admin tasks) and a pooled connection (for application queries). The pooled endpoint handles hundreds of concurrent connections without hitting Postgres connection limits.

This matters for TypeScript backends because Node.js applications often open many connections, especially when running multiple service instances or handling concurrent requests. With traditional Postgres hosting, you'd add PgBouncer yourself. Neon includes it by default.

Getting Started with Neon

Creating a Database

  1. Sign up at neon.com
  2. Create a project (this creates your first database)
  3. Choose your region (pick the one closest to your application servers)

Neon gives you a connection string immediately:

postgresql://username:password@ep-example-123.us-east-2.aws.neon.com/neondb?sslmode=require

You also get a pooled connection string for application use:

postgresql://username:password@ep-example-123-pooler.us-east-2.aws.neon.com/neondb?sslmode=require

Setting Up Locally

Store your connection string in an environment variable:

export DATABASE_URL="postgresql://username:password@ep-example-123-pooler.us-east-2.aws.neon.com/neondb?sslmode=require"

For local development, you have two options: connect directly to a Neon development branch, or run Postgres locally and only use Neon in staging and production. A local database is faster for iteration (no network round-trip), but a Neon branch gives you production-like data.

Using Neon with Common TypeScript Tools

Neon is standard Postgres, so any TypeScript library that connects to Postgres works. Here are the most common setups.

With Drizzle ORM

Drizzle is a TypeScript ORM that generates type-safe queries with a SQL-like API. It works well with Neon because it supports both the standard pg driver and Neon's serverless driver.

Install dependencies:

npm install drizzle-orm @neondatabase/serverless npm install -D drizzle-kit

Define your schema:

// schema.ts import { pgTable, serial, text, timestamp } from "drizzle-orm/pg-core"; export const users = pgTable("users", { id: serial("id").primaryKey(), name: text("name").notNull(), email: text("email").notNull().unique(), createdAt: timestamp("created_at").defaultNow(), });

Connect using Neon's serverless driver:

// db.ts import { drizzle } from "drizzle-orm/neon-serverless"; import { Pool } from "@neondatabase/serverless"; import * as schema from "./schema"; const pool = new Pool({ connectionString: process.env.DATABASE_URL, }); export const db = drizzle(pool, { schema });

Query with full type safety:

import { db } from "./db"; import { users } from "./schema"; import { eq } from "drizzle-orm"; // Insert await db.insert(users).values({ name: "Alice", email: "alice@example.com", }); // Select with type-safe where clause const user = await db.query.users.findFirst({ where: eq(users.email, "alice@example.com"), });

With Prisma

Prisma works with Neon through its standard PostgreSQL adapter. No special configuration is needed beyond the connection string.

// schema.prisma datasource db { provider = "postgresql" url = env("DATABASE_URL") } model User { id Int @id @default(autoincrement()) name String email String @unique createdAt DateTime @default(now()) }

For serverless environments, Prisma also offers a Neon adapter:

import { Pool } from "@neondatabase/serverless"; import { PrismaNeon } from "@prisma/adapter-neon"; import { PrismaClient } from "@prisma/client"; const pool = new Pool({ connectionString: process.env.DATABASE_URL, }); const adapter = new PrismaNeon(pool); const prisma = new PrismaClient({ adapter });

With the pg Driver (Raw SQL)

If you prefer writing SQL directly, the standard pg library works with no changes:

import { Pool } from "pg"; const pool = new Pool({ connectionString: process.env.DATABASE_URL, }); const result = await pool.query( "SELECT * FROM users WHERE email = $1", ["alice@example.com"] );

For serverless runtimes, swap in Neon's driver:

import { Pool } from "@neondatabase/serverless"; const pool = new Pool({ connectionString: process.env.DATABASE_URL, }); // Same query API as pg const result = await pool.query( "SELECT * FROM users WHERE email = $1", ["alice@example.com"] );

Using Neon with Encore.ts

The approaches above all work, but they share a common overhead: you're managing connection strings, configuring pooling, writing migration scripts, and setting up different databases for each environment. This is the kind of infrastructure plumbing that adds up across services.

Encore.ts takes a different approach. Databases are declared as part of your application code. Encore handles provisioning, migrations, connection pooling, and per-environment configuration automatically.

Encore has a built-in integration with Neon. Neon is the default database provider for environments hosted by Encore, and you can select it as the database for environments in your own AWS or GCP account. You connect a Neon account once with an API key, and Encore provisions the databases your code declares. No connection strings to copy and no pooler setup.

Declaring a Database

In Encore, you declare a database by creating a SQLDatabase instance and putting your migration files in a migrations directory:

// users/db.ts import { SQLDatabase } from "encore.dev/storage/sqldb"; const db = new SQLDatabase("users", { migrations: "./migrations", }); export default db;

Create your migration:

-- users/migrations/1_create_users.up.sql CREATE TABLE users ( id BIGSERIAL PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL UNIQUE, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() );

That's it. When you run encore run, Encore creates a local Postgres database, runs the migration, and gives you a type-safe query API. When you deploy, the same declaration provisions a Neon database in the cloud. This structured approach also means AI coding agents can generate database queries that match your actual schema - when an agent sees new SQLDatabase("users", { migrations: "./migrations" }), it knows the database exists and can write queries against the migration-defined schema. Encore's MCP server takes this further by giving agents access to live database schemas, so generated queries are always valid.

Querying

Encore provides a built-in query API. No ORM installation, no connection management:

import db from "./db"; // Type-safe row mapping interface User { id: number; name: string; email: string; } // Query const user = await db.queryRow<User>` SELECT id, name, email FROM users WHERE email = ${email} `; // Insert await db.exec` INSERT INTO users (name, email) VALUES (${name}, ${email}) `;

You can also use Drizzle or Prisma on top of Encore's database if you prefer an ORM. Encore handles the connection; the ORM handles the query building.

Automatic Neon Provisioning

To use a Neon database with Encore, follow the Neon integration docs:

  1. Create an API key in the Neon Console.
  2. In the Encore dashboard, go to App Settings > Integrations > Neon, click "Connect Account" and paste the key.
  3. When you create an environment, pick Neon database in the Database section. You can create a new Neon project and branch, branch from another Encore environment that uses Neon, branch from an existing Neon branch, or import an existing Neon branch.

How each stage gets its database:

  • Local development: Encore runs a local Postgres instance. No Neon account needed.
  • Preview environments: Under App Settings > Preview Environments, you choose an environment that uses Neon, and each preview environment branches its database from it. Pick a source environment with test or scrubbed data rather than production, since previews run unreviewed pull request code.
  • Staging and production: Each environment uses the Neon project, branch and region you select. You can edit the endpoint's CPU limits and suspend timeout, and the project's history retention, from the environment's Infrastructure page.

Encore also sets up a role hierarchy in the Neon database, including a svc_<name> role per service with write access only to the databases that service uses. If you import an existing branch, you may need to change the database owner to the db_<db_name>_admin role so migrations can run.

The connection string and credentials are handled by the platform. Your application code stays the same across environments. Read more about Encore's database support in the database docs and the Neon integration docs.

Why This Approach Works Better

The manual approach (Neon + Drizzle/Prisma + environment variables) works. Plenty of teams ship this way. But it means every developer on the team needs to set up their own Neon branch (or local Postgres), keep their environment variables in sync, and remember to run migrations after pulling new code.

With Encore, encore run handles all of that. New developer joins the team, clones the repo, runs encore run, and has a working local database with the latest schema. Open a pull request, and the preview environment has its own database branch. Merge to main, and the migration runs automatically in production. The database is part of the code, not a separate thing you configure.

Neon Features for Production

Once you're running in production, Neon has several features that make operations easier.

Autoscaling

Neon compute scales based on load. You configure a minimum and maximum compute size (measured in Compute Units, where 1 CU is roughly 1 vCPU and 4GB RAM). The database scales up during traffic spikes and back down during quiet periods.

Branching Workflows

Beyond preview environments, branching is useful for:

  • Testing migrations: Branch production, run the migration on the branch, verify it works, then run it on production.
  • Analytics queries: Branch production and run expensive analytical queries on the branch without affecting production performance.
  • Data seeding: Create a branch, modify data for a specific test scenario, use it for QA, then delete it.

Branches can be created through the Neon console, CLI, or API:

# Create a branch from the main branch neonctl branches create --name feature-xyz --project-id your-project-id # Get the connection string for the new branch neonctl connection-string feature-xyz --project-id your-project-id

Monitoring and Observability

Neon's dashboard shows query performance, connection counts, compute utilization, and storage usage. For deeper monitoring, you can connect Neon to external tools:

  • pg_stat_statements is enabled by default for query performance analysis
  • Connection pooler metrics show active, idle, and waiting connections
  • Storage metrics track data size and history retention

Read Replicas

Neon supports read replicas that run on separate compute endpoints. Unlike traditional Postgres replicas, Neon replicas share the same storage layer, so there's no replication lag for committed writes. This is useful for separating read-heavy analytics workloads from your primary application traffic.

# Create a read replica neonctl endpoints create --type read_replica --branch main --project-id your-project-id

Cost Considerations

Neon bills paid plans by usage with no monthly minimum. Compute is metered in CU-hours (compute size times hours running, where 1 CU is about 4 GB of RAM), and suspended compute costs nothing. Prices below are from neon.com/pricing, checked October 27, 2026:

FreeLaunchScale
Price$0Pay for what you usePay for what you use
Compute100 CU-hours per project$0.106 per CU-hour$0.222 per CU-hour
Max autoscaling2 CU16 CU16 CU, or fixed up to 56 CU
Storage1 GB per project$0.35 per GB-month$0.35 per GB-month
Branches per project1010, then $1.50 per branch-month25, then $1.50 per branch-month
Restore window6 hoursUp to 7 daysUp to 30 days
Scale to zeroAfter 5 minAfter 5 min, can be disabled1 min to always on
Egress included5 GB per project500 GB per project500 GB per project

As a rough example, a 0.25 CU compute that runs 8 hours a day for 30 days uses 60 CU-hours, about $6.36 on Launch before storage. Branches use copy-on-write storage, so a new branch starts at $0 and grows only with the writes made on it. Branches beyond the plan's included count cost $1.50 per branch-month.

For comparison, a small RDS instance (db.t4g.micro) costs around $12/month but runs 24/7 whether you use it or not. Neon's paid plans are usage-based, so actual costs depend on how many compute hours you consume. For workloads with variable traffic or long idle periods, Neon's scale-to-zero model can be significantly cheaper. For consistently high-traffic workloads, fixed-compute options may be more predictable.

Neon vs Supabase

Both run standard Postgres, so your queries, extensions and ORMs work on either. The difference is what sits around the database.

  • Neon started as a serverless Postgres service: separate compute and storage, scale to zero, and copy-on-write branches you can create per pull request. Its pricing page now also lists auth, a Data API, functions and object storage, but branching and usage-based compute are the core of the product.
  • Supabase is a backend platform built on Postgres. It bundles the database with auth, file storage, realtime subscriptions, edge functions and auto-generated APIs, and is designed for apps that call those services directly from the frontend.

Pick Neon if you write your own backend and want a database that costs little when idle and branches cleanly for preview environments. Pick Supabase if you want a hosted backend with auth and storage included and plan to write most logic in the frontend. If you use Encore, Neon is the provider with a built-in integration: Encore provisions it per environment and branches it for preview environments.

Common Patterns

Multi-Tenant Databases

For SaaS applications, you have two options with Neon:

  1. Schema-per-tenant: One database, separate schemas. Works well up to a few hundred tenants. Use Postgres SET search_path to route queries.

  2. Database-per-tenant: Separate Neon projects or branches per tenant. Better isolation, but more management overhead.

For most TypeScript backends, the schema-per-tenant approach is simpler and Postgres handles it well.

Connection Management in Serverless

If your TypeScript backend runs on a serverless platform (Lambda, Cloudflare Workers, Vercel), use Neon's serverless driver over HTTP instead of maintaining persistent connections:

import { neon } from "@neondatabase/serverless"; const sql = neon(process.env.DATABASE_URL!); // Each invocation gets a fresh connection over HTTP const users = await sql`SELECT * FROM users WHERE active = true`;

This avoids the cold-start connection overhead and connection limit issues that serverless functions face with traditional Postgres drivers.

Migrations

For migration management, the choice depends on your setup:

  • Drizzle Kit: npx drizzle-kit push or npx drizzle-kit migrate for Drizzle users
  • Prisma Migrate: npx prisma migrate deploy for Prisma users
  • Plain SQL files: Tools like golang-migrate or dbmate for teams that prefer writing SQL directly
  • Encore: Migrations run automatically. Put .up.sql files in the migrations directory, and Encore applies them on deploy.

Conclusion

Neon makes PostgreSQL work the way developers expect databases to work in 2026: instant provisioning, no capacity planning, and branching workflows that fit how teams actually build software.

For TypeScript backends, the fastest path from "I need a database" to "it's running in production" is combining Neon with a framework that handles the infrastructure layer. Encore.ts has built-in Neon integration that automates provisioning, migrations, connection pooling, and per-environment configuration. You declare a database in code, and it exists everywhere your app runs.

Get started with Encore and Neon:

curl -L https://encore.dev/install.sh | bash encore app create my-app --example=ts/hello-world

Read the database documentation for the full API reference, and the Neon integration guide for cloud deployment configuration.

Deploy an app with a database to see it in action:

Install Encore
brew install encoredev/tap/encore &&
encore app create --example=ts/url-shortener
Copy

Deploy to your cloud on AWS and Google Cloud
without managing infrastructure

Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.

~/orders — claude
Claude Code
Claude Code v2.1.180Opus 5.5~/orders