I saw Neon’s announcement of the @neon/sdk TypeScript client this morning and instantly thought about the countless scripts I’ve written to spin up test databases for CI pipelines. Until now, I’ve been hammering the Neon REST endpoints with curl or axios, manually handling the “operation‑in‑progress” polling loop that the OpenAPI spec requires. The new SDK promises a first‑class, typed wrapper around those endpoints, handling the async lifecycle for us. For a developer who lives in the Node.js/TypeScript ecosystem, that feels like a productivity win the moment I can replace a dozen lines of boilerplate with a clean, promise‑based call.
Below I walk through the parts of the SDK that matter most to a day‑to‑day dev: authenticating, provisioning a new project, creating a branch, and finally pulling the connection string to run migrations or tests. I’ll also touch on the built‑in polling helpers that the SDK adds, so you don’t have to write your own setInterval loops.
Why a Typed SDK Matters Right Now
Neon’s API is already OpenAPI‑compliant, which means you can generate client code in any language. However, the generated code is often generic, missing the ergonomics you expect from a hand‑crafted library: proper TypeScript types, sensible defaults, and higher‑level helpers. The @neon/sdk fills that gap for us JavaScript/TypeScript developers.
- Typed request/response objects – No more “any” creeping into your CI scripts.
-
Built‑in operation polling – Neon creates resources asynchronously; the SDK abstracts the
GET /operations/{id}loop. -
Zero‑config auth – It reads the
NEON_API_KEYenv var automatically, falling back to a token you pass in. -
Consistent error handling – Errors surface as native
Errorobjects with the API’scodeandmessagefields attached.
All of that translates to fewer runtime bugs and a smoother developer experience when you need to spin up a fresh PostgreSQL instance on the fly.
Getting Started: Install and Authenticate
The SDK is published to npm, so adding it to a project is as simple as:
npm i @neon/sdk
Once installed, the client reads the NEON_API_KEY environment variable. If you prefer to pass the token explicitly, you can do that too:
import { NeonClient } from '@neon/sdk';
// Option 1: env var (recommended for CI)
process.env.NEON_API_KEY = 'your‑neon‑api‑key';
// Option 2: explicit token
const client = new NeonClient({ apiKey: 'your‑neon‑api‑key' });
The client constructor also accepts optional configuration like a custom API base URL (useful for testing against a mock server) and request timeout settings.
Provisioning a New Project in One Call
Creating a Neon project involves two steps behind the scenes: a POST /projects request and then waiting for the asynchronous operation to finish. The SDK collapses that into a single createProject method that returns the fully provisioned project object.
import { NeonClient } from '@neon/sdk';
async function provisionProject() {
const client = new NeonClient();
// Define the project you want – region, name, and optional settings
const project = await client.createProject({
name: 'ci‑test‑project',
region_id: 'aws-us-east-2', // example region identifier
// You can also pass initial database settings here if you like
});
console.log('Project ready:', project.id);
return project;
}
The SDK internally sends the POST /projects request, receives an operation ID, and then polls GET /operations/{id} until the status is succeeded. If the operation fails, it throws an error with the API’s diagnostic payload.
Adding a Branch (Database Instance) Quickly
Neon treats each branch as a separate PostgreSQL endpoint, perfect for isolated test environments. The SDK’s createBranch method mirrors the same “create‑and‑wait” pattern:
async function createTestBranch(projectId: string) {
const client = new NeonClient();
const branch = await client.createBranch({
project_id: projectId,
name: `test‑branch-${Date.now()}`,
// You can optionally clone from another branch or snapshot
});
console.log('Branch ready:', branch.id);
return branch;
}
Again, you get a fully ready branch object without manually checking the operation status.
Pulling the Connection String for Migrations
Once the branch is up, you’ll need its connection string to run migrations or spin up a Prisma client. The SDK provides a convenience method that resolves the URL directly:
async function getConnectionUrl(branchId: string) {
const client = new NeonClient();
const connection = await client.getBranchConnectionUrl({
branch_id: branchId,
// optional: include password in the URL (default is true)
include_password: true,
});
console.log('Connection URL:', connection.url);
return connection.url;
}
You can now feed that URL into any migration tool (e.g., drizzle-kit, Prisma Migrate, or raw psql scripts) without any extra lookups.
Putting It All Together: A One‑Click Test Database
Here’s a minimal script that ties the previous snippets into a reusable helper for CI pipelines:
import { NeonClient } from '@neon/sdk';
export async function createCiDatabase() {
const client = new NeonClient();
// 1️⃣ Create a fresh project
const project = await client.createProject({
name: `ci‑project-${Date.now()}`,
region_id: 'aws-us-east-2',
});
// 2️⃣ Spin up a branch for this test run
const branch = await client.createBranch({
project_id: project.id,
name: `ci‑branch-${Date.now()}`,
});
// 3️⃣ Grab the connection string
const { url } = await client.getBranchConnectionUrl({
branch_id: branch.id,
});
// Return everything you might need later (e.g., for cleanup)
return {
projectId: project.id,
branchId: branch.id,
connectionUrl: url,
};
}
// Example usage in a Jest globalSetup
/*
module.exports = async () => {
const db = await createCiDatabase();
process.env.DATABASE_URL = db.connectionUrl;
};
*/
The script is under 30 lines, yet it replaces the dozens of fetch calls and manual polling loops I previously maintained. Because the SDK is typed, my editor now warns me if I pass an invalid region ID or forget a required field.
My Take: Is It Worth Adding to Your Stack?
The @neon/sdk feels like a natural evolution for anyone already using Neon in development or CI. The biggest win is the removal of boilerplate around asynchronous provisioning—no more “while (operation.status !== 'succeeded') …” code littered across repos. The typed API also reduces friction when onboarding new team members; they can explore
Top comments (4)
I appreciate how the
@neon/sdksimplifies provisioning Neon PostgreSQL instances, especially with its built-in polling helpers that handle the async lifecycle for us. The example of collapsing thePOST /projectsrequest and waiting for the operation to finish into a singlecreateProjectmethod is particularly useful, as it eliminates the need for manualsetIntervalloops. I've had similar experiences with other APIs where a well-designed SDK can significantly reduce boilerplate code and improve overall productivity. Have you explored using this SDK in conjunction with other CI/CD tools to further automate your testing pipelines?Thanks for highlighting the polling helpers and
createProjectexample - they really do simplify things! We've started exploring using the SDK with CI/CD tools for ephemeral test environments, and it shows great promise.Thanks for the thoughtful feedback! I completely agree that moving provisioning into a developer workflow abstraction is key. Exploring
Some comments may only be visible to logged-in visitors. Sign in to view all comments.