Migrate from @neondatabase/api-client to @neon/sdk
The legacy @neondatabase/api client package continues to work. @neon/sdk is the recommended replacement for new projects and for teams that want fetch based, zero dependency Platform API access with e...
This guide maps common @neondatabase/api-client patterns to @neon/sdk. For full method reference, see the Neon Management SDK documentation.
What changes
Section titled “What changes”@neondatabase/api-client |
@neon/sdk |
|
|---|---|---|
| HTTP client | Axios | fetch (zero runtime dependencies) |
| Factory | createApiClient({ apiKey }) |
createNeonClient({ apiKey }) |
| Method layout | Flat (listProjects, createProjectBranch, …) |
Namespaced (neon.projects.list(), neon.branches.create(), …) |
| Success path | response.data on Axios responses |
{ data, error } by default, or bare resource with throwOnError: true |
| Errors | AxiosError + error.response |
Typed NeonError hierarchy (kind: api, not_found, auth, …) |
| Node.js | Broader support | ≥ 20.19 (or any runtime with global fetch) |
| Low-level API | Generated methods on the client | raw.* functions + neon.client |
Install and swap the package
Section titled “Install and swap the package”npm uninstall @neondatabase/api-client
npm install @neon/sdkUpdate imports:
// Before
import { createApiClient } from '@neondatabase/api-client';
// After
import { createNeonClient } from '@neon/sdk';Client setup
Section titled “Client setup”// Before
const apiClient = createApiClient({
apiKey: process.env.NEON_API_KEY!,
});
// After — check { data, error } on each call
const neon = createNeonClient({
apiKey: process.env.NEON_API_KEY!,
});
// After — throw on error (closer to try/catch style)
const neon = createNeonClient({
apiKey: process.env.NEON_API_KEY!,
throwOnError: true,
});createNeonClient also supports orgId, waitForReadiness, retries, baseUrl, and a custom fetch implementation. See Neon Management SDK.
Method mapping
Section titled “Method mapping”Common Platform API calls and their @neon/sdk equivalents:
@neondatabase/api-client |
@neon/sdk |
|---|---|
getCurrentUserOrganizations() |
neon.user.organizations() |
getCurrentUserInfo() |
neon.user.me() |
listProjects({ org_id }) |
neon.projects.list({ org_id }).page() or .all() |
createProject({ project }) |
neon.projects.create({ name, region_id, … }) |
getProject(projectId) |
neon.projects.get(projectId) |
deleteProject(projectId) |
neon.projects.delete(projectId) |
listProjectBranches({ projectId }) |
neon.branches.list(projectId).page() or .all() |
createProjectBranch(projectId, body) |
neon.branches.create(projectId, input) or neon.branches.createAndConnect(…) |
getConnectionUri(projectId, query) |
neon.postgres.connectionString({ projectId, … }) |
listProjectBranchDatabases(…) |
neon.postgres.databases.list(…) |
createProjectBranchDatabase(…) |
neon.postgres.databases.create(…) |
listProjectBranchRoles(…) |
neon.postgres.roles.list(…) |
createProjectBranchRole(…) |
neon.postgres.roles.create(…) |
listProjectEndpoints(projectId) |
neon.postgres.endpoints.list(projectId) |
listApiKeys() |
neon.apiKeys.list() |
getActiveRegions() |
neon.regions.list() |
Endpoints that are not wrapped in an ergonomic namespace remain available through raw.
Error handling
Section titled “Error handling”Before — Axios throws; inspect error.response:
try {
const response = await apiClient.getProject(projectId);
console.log(response.data.project);
} catch (error) {
// AxiosError — error.response?.status, error.response?.data
}After — default { data, error } envelope:
const { data: project, error } = await neon.projects.get(projectId);
if (error) {
if (error.kind === 'not_found') {
// handle 404
}
throw error;
}
console.log(project);After — throwOnError: true on the client or per call:
const neon = createNeonClient({ apiKey, throwOnError: true });
const project = await neon.projects.get(projectId); // throws NeonError on failureSide-by-side examples
Section titled “Side-by-side examples”List projects
Section titled “List projects”// Before
const orgs = await apiClient.getCurrentUserOrganizations();
const orgId = orgs.data.organizations[0].id;
const response = await apiClient.listProjects({ org_id: orgId });
console.log(response.data.projects);
// After
const { data: orgs, error: orgsError } = await neon.user.organizations();
if (orgsError) throw orgsError;
const { data: page, error } = await neon.projects.list({ org_id: orgs[0].id }).page();
if (error) throw error;
console.log(page.items);Create a project with a connection string
Section titled “Create a project with a connection string”// Before
const response = await apiClient.createProject({
project: { name: 'my-app', region_id: 'aws-us-east-1', pg_version: 17 },
});
const uri = response.data.connection_uris[0].connection_uri;
// After — waits for provisioning; data is { project, connectionString }
const { data, error } = await neon.projects.createAndConnect({
name: 'my-app',
region_id: 'aws-us-east-1',
pg_version: 17,
});
if (error) throw error;
const { project, connectionString } = data;Create a branch with compute
Section titled “Create a branch with compute”// Before
import { EndpointType } from '@neondatabase/api-client';
await apiClient.createProjectBranch(projectId, {
branch: { name: 'dev-1', parent_id: parentBranchId },
endpoints: [{ type: EndpointType.ReadWrite }],
});
// After — read-write compute is attached by default
const { data, error } = await neon.branches.create(projectId, {
name: 'dev-1',
parent_id: parentBranchId,
});
if (error) throw error;
const branch = data;Create a database
Section titled “Create a database”// Before
await apiClient.createProjectBranchDatabase(projectId, branchId, {
database: { name: 'mydb', owner_name: 'neondb_owner' },
});
// After
const { error } = await neon.postgres.databases.create(projectId, branchId, {
name: 'mydb',
owner_name: 'neondb_owner',
});
if (error) throw error;Raw layer changes in 1.0
Section titled “Raw layer changes in 1.0”If you adopted @neon/sdk 0.x and used raw.* directly, 1.0 changes the raw contract:
| 0.x | 1.0 |
|---|---|
hey-api { data, request, response } envelope |
{ data, error } NeonResult |
responseStyle: "data" |
Removed |
throwOnError: true needed workarounds |
Returns the bare resource; types narrow correctly |
// Before (0.x)
const project = await raw.getProject({
client: neon.client,
path: { project_id: projectId },
throwOnError: true,
responseStyle: 'data',
});
// After (1.0)
const project = await raw.getProject({
client: neon.client,
path: { project_id: projectId },
throwOnError: true,
});Drop any unwrapRaw helpers or responseStyle usage.
Import request/response types from @neon/sdk instead of @neondatabase/api-client:
import type { Project, Branch } from '@neon/sdk';Some generated type names changed (for example, DataAPI* → DataApi*). Endpoint types are string unions ("read_write" / "read_only") rather than enums.
What you gain
Section titled “What you gain”- Workflow helpers such as
projects.createAndConnectandbranches.createAndConnectthat poll operations and return{ project, connectionString }or{ branch, endpoint, connectionString }.createreturns the resource;branches.createattaches a read-write endpoint unless you passnoCompute: true. - Readiness polling via
waitForReadinessandneon.operations.waitFor - Automatic retries on safe statuses (
423,429,503) - Ergonomic beta APIs for storage, functions, credentials, AI gateway, snapshots, and branch-scoped Managed Better Auth (
neon.auth,neon.storage, …) - Tree-shakeable raw imports from
@neon/sdk/raw
Next steps
Section titled “Next steps”- Neon Management SDK — install, configuration, examples, and namespace reference
- Neon API Reference — REST endpoint details
@neon/sdkon GitHub — full method tables and regeneration notes
Need help?
Section titled “Need help?”Join our Discord Server to ask questions or see what others are doing with Neon. For paid plan support options, see Support.