
Neon Postgres
- 46 installs
- 18 repo stars
- Updated June 8, 2026
- andrelandgraf/fullstackrecipes
neon-postgres is a Claude Code skill with guides and best practices for working with Neon serverless Postgres, including connection methods, drivers, auth, the CLI, and the Platform API.
About
This skill is a set of guides and best practices for working with Neon serverless Postgres, which separates compute and storage for autoscaling, branching, instant restore, and scale-to-zero. It covers getting started, connection methods and drivers, Neon Auth, the PostgREST-style data API, the Neon CLI, and the Platform API/SDKs, pointing to reference files for each. Developers use it for any Neon-related questions.
- Best practices for Neon serverless Postgres (branching, autoscaling, scale-to-zero)
- Guidance on choosing a connection method and driver
- Covers Neon Auth, Data API, CLI, REST API, and SDKs
Neon Postgres by the numbers
- 46 all-time installs (skills.sh)
- Ranked #428 of 911 Databases skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
neon-postgres capabilities & compatibility
Needs a Neon connection string and, for Platform API automation, a Neon API key; Neon has a free tier with autoscaling and scale-to-zero.
- Capabilities
- database setup · connection config · database branching
- Works with
- postgres
- Use cases
- database · devops
- Pricing
- Freemium
What neon-postgres says it does
Neon is a serverless Postgres platform that separates compute and storage to offer autoscaling, branching, instant restore, and scale-to-zero.
Branches are instant, copy-on-write clones (no full data copy).
npx skills add https://github.com/andrelandgraf/fullstackrecipes --skill neon-postgresAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 46 |
|---|---|
| repo stars | ★ 18 |
| Last updated | June 8, 2026 |
| Repository | andrelandgraf/fullstackrecipes ↗ |
What it does
Work with Neon serverless Postgres: pick a connection method and driver, use branching, auth, the CLI, and the Platform API.
Who is it for?
Developers working with Neon Postgres who need connection, branching, auth, or CLI guidance.
When should I use this skill?
Any Neon-related question or task.
What you get
- Neon connection setup
- Driver selection
- Branching workflow
By the numbers
- Default scale-to-zero suspend after 5 minutes idle
- 14 reference files bundled
Files
Neon Serverless Postgres
Neon is a serverless Postgres platform that separates compute and storage to offer autoscaling, branching, instant restore, and scale-to-zero. It's fully compatible with Postgres and works with any language, framework, or ORM that supports Postgres.
Neon Documentation
The Neon documentation is the source of truth for all Neon-related information. Always verify claims against the official docs before responding. Neon features and APIs evolve, so prefer fetching current docs over relying on training data.
Fetching Docs as Markdown
Any Neon doc page can be fetched as markdown in two ways:
1. Append `.md` to the URL (simplest): https://neon.com/docs/introduction/branching.md 2. Request `text/markdown` on the standard URL: curl -H "Accept: text/markdown" https://neon.com/docs/introduction/branching
Both return the same markdown content. Use whichever method your tools support.
Finding the Right Page
The docs index lists every available page with its URL and a short description:
https://neon.com/docs/llms.txtCommon doc URLs are organized in the topic links below. If you need a page not listed here, search the docs index — don't guess URLs.
What Is Neon
Use this for architecture explanations and terminology (organizations, projects, branches, endpoints) before giving implementation advice.
Link: references/what-is-neon.md
Getting Started
Use this for first-time setup: org/project selection, connection strings, driver installation, optional auth, and initial schema setup.
Link: references/getting-started.md
Connection Methods & Drivers
Use this when you need to pick the correct transport and driver based on runtime constraints (TCP, HTTP, WebSocket, edge, serverless, long-running).
Link: references/connection-methods.md
Serverless Driver
Use this for @neondatabase/serverless patterns, including HTTP queries, WebSocket transactions, and runtime-specific optimizations.
Link: references/neon-serverless.md
Neon JS SDK
Use this for combined Neon Auth + Data API workflows with PostgREST-style querying and typed client setup.
Link: references/neon-js.md
Developer Tools
Use this for local development enablement with npx neonctl@latest init, VSCode extension setup, and Neon MCP server configuration.
Link: references/devtools.md
Neon CLI
Use this for terminal-first workflows, scripts, and CI/CD automation with neonctl.
Link: references/neon-cli.md
Neon Admin API
The Neon Admin API can be used to manage Neon resources programmatically. It is used behind the scenes by the Neon CLI and MCP server, but can also be used directly for more complex automation workflows or when embedding Neon in other applications.
Neon REST API
Use this for direct HTTP automation, endpoint-level control, API key auth, rate-limit handling, and operation polling.
Link: references/neon-rest-api.md
Neon TypeScript SDK
Use this when implementing typed programmatic control of Neon resources in TypeScript via @neondatabase/api-client.
Link: references/neon-typescript-sdk.md
Neon Python SDK
Use this when implementing programmatic Neon management in Python with the neon-api package.
Link: references/neon-python-sdk.md
Neon Auth
Use this for managed user authentication setup, UI components, auth methods, and Neon Auth integration pitfalls in Next.js and React apps.
Link: references/neon-auth.md
Neon Auth is also embedded in the Neon JS SDK - so depending on your use case, you may want to use the Neon JS SDK instead of Neon Auth. See references/connection-methods.md for more details.
Branching
Use this when the user is planning isolated environments, schema migration testing, preview deployments, or branch lifecycle automation.
Key points:
- Branches are instant, copy-on-write clones (no full data copy).
- Each branch has its own compute endpoint.
- Use the neonctl CLI or MCP server to create, inspect, and compare branches.
Link: references/branching.md
Autoscaling
Use this when the user needs compute to scale automatically with workload and wants guidance on CU sizing and runtime behavior.
Link: https://neon.com/docs/introduction/autoscaling.md
Scale to Zero
Use this when optimizing idle costs and discussing suspend/resume behavior, including cold-start trade-offs.
Key points:
- Idle computes suspend automatically (default 5 minutes, configurable) (unless disabled - launch & scale plan only)
- First query after suspend typically has a cold-start penalty (around hundreds of ms)
- Storage remains active while compute is suspended.
Link: https://neon.com/docs/introduction/scale-to-zero.md
Instant Restore
Use this when the user needs point-in-time recovery or wants to restore data state without traditional backup restore workflows.
Key points:
- Restore windows depend on plan limits.
- Users can create branches from historical points-in-time.
- Time Travel queries can be used for historical inspection workflows.
Link: https://neon.com/docs/introduction/branch-restore.md
Read Replicas
Use this for read-heavy workloads where the user needs dedicated read-only compute without duplicating storage.
Key points:
- Replicas are read-only compute endpoints sharing the same storage.
- Creation is fast and scaling is independent from primary compute.
- Typical use cases: analytics, reporting, and read-heavy APIs.
Link: https://neon.com/docs/introduction/read-replicas.md
Connection Pooling
Use this when the user is in serverless or high-concurrency environments and needs safe, scalable Postgres connection management.
Key points:
- Neon pooling uses PgBouncer.
- Add
-poolerto endpoint hostnames to use pooled connections. - Pooling is especially important in serverless runtimes with bursty concurrency.
Link: https://neon.com/docs/connect/connection-pooling.md
IP Allow Lists
Use this when the user needs to restrict database access by trusted networks, IPs, or CIDR ranges.
Link: https://neon.com/docs/introduction/ip-allow.md
Logical Replication
Use this when integrating CDC pipelines, external Postgres sync, or replication-based data movement.
Key points:
- Neon supports native logical replication workflows.
- Useful for replicating to/from external Postgres systems.
Link: https://neon.com/docs/guides/logical-replication-guide.md
Branching in Neon
Neon branching gives you instant, copy-on-write database environments. Use this for preview environments, migration testing, branch-based isolation, and restore-style workflows.
Product Concept and Branching Behavior
Start here for branching mechanics, parent-child model, and practical usage patterns.
Link: https://neon.com/docs/introduction/branching.md
Branch Operations via REST API
Use this when automating branch creation, listing, or deletion through direct HTTP integration.
Link: references/neon-rest-api.md
Branch Operations via CLI
Use this for terminal workflows and CI jobs that create and clean up temporary branches.
Link: references/neon-cli.md
Branch Operations via MCP Server
Use this when an AI assistant should create, inspect, or compare branches directly from tool calls.
Link: https://neon.com/docs/ai/neon-mcp-server.md
Connection Methods
Guide to selecting the optimal connection method for your Neon Postgres database based on deployment platform and runtime environment.
See the official connection guide for complete details.
Decision Tree
Follow this flow to determine the right connection approach:
1. What Language Are You Using?
Not TypeScript/JavaScript → Use TCP with connection pooling from a secure server.
For non-TypeScript languages, connect from a secure backend server using your language's native Postgres driver with connection pooling enabled.
| Language/Framework | Documentation |
|---|---|
| Django (Python) | https://neon.com/docs/guides/django.md |
| SQLAlchemy (Python) | https://neon.com/docs/guides/sqlalchemy.md |
| Elixir Ecto | https://neon.com/docs/guides/elixir-ecto.md |
| Laravel (PHP) | https://neon.com/docs/guides/laravel.md |
| Ruby on Rails | https://neon.com/docs/guides/ruby-on-rails.md |
| Go | https://neon.com/docs/guides/go.md |
| Rust | https://neon.com/docs/guides/rust.md |
| Java | https://neon.com/docs/guides/java.md |
TypeScript/JavaScript → Continue to step 2.
---
2. Client-Side App Without Backend?
Yes → Use Neon Data API via @neondatabase/neon-js
This is the only option for client-side apps since browsers cannot make direct TCP connections to Postgres. See neon-js.md for setup and the JavaScript SDK docs for the full reference.
No → Continue to step 3.
---
3. Long-Running Server? (Railway, Render, traditional VPS)
Yes → Use TCP with connection pooling via node-postgres, postgres.js, or bun:pg
Long-running servers maintain persistent connections, so standard TCP drivers with pooling are optimal.
No → Continue to step 4.
---
4. Edge Environment Without TCP Support?
Some edge runtimes don't support TCP connections. Rarely the case anymore.
Yes → Continue to step 5 to check transaction requirements.
No → Continue to step 6 to check pooling support.
---
5. Does Your App Use SQL Transactions?
Yes → Use WebSocket transport via @neondatabase/serverless with Pool
WebSocket maintains connection state needed for transactions. See neon-serverless.md for setup.
No → Use HTTP transport via @neondatabase/serverless
HTTP is faster for single queries (~3 roundtrips vs ~8 for TCP). See neon-serverless.md for setup and the serverless driver docs for the full reference.
---
6. Serverless Environment With Connection Pooling Support?
Vercel (Fluid Compute) → Use TCP with `@vercel/functions`
Vercel's Fluid compute supports connection pooling. Use attachDatabasePool for optimal connection management. See the Vercel connection methods guide for details.
Cloudflare (with Hyperdrive) → Use TCP via Hyperdrive
Cloudflare Hyperdrive provides connection pooling for Workers. Use node-postgres or any native TCP driver.
See the Cloudflare Hyperdrive guide for connecting with Cloudflare Workers and Hyperdrive.
Also consider Placement Hints to ensure your Workers are deployed as close as possible to your Neon database - read the Placement Configuration docs for details.
No pooling support (Netlify, Deno Deploy) → Use @neondatabase/serverless
Fall back to the decision in step 5 based on transaction requirements.
---
Quick Reference Table
| Platform | TCP Support | Pooling | Recommended Driver |
|---|---|---|---|
| Vercel (Fluid) | Yes | @vercel/functions | pg (node-postgres) |
| Cloudflare (Hyperdrive) | Yes | Hyperdrive | pg (node-postgres) |
| Cloudflare Workers | No | No | @neondatabase/serverless |
| Netlify Functions | No | No | @neondatabase/serverless |
| Deno Deploy | No | No | @neondatabase/serverless |
| Railway / Render | Yes | Built-in | pg (node-postgres) |
| Client-side (browser) | No | N/A | @neondatabase/neon-js |
---
ORM Support
Popular TypeScript/JavaScript ORMs all work with Neon:
| ORM | Drivers Supported | Documentation |
|---|---|---|
| Drizzle | pg, postgres.js, @neondatabase/serverless | https://neon.com/docs/guides/drizzle.md |
| Kysely | pg, postgres.js, @neondatabase/serverless | https://neon.com/docs/guides/kysely.md |
| Prisma | pg, @neondatabase/serverless | https://neon.com/docs/guides/prisma.md |
| TypeORM | pg | https://neon.com/docs/guides/typeorm.md |
All ORMs support both TCP drivers and Neon's serverless driver depending on your platform.
For Drizzle ORM integration with Neon, see neon-drizzle.md.
---
Vercel Fluid + Drizzle Example
Complete database client setup for Vercel with Drizzle ORM and connection pooling. See neon-drizzle.md for more examples.
// src/lib/db/client.ts
import { attachDatabasePool } from "@vercel/functions";
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
import * as schema from "./schema";
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
attachDatabasePool(pool);
export const db = drizzle({ client: pool, schema });Why `attachDatabasePool`?
- First request establishes the TCP connection (~8 roundtrips)
- Subsequent requests reuse the connection instantly
- Ensures idle connections close gracefully before function suspension
- Prevents connection leaks in serverless environments
---
Gathering Requirements
When helping a user choose their connection method, gather this information:
1. Deployment platform: Where will the app run? (Vercel, Cloudflare, Netlify, Railway, browser, etc.) 2. Runtime type: Serverless functions, edge functions, or long-running server? 3. Transaction requirements: Does the app need SQL transactions? 4. ORM preference: Using Drizzle, Kysely, Prisma, or raw SQL?
Then provide:
- The recommended driver/package
- A working code example for their setup
- The correct npm install command
---
Documentation Resources
| Topic | URL |
|---|---|
| Choosing Connection Method | https://neon.com/docs/connect/choose-connection.md |
| Serverless Driver | https://neon.com/docs/serverless/serverless-driver.md |
| JavaScript SDK | https://neon.com/docs/reference/javascript-sdk.md |
| Connection Pooling | https://neon.com/docs/connect/connection-pooling.md |
| Vercel Connection Methods | https://neon.com/docs/guides/vercel-connection-methods.md |
Neon Developer Tools
Neon provides developer tools to enhance your local development workflow, including a VSCode extension and MCP server for AI-assisted development.
Quick Setup with neon init
The fastest way to set up all Neon developer tools:
npx neonctl@latest initThis command:
- Installs the Neon VSCode extension
- Configures the Neon MCP server for AI assistants
- Sets up your local environment for Neon development
See the full CLI init reference for all options.
VSCode Extension
The Neon VSCode extension provides:
- Database Explorer: Browse projects, branches, tables, and data
- SQL Editor: Write and execute queries with IntelliSense
- Branch Management: Create, switch, and manage database branches
- Connection String Access: Quick copy of connection strings
Install from VSCode:
1. Open Extensions (Cmd/Ctrl+Shift+X) 2. Search "Neon" 3. Install "Neon" by Neon
Or via command line:
code --install-extension neon.neon-vscodeSee the full VSCode extension docs for all features.
Neon MCP Server
The Neon MCP (Model Context Protocol) server enables AI assistants like Claude, Cursor, and GitHub Copilot to interact with your Neon databases directly.
Capabilities
The MCP server provides AI assistants with:
- Project Management: List, create, describe, and delete projects
- Branch Operations: Create branches, compare schemas, reset from parent
- SQL Execution: Run queries and transactions
- Schema Operations: Describe tables, get database structure
- Migrations: Prepare and complete database migrations with safety checks
- Query Tuning: Analyze and optimize slow queries
- Neon Auth: Provision authentication for your branches
Setup
Option 1: Via neon init (Recommended)
npx neonctl@latest initOption 2: Manual Configuration
Add to your AI assistant's MCP configuration:
{
"mcpServers": {
"neon": {
"command": "npx",
"args": ["-y", "@neondatabase/mcp-server-neon"],
"env": {
"NEON_API_KEY": "your-api-key"
}
}
}
}Get your API key from: https://console.neon.tech/app/settings/api-keys
Common MCP Operations
| Operation | What It Does |
|---|---|
list_projects | Show all Neon projects |
create_project | Create a new project |
run_sql | Execute SQL queries |
get_connection_string | Get database connection URL |
create_branch | Create a database branch |
prepare_database_migration | Safely prepare schema changes |
provision_neon_auth | Set up Neon Auth |
See the full MCP server docs for all available operations.
Documentation Resources
| Topic | URL |
|---|---|
| CLI Init Command | https://neon.com/docs/reference/cli-init.md |
| VSCode Extension | https://neon.com/docs/local/vscode-extension.md |
| MCP Server | https://neon.com/docs/ai/neon-mcp-server.md |
| Neon CLI Reference | https://neon.com/docs/reference/neon-cli.md |
Getting Started with Neon
Interactive guide for setting up a Neon project and connecting it to code.
Check Status Quo
Inspect the user's codebase and environment to see if they have already integrated Neon and to better understand their needs and constraints.
Specifically check for:
- Existing database connection code
- Existing Neon MCP server or Neon CLI configuration
- Existence of a
.envfile andDATABASE_URLenvironment variable - Existing ORM (Prisma, Drizzle, TypeORM) configuration
Self-Driving Setup With Neon's CLI or MCP Server
You can offer the user to inspect the existing connected Neon projects or create new ones using the Neon CLI or MCP server.
If the MCP server and CLI aren't set up yet, ask the user for permission to run:
npx neonctl@latest initThis will install the Neon VSCode extension (if applicable) and the Neon MCP server and neon-postgres agent skill. Alternatively, you can offer to install the Neon CLI. Install instructions here: https://neon.com/docs/reference/cli-install.md
Either CLI or MCP server can be used to manage Neon projects and databases on the user's behalf. If the user prefers to manually get started with Neon, then you can guide them through the setup process instead of using the CLI or MCP server directly. See devtools.md for details.
Since the Neon CLI and MCP server do interact with database resources, it's important to verify the user is comfortable with the security implications of running these tools.
Setup Flow
1. Select Organization and Project
- Check existing organizations and projects (via MCP server or CLI or manually by the user)
- 1 organization: default to it
- Multiple organizations: list all and ask which to use
- No projects: ask if they want to create a new project
- 1 project: ask "Would you like to use '{project_name}' or create a new one?"
- Multiple projects (<6): list all and let them choose
- Many projects (6+): list recent projects, offer to create new or specify by name/ID
2. Get Connection String
- Use MCP server or CLI to get the connection string
- Store it in
.envasDATABASE_URL:
DATABASE_URL=postgresql://user:password@host/databaseBefore modifying `.env`:
1. Try to read the .env file first 2. If readable: use search/replace to update or append DATABASE_URL 3. If unreadable (permissions): use append command or show the line to add manually 4. Never overwrite an existing .env — always append or update in place
3. Pick Connection Method & Pick Driver
Refer to connection-methods.md to pick the correct connection method and driver based on your deployment platform.
3.1. User Authentication with Neon Auth (if needed)
Skip for CLI tools, scripts, or apps without user accounts.
If the app needs auth: use MCP server provision_neon_auth tool, then see neon-auth.md for setup. For auth + database queries, see neon-js.md.
3.2. ORM Setup (optional)
Check for existing ORM (Prisma, Drizzle, TypeORM). If none, ask if they want one. For Drizzle integration, see neon-drizzle.md.
6. Schema Setup
- Check for existing migration files or ORM schemas
- If none: offer to create an example schema or design one together
What's Next
After setup is complete, offer to help with:
- Neon-specific features (branching, autoscaling, scale-to-zero)
- Connection pooling for production
- Writing queries or building API endpoints
- Database migrations and schema changes
- Performance optimization
Resume Support
If the user says "Continue with Neon setup", check what's already configured:
- MCP server connection
.envfile withDATABASE_URL- Dependencies installed
- Schema created
Then resume from where they left off.
Security Reminders
- Never commit connection strings to version control
- Use environment variables for all credentials
- Prefer SSL connections (default in Neon)
- Use least-privilege database roles
- Rotate API keys and passwords regularly
Documentation
| Topic | URL |
|---|---|
| Getting Started | https://neon.com/docs/get-started/signing-up.md |
| Connecting to Neon | https://neon.com/docs/connect/connect-intro.md |
| Connection String | https://neon.com/docs/connect/connect-from-any-app.md |
| Frameworks Guide | https://neon.com/docs/get-started/frameworks.md |
| ORMs Guide | https://neon.com/docs/get-started/orms.md |
| VSCode Extension | https://neon.com/docs/local/vscode-extension.md |
| MCP Server | https://neon.com/docs/ai/neon-mcp-server.md |
Neon Auth
Neon Auth provides managed authentication that stores users, sessions, and auth configuration directly in your Neon database. When you branch your database, your entire auth state branches with it.
See the official Neon Auth docs for complete details.
Package Selection
| Framework / Use Case | Package | Notes |
|---|---|---|
| Next.js | @neondatabase/auth | Server + client SDK |
| React SPA (Vite, etc) | @neondatabase/neon-js | Client SDK + optional Data API |
| Auth + Database queries | @neondatabase/neon-js | Full SDK |
Both packages share auth exports (@neondatabase/neon-js/auth/* re-exports @neondatabase/auth/*).
# Next.js
npm install @neondatabase/auth@latest
# React SPA / Full SDK
npm install @neondatabase/neon-js@latestNote: While these packages are in pre-release (beta), you must use @latest with npm. Without it, npm may install an older version. This is not needed with pnpm or yarn.Next.js Setup
1. Server auth instance (lib/auth/server.ts):
import { createNeonAuth } from "@neondatabase/auth/next/server";
export const auth = createNeonAuth({
baseUrl: process.env.NEON_AUTH_BASE_URL!,
cookies: {
secret: process.env.NEON_AUTH_COOKIE_SECRET!,
},
});2. API route handler (app/api/auth/[...path]/route.ts):
import { auth } from "@/lib/auth/server";
export const { GET, POST } = auth.handler();3. Middleware (middleware.ts):
import { auth } from "@/lib/auth/server";
export default auth.middleware({
loginUrl: "/auth/sign-in",
});
export const config = {
matcher: ["/account/:path*"],
};4. Client (lib/auth/client.ts):
"use client";
import { createAuthClient } from "@neondatabase/auth/next";
export const authClient = createAuthClient();5. Server component access (must set force-dynamic):
import { auth } from "@/lib/auth/server";
export const dynamic = "force-dynamic";
export default async function DashboardPage() {
const { data: session } = await auth.getSession();
if (!session?.user) return <div>Not logged in</div>;
return <div>Hello {session.user.name}</div>;
}6. UI setup — Add the provider, CSS, and page components. See UI Components below for NeonAuthUIProvider, CSS imports, AuthView, and AccountView setup.
See the Next.js quickstart and server SDK reference for the full setup.
Environment Variables (Next.js)
NEON_AUTH_BASE_URL=https://ep-xxx.neonauth.us-east-1.aws.neon.tech/neondb/auth
NEON_AUTH_COOKIE_SECRET=your-secret-at-least-32-characters-longGet your Auth URL from the Neon Console: Project -> Branch -> Auth -> Configuration.
Generate a cookie secret: openssl rand -base64 32
React SPA Setup
1. Auth client (lib/auth.ts):
import { createAuthClient } from "@neondatabase/neon-js/auth";
export const authClient = createAuthClient(import.meta.env.VITE_NEON_AUTH_URL);If you need useSession() in custom components, pass an adapter:
import { BetterAuthReactAdapter } from "@neondatabase/neon-js/auth/react";
const authClient = createAuthClient(import.meta.env.VITE_NEON_AUTH_URL, {
adapter: BetterAuthReactAdapter(),
});UI components (AuthView, SignedIn, etc.) work without an adapter.
2. UI setup — Wrap your app with NeonAuthUIProvider and import CSS. See UI Components below. In a SPA, the provider and CSS go in your root component (e.g., App.tsx or your router layout).
3. Routing — Map AuthView and AccountView to routes in your router (React Router, TanStack Router, etc.). For example, with React Router:
<Route path="/auth/:path" element={<AuthView />} />
<Route path="/account/:path" element={<AccountView />} />Environment Variables (React SPA)
VITE_NEON_AUTH_URL=https://ep-xxx.neonauth.us-east-1.aws.neon.tech/neondb/authSee the React quickstart with UI components and React API-only quickstart for the full setup.
UI Components
Use pre-built components instead of building custom auth forms.
| Component | Purpose |
|---|---|
AuthView | Sign-in, sign-up, forgot-password pages |
AccountView | Account settings, security pages |
UserButton | User avatar with dropdown menu |
SignedIn / SignedOut | Conditional rendering based on auth state |
RedirectToSignIn | Redirect unauthenticated users |
RedirectToSignUp | Redirect to sign-up page |
See the UI components reference for full props and customization.
CSS (choose one, never both)
The CSS import path depends on which package you installed:
// Next.js (@neondatabase/auth)
import "@neondatabase/auth/ui/css";
// React SPA (@neondatabase/neon-js)
import "@neondatabase/neon-js/ui/css";/* With Tailwind v4 — Next.js */
@import "tailwindcss";
@import "@neondatabase/auth/ui/tailwind";
/* With Tailwind v4 — React SPA */
@import "tailwindcss";
@import "@neondatabase/neon-js/ui/tailwind";Provider Setup
Wrap your app with NeonAuthUIProvider. Only authClient is required.
In Next.js, add suppressHydrationWarning to the <html> tag in your root layout — the provider injects theme attributes (className="light", color-scheme) client-side that don't exist in the server render:
// app/layout.tsx
import { NeonAuthUIProvider, UserButton } from "@neondatabase/auth/react";
import { authClient } from "@/lib/auth/client";
export default function RootLayout({ children }) {
return (
<html lang="en" suppressHydrationWarning>
<body>
<NeonAuthUIProvider
authClient={authClient}
social={{ providers: ["google", "github", "vercel"] }} // optional
>
{children}
</NeonAuthUIProvider>
</body>
</html>
);
}Social login requires TWO configurations: enable in Neon Console AND add social prop to provider.
AuthView (Next.js)
Create app/auth/[path]/page.tsx:
import { AuthView } from "@neondatabase/auth/react";
export const dynamicParams = false;
export default async function AuthPage({
params,
}: {
params: Promise<{ path: string }>;
}) {
const { path } = await params;
return <AuthView path={path} />;
}Auth paths: sign-in, sign-up, forgot-password, reset-password, magic-link, two-factor, callback, sign-out
AccountView (Next.js)
Create app/account/[path]/page.tsx:
import { AccountView } from "@neondatabase/auth/react";
import { accountViewPaths } from "@neondatabase/auth/react/ui/server";
export const dynamicParams = false;
export function generateStaticParams() {
return Object.values(accountViewPaths).map((path) => ({ path }));
}
export default async function AccountPage({
params,
}: {
params: Promise<{ path: string }>;
}) {
const { path } = await params;
return <AccountView path={path} />;
}Account paths: settings, security
Conditional Rendering
import { SignedIn, SignedOut, UserButton } from "@neondatabase/auth/react";
<SignedOut>
<a href="/auth/sign-in">Sign In</a>
</SignedOut>
<SignedIn>
<UserButton />
</SignedIn>Auth Methods Quick Reference
| Method | Usage |
|---|---|
auth.signUp.email({ email, password, name }) | Create account (server) |
auth.signIn.email({ email, password }) | Sign in (server) |
auth.signIn.social({ provider, callbackURL }) | OAuth sign-in (server) |
auth.signOut() | Sign out (server) |
auth.getSession() | Get session (server, requires force-dynamic) |
authClient.useSession() | Session hook (client, needs React adapter) |
authClient.getSession() | Get session (client, no adapter needed) |
authClient.signIn.email(...) | Sign in (client) |
authClient.signUp.email(...) | Create account (client) |
Session Data
const { data: session } = await auth.getSession();
// session.user: { id, name, email, image, emailVerified, createdAt, updatedAt }
// session.session: { id, expiresAt, token, createdAt, updatedAt, userId }Error Handling
const { error } = await auth.signIn.email({ email, password });
if (error) {
// error.code: "INVALID_EMAIL_OR_PASSWORD", "EMAIL_NOT_VERIFIED",
// "USER_NOT_FOUND", "TOO_MANY_REQUESTS"
console.error(error.message);
}Key Imports
// Server (Next.js)
import { createNeonAuth } from "@neondatabase/auth/next/server";
// Client (Next.js) -- includes React adapter automatically
import { createAuthClient } from "@neondatabase/auth/next";
// Client (React SPA)
import { createAuthClient } from "@neondatabase/neon-js/auth";
// React adapter (only needed for useSession() in React SPA)
import { BetterAuthReactAdapter } from "@neondatabase/neon-js/auth/react";
// UI components
import {
NeonAuthUIProvider,
AuthView,
AccountView,
SignedIn,
SignedOut,
UserButton,
} from "@neondatabase/auth/react";
import { accountViewPaths } from "@neondatabase/auth/react/ui/server";
// CSS (choose one, never both; path matches your package)
import "@neondatabase/auth/ui/css"; // Next.js
import "@neondatabase/neon-js/ui/css"; // React SPA
// or in CSS: @import "@neondatabase/auth/ui/tailwind"; (Next.js)
// or in CSS: @import "@neondatabase/neon-js/ui/tailwind"; (React SPA)Common Mistakes
Missing NEON_AUTH_COOKIE_SECRET
Required for Next.js, must be 32+ characters for HMAC-SHA256. Generate with openssl rand -base64 32.
Missing force-dynamic on server components
// WRONG -- will error
export default async function Page() {
const { data: session } = await auth.getSession();
}
// CORRECT
export const dynamic = "force-dynamic";
export default async function Page() {
const { data: session } = await auth.getSession();
}Using v0.1 API patterns
Use createNeonAuth() + auth.handler(), not the old standalone authApiHandler(). See the migration guide.
Using useSession() without adapter in React SPA
createAuthClient(url) without an adapter returns a vanilla client with no React hooks. Either pass BetterAuthReactAdapter() or use UI components (SignedIn, etc.) which don't require an adapter.
Wrong BetterAuthReactAdapter import
Must use subpath import and call as function:
// WRONG
import { BetterAuthReactAdapter } from "@neondatabase/neon-js";
// CORRECT
import { BetterAuthReactAdapter } from "@neondatabase/neon-js/auth/react";
const client = createAuthClient(url, { adapter: BetterAuthReactAdapter() });CSS import conflicts
Choose ONE: ui/css (without Tailwind) or ui/tailwind (with Tailwind v4). Never import both -- causes ~94KB of duplicate styles.
Missing "use client" directive
Required for any component using useSession() or other React hooks.
Wrong createAuthClient signature
URL is the first argument, not a property in an options object:
// WRONG
createAuthClient({ url: myUrl });
// CORRECT (React SPA)
createAuthClient(url);
createAuthClient(url, { adapter: BetterAuthReactAdapter() });
// CORRECT (Next.js) -- no arguments, uses proxy
createAuthClient();Documentation
| Topic | URL |
|---|---|
| Auth Overview | https://neon.com/docs/auth/overview.md |
| Next.js Quickstart | https://neon.com/docs/auth/quick-start/nextjs.md |
| Next.js API-only | https://neon.com/docs/auth/quick-start/nextjs-api-only.md |
| React with UI | https://neon.com/docs/auth/quick-start/react-router-components.md |
| React API Methods | https://neon.com/docs/auth/quick-start/react.md |
| TanStack Router | https://neon.com/docs/auth/quick-start/tanstack-router.md |
| Server SDK Reference | https://neon.com/docs/auth/reference/nextjs-server.md |
| UI Components Ref | https://neon.com/docs/auth/reference/ui-components.md |
| Client SDK Reference | https://neon.com/docs/reference/javascript-sdk.md |
| v0.1 Migration Guide | https://neon.com/docs/auth/migrate/from-auth-v0.1.md |
| OAuth Setup | https://neon.com/docs/auth/guides/setup-oauth.md |
| Email Verification | https://neon.com/docs/auth/guides/email-verification.md |
| Branching Auth | https://neon.com/docs/auth/branching-authentication.md |
Neon CLI
The Neon CLI is a command-line interface for managing Neon Serverless Postgres directly from your terminal. It provides the same capabilities as the Neon Platform API and is ideal for scripting, CI/CD pipelines, and developers who prefer terminal workflows.
Installation
macOS (Homebrew):
brew install neonctlnpm (cross-platform):
npm install -g neonctlAuthentication
Authenticate with your Neon account:
neonctl authThis opens a browser for OAuth authentication and stores credentials locally.
For CI/CD or non-interactive environments, use an API key:
export NEON_API_KEY=your-api-keyGet your API key from: https://console.neon.tech/app/settings/api-keys
Common Commands
Project Management
# List all projects (org-scoped)
neonctl projects list --org-id <org-id>
# Create a new project
neonctl projects create --name my-project --org-id <org-id>
# Get project details
neonctl projects get <project-id>
# Delete a project
neonctl projects delete <project-id>Branch Operations
# List branches
neonctl branches list --project-id <project-id>
# Create a branch
neonctl branches create --project-id <project-id> --name dev
# Delete a branch
neonctl branches delete <branch-id> --project-id <project-id>Connection Strings
# Get connection string
neonctl connection-string --project-id <project-id>
# Get connection string for specific branch
neonctl connection-string --project-id <project-id> --branch-id <branch-id>
# Get pooled connection string
neonctl connection-string --project-id <project-id> --pooledSQL Execution
# Run SQL query
neonctl sql "SELECT * FROM users LIMIT 10" --project-id <project-id>
# Run SQL from file
neonctl sql --file schema.sql --project-id <project-id>Database Management
# List databases
neonctl databases list --project-id <project-id> --branch-id <branch-id>
# Create database
neonctl databases create --project-id <project-id> --name mydb
# List roles
neonctl roles list --project-id <project-id> --branch-id <branch-id>Output Formats
The CLI supports multiple output formats:
# JSON output (default for scripting)
neonctl projects list --output json
# Table output (human-readable)
neonctl projects list --output table
# YAML output
neonctl projects list --output yamlCI/CD Integration
Example GitHub Actions workflow:
- name: Create preview branch
env:
NEON_API_KEY: ${{ secrets.NEON_API_KEY }}
run: |
neonctl branches create \
--project-id ${{ vars.NEON_PROJECT_ID }} \
--name preview-${{ github.event.pull_request.number }}CLI vs MCP Server vs SDKs
| Tool | Best For |
|---|---|
| Neon CLI | Terminal workflows, scripts, CI/CD pipelines |
| MCP Server | AI-assisted development with Claude, Cursor, etc. |
| TypeScript SDK | Programmatic access in Node.js/TypeScript apps |
| Python SDK | Programmatic access in Python applications |
| REST API | Direct HTTP integration in any language |
Documentation Resources
| Topic | URL |
|---|---|
| CLI Reference | https://neon.com/docs/reference/neon-cli.md |
| CLI Install | https://neon.com/docs/reference/cli-install.md |
| CLI Auth | https://neon.com/docs/reference/cli-auth.md |
| CLI Projects | https://neon.com/docs/reference/cli-projects.md |
| CLI Branches | https://neon.com/docs/reference/cli-branches.md |
| CLI Connection | https://neon.com/docs/reference/cli-connection-string.md |
See the full CLI docs for the complete command reference.
Neon and Drizzle Integration
Integration patterns, configurations, and optimizations for using Drizzle ORM with Neon Postgres.
See the official Drizzle guide for complete details.
Choosing the Right Driver
Drizzle ORM works with multiple Postgres drivers. See connection-methods.md for the full decision tree.
| Platform | TCP Support | Pooling | Recommended Driver |
|---|---|---|---|
| Vercel (Fluid) | Yes | @vercel/functions | pg (node-postgres) |
| Cloudflare (Hyperdrive) | Yes | Hyperdrive | pg (node-postgres) |
| Cloudflare Workers | No | No | @neondatabase/serverless |
| Netlify Functions | No | No | @neondatabase/serverless |
| Deno Deploy | No | No | @neondatabase/serverless |
| Railway / Render | Yes | Built-in | pg (node-postgres) |
Connection Setup
1. TCP with node-postgres (Long-Running Servers)
Best for Railway, Render, traditional VPS.
npm install drizzle-orm pg
npm install -D drizzle-kit @types/pg dotenv// src/db.ts
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
export const db = drizzle({ client: pool });2. Vercel Fluid Compute with Connection Pooling
npm install drizzle-orm pg @vercel/functions
npm install -D drizzle-kit @types/pg// src/db.ts
import { attachDatabasePool } from "@vercel/functions";
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
import * as schema from "./schema";
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
attachDatabasePool(pool);
export const db = drizzle({ client: pool, schema });3. HTTP Adapter (Edge Without TCP)
For Cloudflare Workers, Netlify Edge, Deno Deploy. Does NOT support interactive transactions.
npm install drizzle-orm @neondatabase/serverless
npm install -D drizzle-kit dotenv// src/db.ts
import { drizzle } from "drizzle-orm/neon-http";
import { neon } from "@neondatabase/serverless";
const sql = neon(process.env.DATABASE_URL!);
export const db = drizzle(sql);4. WebSocket Adapter (Edge with Transactions)
npm install drizzle-orm @neondatabase/serverless ws
npm install -D drizzle-kit dotenv @types/ws// src/db.ts
import { drizzle } from "drizzle-orm/neon-serverless";
import { Pool, neonConfig } from "@neondatabase/serverless";
import ws from "ws";
neonConfig.webSocketConstructor = ws; // Required for Node.js < v22
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
export const db = drizzle(pool);Drizzle Config
// drizzle.config.ts
import { config } from "dotenv";
import { defineConfig } from "drizzle-kit";
config({ path: ".env.local" });
export default defineConfig({
schema: "./src/schema.ts",
out: "./drizzle",
dialect: "postgresql",
dbCredentials: {
url: process.env.DATABASE_URL!,
},
});Migrations
# Generate migrations
npx drizzle-kit generate
# Apply migrations
npx drizzle-kit migrateSchema Definition
// src/schema.ts
import { pgTable, serial, text, integer, timestamp } from "drizzle-orm/pg-core";
export const usersTable = pgTable("users", {
id: serial("id").primaryKey(),
name: text("name").notNull(),
email: text("email").notNull().unique(),
role: text("role").default("user").notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
export type User = typeof usersTable.$inferSelect;
export type NewUser = typeof usersTable.$inferInsert;
export const postsTable = pgTable("posts", {
id: serial("id").primaryKey(),
title: text("title").notNull(),
content: text("content").notNull(),
userId: integer("user_id")
.notNull()
.references(() => usersTable.id, { onDelete: "cascade" }),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
export type Post = typeof postsTable.$inferSelect;
export type NewPost = typeof postsTable.$inferInsert;Query Patterns
Batch Inserts
export async function batchInsertUsers(users: NewUser[]) {
return db.insert(usersTable).values(users).returning();
}Prepared Statements
import { sql } from "drizzle-orm";
export const getUsersByRolePrepared = db
.select()
.from(usersTable)
.where(sql`${usersTable.role} = $1`)
.prepare("get_users_by_role");
// Usage: getUsersByRolePrepared.execute(['admin'])Transactions
export async function createUserWithPosts(user: NewUser, posts: NewPost[]) {
return await db.transaction(async (tx) => {
const [newUser] = await tx.insert(usersTable).values(user).returning();
if (posts.length > 0) {
await tx.insert(postsTable).values(
posts.map((post) => ({
...post,
userId: newUser.id,
})),
);
}
return newUser;
});
}Working with Neon Branches
import { drizzle } from "drizzle-orm/neon-http";
import { neon } from "@neondatabase/serverless";
const getBranchUrl = () => {
const env = process.env.NODE_ENV;
if (env === "development") return process.env.DEV_DATABASE_URL;
if (env === "test") return process.env.TEST_DATABASE_URL;
return process.env.DATABASE_URL;
};
const sql = neon(getBranchUrl()!);
export const db = drizzle({ client: sql });Error Handling
export async function safeNeonOperation<T>(
operation: () => Promise<T>,
): Promise<T> {
try {
return await operation();
} catch (error: any) {
if (error.message?.includes("connection pool timeout")) {
console.error("Neon connection pool timeout");
}
throw error;
}
}Best Practices
1. Connection Management - See connection-methods.md for platform-specific guidance 2. Neon Features - Utilize branching for development/testing 3. Query Optimization - Batch operations, use prepared statements 4. Schema Design - Leverage Postgres-specific features, use appropriate indexes
Neon JS SDK
The @neondatabase/neon-js SDK provides a unified client for Neon Auth and Data API. It combines authentication handling with PostgREST-compatible database queries.
Auth only? Use @neondatabase/auth instead (see neon-auth.md) for smaller bundle size.
See the official JavaScript SDK docs for complete details.
Package Selection
| Use Case | Package | Notes |
|---|---|---|
| Auth + Data API | @neondatabase/neon-js | Full SDK |
| Auth only | @neondatabase/auth | Smaller bundle |
| Data API only | @neondatabase/postgrest-js | Bring your own auth |
Installation
npm install @neondatabase/neon-js@latestNote: While this package is in pre-release (beta), you must use @latest with npm. Without it, npm may install an older version. This is not needed with pnpm or yarn.Quick Setup Patterns
Next.js
1. Server Auth Instance:
// lib/auth/server.ts
import { createNeonAuth } from "@neondatabase/neon-js/auth/next/server";
export const auth = createNeonAuth({
baseUrl: process.env.NEON_AUTH_BASE_URL!,
cookies: {
secret: process.env.NEON_AUTH_COOKIE_SECRET!,
},
});2. API Route Handler:
// app/api/auth/[...path]/route.ts
import { auth } from "@/lib/auth/server";
export const { GET, POST } = auth.handler();3. Auth Client:
// lib/auth/client.ts
"use client";
import { createAuthClient } from "@neondatabase/neon-js/auth/next";
export const authClient = createAuthClient();4. Database Client:
// lib/db/client.ts
import { createClient } from "@neondatabase/neon-js";
import type { Database } from "./database.types";
export const dbClient = createClient<Database>({
auth: { url: process.env.NEON_AUTH_BASE_URL! },
dataApi: { url: process.env.NEON_DATA_API_URL! },
});5. Middleware + UI setup — See Neon Auth reference for middleware configuration, NeonAuthUIProvider, CSS imports, and AuthView/AccountView page components.
React SPA
import { createAuthClient } from "@neondatabase/neon-js/auth";
const authClient = createAuthClient(import.meta.env.VITE_NEON_AUTH_URL);Note: If you need React hooks like useSession() in custom components, pass an adapter:createAuthClient(url, { adapter: BetterAuthReactAdapter() }).UI components (AuthView,SignedIn, etc.) do not require an adapter.
React SPA with Data API
import { createClient } from "@neondatabase/neon-js";
const client = createClient<Database>({
auth: { url: import.meta.env.VITE_NEON_AUTH_URL },
dataApi: { url: import.meta.env.VITE_NEON_DATA_API_URL },
});
export const authClient = client.auth;Environment Variables
# Next.js (.env)
NEON_AUTH_BASE_URL=https://ep-xxx.neonauth.us-east-1.aws.neon.tech/neondb/auth
NEON_AUTH_COOKIE_SECRET=your-secret-at-least-32-characters-long
NEON_DATA_API_URL=https://ep-xxx.apirest.us-east-1.aws.neon.tech/neondb/rest/v1
# Vite/React (.env)
VITE_NEON_AUTH_URL=https://ep-xxx.neonauth.us-east-1.aws.neon.tech/neondb/auth
VITE_NEON_DATA_API_URL=https://ep-xxx.apirest.us-east-1.aws.neon.tech/neondb/rest/v1Get your Auth URL from the Neon Console: Project -> Branch -> Auth -> Configuration.
Generate a cookie secret: openssl rand -base64 32
Database Queries (PostgREST / Data API)
Prerequisite: The Data API must be enabled per branch before making queries. Enable it via the Neon Console (Project → Data API), the MCP server'sprovision_neon_data_apitool, or the REST API (POST /projects/{project_id}/branches/{branch_id}/data-api/{database_name}). Without it, requests will return 404.
All query methods follow PostgREST syntax (same as Supabase).
// Select with filters
const { data } = await client
.from("items")
.select("id, name, status")
.eq("status", "active")
.order("created_at", { ascending: false })
.limit(10);
// Select single row
const { data, error } = await client
.from("items")
.select("*")
.eq("id", 1)
.single();
// Insert (returns inserted row)
const { data, error } = await client
.from("items")
.insert({ name: "New Item", status: "pending" })
.select()
.single();
// Insert multiple
const { data } = await client
.from("items")
.insert([{ name: "A" }, { name: "B" }])
.select();
// Update
await client.from("items").update({ status: "completed" }).eq("id", 1);
// Update and return updated row
const { data } = await client
.from("items")
.update({ status: "completed" })
.eq("id", 1)
.select()
.single();
// Delete
await client.from("items").delete().eq("id", 1);
// Delete and return deleted row
const { data } = await client
.from("items")
.delete()
.eq("id", 1)
.select()
.single();
// Upsert
await client.from("items").upsert({ id: 1, name: "Updated", status: "active" });Filter Operators
| Operator | Example |
|---|---|
.eq() | .eq("status", "active") |
.neq() | .neq("status", "archived") |
.gt() | .gt("price", 100) |
.gte() | .gte("price", 100) |
.lt() | .lt("price", 100) |
.lte() | .lte("price", 100) |
.like() | .like("name", "%item%") |
.ilike() | .ilike("name", "%item%") |
.is() | .is("deleted_at", null) |
.in() | .in("status", ["active", "pending"]) |
.contains() | .contains("tags", ["important"]) |
.or() | .or("status.eq.active,price.gt.100") |
.not() | .not("status", "eq", "archived") |
.order() | .order("created_at", { ascending: false }) |
.limit() | .limit(10) |
.range() | .range(0, 9) (first 10 items) |
Pagination formula: .range((page - 1) * pageSize, page * pageSize - 1)
Relationships
// One-to-many
const { data } = await client
.from("posts")
.select("id, title, author:users(name, email)");
// Many-to-many
const { data } = await client
.from("posts")
.select("id, title, tags:post_tags(tag:tags(name))");
// Nested
const { data } = await client.from("posts").select(`
id, title,
author:users(id, name, profile:profiles(bio, avatar))
`);Error Handling
const { data, error } = await client.from("items").select();
if (error) {
console.error(error.message, error.code, error.details);
return;
}Common error codes: PGRST116 (no rows with .single()), 23505 (unique violation), 23503 (FK violation), 42P01 (table not found).
Next.js Usage Examples
Server Component:
// app/posts/page.tsx
import { dbClient } from "@/lib/db/client";
export default async function PostsPage() {
const { data: posts, error } = await dbClient
.from("posts")
.select("id, title, created_at, author:users(name)")
.order("created_at", { ascending: false })
.limit(10);
if (error) return <div>Error loading posts</div>;
return (
<ul>
{posts?.map((post) => (
<li key={post.id}>
<h2>{post.title}</h2>
<p>By {post.author?.name}</p>
</li>
))}
</ul>
);
}API Route:
// app/api/posts/route.ts
import { dbClient } from "@/lib/db/client";
import { NextResponse } from "next/server";
export async function GET() {
const { data, error } = await dbClient.from("posts").select();
if (error)
return NextResponse.json({ error: error.message }, { status: 500 });
return NextResponse.json(data);
}
export async function POST(request: Request) {
const body = await request.json();
const { data, error } = await dbClient
.from("posts")
.insert(body)
.select()
.single();
if (error)
return NextResponse.json({ error: error.message }, { status: 400 });
return NextResponse.json(data, { status: 201 });
}Auth Methods
BetterAuth API (Default)
await client.auth.signIn.email({ email, password });
await client.auth.signUp.email({ email, password, name });
await client.auth.signOut();
const { data: session } = await client.auth.getSession();
await client.auth.signIn.social({
provider: "google",
callbackURL: "/dashboard",
});Supabase-Compatible API
import { createClient, SupabaseAuthAdapter } from "@neondatabase/neon-js";
const client = createClient({
auth: { adapter: SupabaseAuthAdapter(), url },
dataApi: { url },
});
await client.auth.signInWithPassword({ email, password });
await client.auth.signUp({ email, password });
const {
data: { session },
} = await client.auth.getSession();Key Imports
// Main client
import {
createClient,
SupabaseAuthAdapter,
BetterAuthVanillaAdapter,
} from "@neondatabase/neon-js";
// Server auth (Next.js) -- unified instance
import { createNeonAuth } from "@neondatabase/neon-js/auth/next/server";
// Client auth (Next.js) -- auto-includes React adapter
import { createAuthClient } from "@neondatabase/neon-js/auth/next";
// Client auth (React SPA / vanilla)
import { createAuthClient } from "@neondatabase/neon-js/auth";
// React adapter (only needed for useSession() in custom components)
import { BetterAuthReactAdapter } from "@neondatabase/neon-js/auth/react";
// UI components (use /auth/react -- superset of /auth/react/ui and /auth/react/adapters)
import {
NeonAuthUIProvider,
AuthView,
AccountView,
SignedIn,
SignedOut,
UserButton,
} from "@neondatabase/neon-js/auth/react";
import { accountViewPaths } from "@neondatabase/neon-js/auth/react/ui/server";
// CSS (choose one, never both)
import "@neondatabase/neon-js/ui/css"; // Without Tailwind
// @import '@neondatabase/neon-js/ui/tailwind'; // With Tailwind v4 (in CSS file)Generate Types
npx neon-js gen-types --db-url "$DATABASE_URL" --output src/types/database.tsUse types in the client for autocomplete and compile-time checking:
import type { Database } from "./database.types";
const client = createClient<Database>({ ... });Supabase Migration
The Neon JS SDK uses the same PostgREST API as Supabase. Query syntax is identical:
// Before (Supabase)
import { createClient } from "@supabase/supabase-js";
const client = createClient(SUPABASE_URL, SUPABASE_KEY);
// After (Neon)
import { createClient, SupabaseAuthAdapter } from "@neondatabase/neon-js";
const client = createClient({
auth: { adapter: SupabaseAuthAdapter(), url: NEON_AUTH_URL },
dataApi: { url: NEON_DATA_API_URL },
});
// Queries work the same
const { data } = await client.from("items").select();Common Mistakes
Using old v0.1 server APIs
Use createNeonAuth() + auth.handler(), not standalone authApiHandler(). See neon-auth.md for the v0.2 pattern.
Missing NEON_AUTH_COOKIE_SECRET
Required for Next.js, must be 32+ characters. Generate with openssl rand -base64 32.
Missing force-dynamic on server components
Server components using auth.getSession() need export const dynamic = 'force-dynamic'.
Wrong adapter import path
BetterAuthReactAdapter must be imported from a subpath and called as a function:
// WRONG
import { BetterAuthReactAdapter } from "@neondatabase/neon-js";
// CORRECT
import { BetterAuthReactAdapter } from "@neondatabase/neon-js/auth/react";
auth: {
adapter: BetterAuthReactAdapter();
} // Don't forget ()CSS import conflicts
Choose ONE method. Never import both -- causes duplicate styles:
/* With Tailwind v4 */
@import "tailwindcss";
@import "@neondatabase/neon-js/ui/tailwind";/* Without Tailwind */
import "@neondatabase/neon-js/ui/css";Missing "use client" directive
Required for any component using useSession() or other React hooks:
"use client"; // Required!
import { authClient } from "@/lib/auth/client";Wrong API for adapter type
| Adapter | Sign In | Sign Up |
|---|---|---|
| BetterAuthReactAdapter | signIn.email({ email, password }) | signUp.email({ email, password }) |
| SupabaseAuthAdapter | signInWithPassword({ email, password }) | signUp({ email, password }) |
Neon Python SDK
The neon-api Python SDK is a Pythonic wrapper around the Neon REST API for managing Neon resources programmatically.
For core concepts (Organization, Project, Branch, Endpoint, etc.), see what-is-neon.md.
See the official Python SDK docs for complete details.
Installation
pip install neon-apiAuthentication
import os
from neon_api import NeonAPI
neon = NeonAPI(api_key=os.environ["NEON_API_KEY"])Org-Aware Workflow
All Neon accounts are organization-based. Discover the user's org first, then pass org_id to project operations:
# 1. Get the user's organizations
orgs = neon.current_user_organizations()
org_id = orgs[0].id
# 2. List projects within the org
projects = neon.projects(org_id=org_id)Method Quick Reference
Projects
| Operation | Method |
|---|---|
| List projects | neon.projects(org_id=...) |
| Create project | neon.project_create(project={ 'name': ..., 'pg_version': 17, 'org_id': ... }) |
| Get project | neon.project(project_id=...) |
| Update project | neon.project_update(project_id=..., project={...}) |
| Delete project | neon.project_delete(project_id=...) |
| Get connection URI | neon.connection_uri(project_id=..., database_name=..., role_name=...) |
Branches
| Operation | Method |
|---|---|
| Create branch | neon.branch_create(project_id=..., branch={...}, endpoints=[...]) |
| List branches | neon.branches(project_id=...) |
| Get branch | neon.branch(project_id=..., branch_id=...) |
| Update branch | neon.branch_update(project_id=..., branch_id=..., branch={...}) |
| Delete branch | neon.branch_delete(project_id=..., branch_id=...) |
Databases
| Operation | Method |
|---|---|
| Create database | neon.database_create(project_id=..., branch_id=..., database={...}) |
| List databases | neon.databases(project_id=..., branch_id=...) |
| Delete database | neon.database_delete(project_id=..., branch_id=..., database_id=...) |
Roles
| Operation | Method |
|---|---|
| Create role | neon.role_create(project_id=..., branch_id=..., role_name=...) |
| List roles | neon.roles(project_id=..., branch_id=...) |
| Delete role | neon.role_delete(project_id=..., branch_id=..., role_name=...) |
Endpoints
| Operation | Method |
|---|---|
| Create endpoint | neon.endpoint_create(project_id=..., endpoint={...}) |
| Start endpoint | neon.endpoint_start(project_id=..., endpoint_id=...) |
| Suspend endpoint | neon.endpoint_suspend(project_id=..., endpoint_id=...) |
| Update endpoint | neon.endpoint_update(project_id=..., endpoint_id=..., endpoint={...}) |
| Delete endpoint | neon.endpoint_delete(project_id=..., endpoint_id=...) |
Organizations
| Operation | Method |
|---|---|
| List user orgs | neon.current_user_organizations() |
| Get org | neon.organization(org_id=...) |
API Keys & Operations
| Operation | Method |
|---|---|
| List API keys | neon.api_keys() |
| Create API key | neon.api_key_create(key_name=...) |
| Revoke API key | neon.api_key_revoke(key_id) |
| List operations | neon.operations(project_id=...) |
| Get operation | neon.operation(project_id=..., operation_id=...) |
Neon REST API
Essentials for making direct HTTP requests to the Neon Platform API.
See the official API reference for complete details.
OpenAPI Specification
The full OpenAPI spec is available for programmatic lookup of exact endpoints, request/response schemas, and required fields.
Base URL
https://console.neon.tech/api/v2/Authentication
Include a Neon API key in every request:
Authorization: Bearer $NEON_API_KEYAPI Key Types
| Type | Scope | Best For |
|---|---|---|
| Personal | All projects user has access to | Individual use, scripting |
| Organization | Entire organization | CI/CD, org-wide automation |
| Project-scoped | Single project only | Project-specific integrations |
Rate Limits
- 700 requests/minute (~11/second)
- Bursts up to 40 requests/second per route
- Handle
429 Too Many Requestswith retry + backoff
Common Endpoints
| Operation | Method | Path |
|---|---|---|
| List projects | GET | /projects?org_id={org_id} |
| List user orgs | GET | /users/me/organizations |
| Create project | POST | /projects (include org_id in body) |
| Get connection URI | GET | /projects/{project_id}/connection_uri |
| Create branch | POST | /projects/{project_id}/branches |
| List branches | GET | /projects/{project_id}/branches |
| Delete branch | DELETE | /projects/{project_id}/branches/{branch_id} |
| Start endpoint | POST | /projects/{project_id}/endpoints/{endpoint_id}/start |
| Suspend endpoint | POST | /projects/{project_id}/endpoints/{endpoint_id}/suspend |
| List databases | GET | /projects/{project_id}/branches/{branch_id}/databases |
| Create database | POST | /projects/{project_id}/branches/{branch_id}/databases |
| List roles | GET | /projects/{project_id}/branches/{branch_id}/roles |
| List API keys | GET | /api_keys |
| List operations | GET | /projects/{project_id}/operations |
Important Constraints
- You cannot delete a project's root or default branch
- You cannot delete a branch that has child branches — delete all children first
- Creating a new role may drop existing connections to the active compute endpoint
- A branch can have only one
read_writeendpoint but multipleread_onlyendpoints - Operations are async — poll operation status before starting dependent operations
- Operations older than 6 months may be deleted from Neon's systems
- The first API key must be created from the Neon Console; subsequent keys can be created via the API
Error Codes
| Status | Meaning | Action |
|---|---|---|
| 401 | Unauthorized | Check API key |
| 404 | Not Found | Verify resource ID |
| 429 | Rate Limited | Retry with backoff |
| 500 | Server Error | Retry or contact support |
For TypeScript SDK usage, see neon-typescript-sdk.md. For Python SDK, see neon-python-sdk.md.
Neon Serverless Driver
Patterns and best practices for connecting to Neon databases in serverless environments using the @neondatabase/serverless driver. The driver connects over HTTP for fast, single queries or WebSockets for node-postgres compatibility and interactive transactions.
See the official serverless driver docs for complete details.
Installation
# Using npm
npm install @neondatabase/serverless
# Using JSR
bunx jsr add @neon/serverlessNote: Version 1.0.0+ requires Node.js v19 or later.
For projects that depend on pg but want to use Neon's WebSocket-based connection pool:
"dependencies": {
"pg": "npm:@neondatabase/serverless@^0.10.4"
},
"overrides": {
"pg": "npm:@neondatabase/serverless@^0.10.4"
}Connection String
Always use environment variables:
// For HTTP queries
import { neon } from "@neondatabase/serverless";
const sql = neon(process.env.DATABASE_URL!);
// For WebSocket connections
import { Pool } from "@neondatabase/serverless";
const pool = new Pool({ connectionString: process.env.DATABASE_URL! });Never hardcode credentials:
// AVOID
const sql = neon("postgres://username:password@host.neon.tech/neondb");HTTP Queries with neon function
Ideal for simple, "one-shot" queries in serverless/edge environments. Uses HTTP fetch - fastest method for single queries.
Parameterized Queries
Use tagged template literals for safe parameter interpolation:
const [post] = await sql`SELECT * FROM posts WHERE id = ${postId}`;For manually constructed queries:
const [post] = await sql.query("SELECT * FROM posts WHERE id = $1", [postId]);Never concatenate user input:
// AVOID: SQL Injection Risk
const [post] = await sql("SELECT * FROM posts WHERE id = " + postId);Configuration Options
// Return rows as arrays instead of objects
const sqlArrayMode = neon(process.env.DATABASE_URL!, { arrayMode: true });
const rows = await sqlArrayMode`SELECT id, title FROM posts`;
// rows -> [[1, "First Post"], [2, "Second Post"]]
// Get full results including row count and field metadata
const sqlFull = neon(process.env.DATABASE_URL!, { fullResults: true });
const result = await sqlFull`SELECT * FROM posts LIMIT 1`;
// result -> { rows: [...], fields: [...], rowCount: 1, ... }WebSocket Connections with Pool and Client
Use for node-postgres compatibility, interactive transactions, or session support.
WebSocket Configuration
For Node.js v21 and earlier:
import { Pool, neonConfig } from "@neondatabase/serverless";
import ws from "ws";
// Required for Node.js < v22
neonConfig.webSocketConstructor = ws;
const pool = new Pool({ connectionString: process.env.DATABASE_URL! });Serverless Lifecycle Management
Create, use, and close the pool within the same invocation:
// Vercel Edge Functions example
export default async (req: Request, ctx: ExecutionContext) => {
const pool = new Pool({ connectionString: process.env.DATABASE_URL! });
try {
const { rows } = await pool.query("SELECT * FROM users");
return new Response(JSON.stringify(rows));
} catch (err) {
console.error(err);
return new Response("Database error", { status: 500 });
} finally {
ctx.waitUntil(pool.end());
}
};Avoid creating a global Pool instance outside the handler.
Transactions
HTTP Transactions
For running multiple queries in a single, non-interactive transaction:
const [newUser, newProfile] = await sql.transaction(
[
sql`INSERT INTO users(name) VALUES(${name}) RETURNING id`,
sql`INSERT INTO profiles(user_id, bio) VALUES(${userId}, ${bio})`,
],
{
isolationLevel: "ReadCommitted",
readOnly: false,
},
);Interactive Transactions
For complex transactions with conditional logic:
const pool = new Pool({ connectionString: process.env.DATABASE_URL! });
const client = await pool.connect();
try {
await client.query("BEGIN");
const {
rows: [{ id }],
} = await client.query("INSERT INTO users(name) VALUES($1) RETURNING id", [
name,
]);
await client.query("INSERT INTO profiles(user_id, bio) VALUES($1, $2)", [
id,
bio,
]);
await client.query("COMMIT");
} catch (err) {
await client.query("ROLLBACK");
throw err;
} finally {
client.release();
await pool.end();
}Environment-Specific Optimizations
// For Vercel Edge Functions, specify nearest region
export const config = {
runtime: "edge",
regions: ["iad1"], // Region nearest to your Neon DB
};
// For Cloudflare Workers, consider using Hyperdrive
// https://neon.com/blog/hyperdrive-neon-faqORM Integration
For Drizzle ORM integration with the serverless driver, see neon-drizzle.md.
Prisma
import { neonConfig } from "@neondatabase/serverless";
import { PrismaNeon, PrismaNeonHTTP } from "@prisma/adapter-neon";
import { PrismaClient } from "@prisma/client";
import ws from "ws";
const connectionString = process.env.DATABASE_URL;
neonConfig.webSocketConstructor = ws;
// HTTP adapter
const adapterHttp = new PrismaNeonHTTP(connectionString!, {});
export const prismaClientHttp = new PrismaClient({ adapter: adapterHttp });
// WebSocket adapter
const adapterWs = new PrismaNeon({ connectionString });
export const prismaClientWs = new PrismaClient({ adapter: adapterWs });Kysely
import { Pool } from "@neondatabase/serverless";
import { Kysely, PostgresDialect } from "kysely";
const dialect = new PostgresDialect({
pool: new Pool({ connectionString: process.env.DATABASE_URL }),
});
const db = new Kysely({ dialect });NOTE: Do not pass the neon() function to ORMs that expect a node-postgres compatible Pool.
Error Handling
// Pool error handling
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
pool.on("error", (err) => {
console.error("Unexpected error on idle client", err);
process.exit(-1);
});
// Query error handling
try {
const [post] = await sql`SELECT * FROM posts WHERE id = ${postId}`;
if (!post) {
return new Response("Not found", { status: 404 });
}
} catch (err) {
console.error("Database query failed:", err);
return new Response("Server error", { status: 500 });
}Neon TypeScript SDK
The @neondatabase/api-client TypeScript SDK is a typed wrapper around the Neon REST API for managing Neon resources programmatically.
For core concepts (Organization, Project, Branch, Endpoint, etc.), see what-is-neon.md.
See the official TypeScript SDK docs for complete details.
Installation
npm install @neondatabase/api-clientAuthentication
import { createApiClient } from "@neondatabase/api-client";
const apiClient = createApiClient({ apiKey: process.env.NEON_API_KEY! });Org-Aware Workflow
All Neon accounts are organization-based. You must discover the user's org first, then pass org_id to project operations:
// 1. Get the user's organizations
const { data: orgs } = await apiClient.getCurrentUserOrganizations();
const orgId = orgs.organizations[0].id;
// 2. List projects within the org
const { data: projects } = await apiClient.listProjects({ org_id: orgId });Method Quick Reference
Projects
| Operation | Method |
|---|---|
| List projects | apiClient.listProjects({ org_id }) |
| Create project | apiClient.createProject({ project: { name, pg_version, region_id, org_id } }) |
| Get project | apiClient.getProject(projectId) |
| Update project | apiClient.updateProject(projectId, { project: { name } }) |
| Delete project | apiClient.deleteProject(projectId) |
| Get connection URI | apiClient.getConnectionUri({ projectId, database_name, role_name, pooled }) |
Branches
| Operation | Method |
|---|---|
| Create branch | apiClient.createProjectBranch(projectId, { branch: { name }, endpoints: [{ type }] }) |
| List branches | apiClient.listProjectBranches({ projectId }) |
| Get branch | apiClient.getProjectBranch(projectId, branchId) |
| Update branch | apiClient.updateProjectBranch(projectId, branchId, { branch: { name } }) |
| Delete branch | apiClient.deleteProjectBranch(projectId, branchId) |
Databases
| Operation | Method |
|---|---|
| Create database | apiClient.createProjectBranchDatabase(projectId, branchId, { database: { name, owner_name } }) |
| List databases | apiClient.listProjectBranchDatabases(projectId, branchId) |
| Delete database | apiClient.deleteProjectBranchDatabase(projectId, branchId, databaseName) |
Roles
| Operation | Method |
|---|---|
| Create role | apiClient.createProjectBranchRole(projectId, branchId, { role: { name } }) |
| List roles | apiClient.listProjectBranchRoles(projectId, branchId) |
| Delete role | apiClient.deleteProjectBranchRole(projectId, branchId, roleName) |
Endpoints
| Operation | Method |
|---|---|
| Create endpoint | apiClient.createProjectEndpoint(projectId, { endpoint: { branch_id, type } }) |
| List endpoints | apiClient.listProjectEndpoints(projectId) |
| Start endpoint | apiClient.startProjectEndpoint(projectId, endpointId) |
| Suspend endpoint | apiClient.suspendProjectEndpoint(projectId, endpointId) |
| Restart endpoint | apiClient.restartProjectEndpoint(projectId, endpointId) |
| Update endpoint | apiClient.updateProjectEndpoint(projectId, endpointId, { endpoint: {...} }) |
| Delete endpoint | apiClient.deleteProjectEndpoint(projectId, endpointId) |
API Keys
| Operation | Method |
|---|---|
| List keys | apiClient.listApiKeys() |
| Create key | apiClient.createApiKey({ key_name }) |
| Revoke key | apiClient.revokeApiKey(keyId) |
Operations
| Operation | Method |
|---|---|
| List operations | apiClient.listProjectOperations({ projectId }) |
| Get operation | apiClient.getProjectOperation(projectId, operationId) |
Organizations
| Operation | Method |
|---|---|
| List user orgs | apiClient.getCurrentUserOrganizations() |
| Get org | apiClient.getOrganization(orgId) |
| List members | apiClient.getOrganizationMembers(orgId) |
| Create org key | apiClient.createOrgApiKey(orgId, { key_name, project_id? }) |
| Invite member | apiClient.createOrganizationInvitations(orgId, { invitations: [...] }) |
Error Handling
try {
const response = await apiClient.getProject(projectId);
return response.data;
} catch (error: any) {
if (error.isAxiosError) {
const status = error.response?.status;
// 401 = bad API key, 404 = not found, 429 = rate limited
console.error("API error:", status, error.response?.data?.message);
}
return null;
}Key Types
import { EndpointType, MemberRole } from "@neondatabase/api-client";
// EndpointType.ReadWrite, EndpointType.ReadOnly
// MemberRole.Admin, MemberRole.MemberWhat is Neon
Neon is a serverless Postgres platform that separates compute and storage to offer autoscaling, branching, instant restore, and scale-to-zero.
See the official introduction for complete details.
Core Concepts
| Concept | Description | Key Relationship |
|---|---|---|
| Organization | Highest-level container for billing, users, and projects | Contains Projects |
| Project | Primary container for all database resources for an application | Contains Branches |
| Branch | Lightweight, copy-on-write clone of database state | Contains Databases, Roles |
| Compute Endpoint | Running PostgreSQL instance (CPU/RAM for queries) | Attached to a Branch |
| Database | Logical container for data (tables, schemas, views) | Exists within a Branch |
| Role | PostgreSQL role for authentication and authorization | Belongs to a Branch |
| Operation | Async action by the control plane (creating branch, starting compute) | Associated with Project |
Key Differentiators
1. Serverless Architecture: Compute scales automatically and can suspend when idle 2. Branching: Create instant database copies without duplicating storage 3. Separation of Compute and Storage: Pay for compute only when active 4. Postgres Compatible: Works with any Postgres driver, ORM, or tool
When to Use Neon
- Serverless applications: Functions that need database access without managing connections
- Development workflows: Branch databases like code for isolated testing
- Variable workloads: Auto-scale during traffic spikes, scale to zero when idle
- Cost optimization: Pay only for active compute time and storage used
Further Reading
| Topic | URL |
|---|---|
| Architecture | https://neon.com/docs/introduction/architecture-overview.md |
| Plans & Billing | https://neon.com/docs/introduction/about-billing.md |
| Regions | https://neon.com/docs/introduction/regions.md |
| Postgres Compatibility | https://neon.com/docs/reference/compatibility.md |
Related skills
FAQ
What makes Neon branches fast?
Branches are instant, copy-on-write clones with no full data copy, each with its own compute endpoint.
How do I fetch Neon docs as markdown?
Append .md to the doc URL or request text/markdown; both return the same content.