Skip to content

NeonApiService

NeonApiService

Typed wrapper around the Neon REST API v2 for admin reporting, monitoring, and branch management.

Architecture

flowchart LR
    A["Admin Dashboard"] --> B["NeonApiService"]
    B -->|REST API v2| C["Neon Console API"]
    B -->|Serverless Driver| D["Neon PostgreSQL"]
    C --> E["Projects / Branches / Endpoints"]
    D --> F["SQL Query Results"]

    style A fill:#4f46e5,color:#fff
    style B fill:#0ea5e9,color:#fff
    style C fill:#10b981,color:#fff
    style D fill:#10b981,color:#fff

Configuration

The service is created via a factory function that validates configuration with Zod:

import { createNeonApiService } from '@/services/neonApiService.ts';
const neon = createNeonApiService({
apiKey: env.NEON_API_KEY, // Required — Neon API key
baseUrl: 'https://...', // Optional — defaults to https://console.neon.tech/api/v2
});
ParameterTypeRequiredDefaultDescription
apiKeystringNeon API key
baseUrlstringhttps://console.neon.tech/api/v2Neon REST API base URL

An optional IBasicLogger can be passed as the second argument to enable request logging:

const neon = createNeonApiService({ apiKey }, consoleLogger);

API Reference

getProject(projectId: string): Promise<NeonProject>

Fetch details of a single Neon project.

const project = await neon.getProject('twilight-river-73901472');
// → { id, name, region_id, created_at, updated_at, pg_version? }

listBranches(projectId: string): Promise<NeonBranch[]>

List all branches for a project.

const branches = await neon.listBranches('twilight-river-73901472');

getBranch(projectId: string, branchId: string): Promise<NeonBranch>

Get details of a specific branch.

const branch = await neon.getBranch('twilight-river-73901472', 'br-cool-night-abc123');
// → { id, name, project_id, parent_id, current_state, created_at, updated_at }

createBranch(projectId: string, opts?: CreateBranchOptions): Promise<{ branch, operations }>

Create a new branch. Optionally specify a name and parent branch.

const result = await neon.createBranch('twilight-river-73901472', {
name: 'feature/admin-dashboard',
parent_id: 'br-cool-night-abc123',
});
console.log(result.branch.id); // new branch ID
console.log(result.operations[0].status); // 'finished' | 'running'

deleteBranch(projectId: string, branchId: string): Promise<{ branch, operations }>

Delete a branch. Returns the deleted branch metadata and any triggered operations.

const result = await neon.deleteBranch('twilight-river-73901472', 'br-old-branch');

listEndpoints(projectId: string): Promise<NeonEndpoint[]>

List compute endpoints for a project.

const endpoints = await neon.listEndpoints('twilight-river-73901472');
// → [{ id, host, branch_id, type: 'read_write' | 'read_only', current_state, ... }]

listDatabases(projectId: string, branchId: string): Promise<NeonDatabase[]>

List databases within a specific branch.

const dbs = await neon.listDatabases('twilight-river-73901472', 'br-cool-night-abc123');
// → [{ id, name: 'neondb', branch_id, owner_name, ... }]

getConnectionUri(projectId: string, branchId: string, opts?): Promise<string>

Retrieve a connection URI for a branch. Useful for generating one-off connection strings.

const uri = await neon.getConnectionUri('twilight-river-73901472', 'br-cool-night-abc123', {
database_name: 'neondb',
role_name: 'neondb_owner',
});
// → 'postgres://neondb_owner:***@ep-winter-term-a8rxh2a9.eastus2.azure.neon.tech/neondb'

querySQL<T>(connectionString: string, sql: string, params?): Promise<T[]>

Execute a SQL query via the @neondatabase/serverless driver. Accepts a full postgres:// connection string.

const rows = await neon.querySQL<{ count: number }>(
'postgres://user:pass@host/db',
'SELECT COUNT(*) as count FROM users WHERE created_at > $1',
['2025-01-01'],
);
console.log(rows[0].count);

Error Handling

All REST API errors throw a NeonApiError with:

PropertyTypeDescription
messagestringHuman-readable description
statusnumberHTTP status code (e.g. 404, 403, 500)
bodyunknownParsed JSON body from Neon (if available)
import { NeonApiError } from '@/services/neonApiService.ts';
try {
await neon.getProject('nonexistent');
} catch (err) {
if (err instanceof NeonApiError) {
console.error(`Neon API error ${err.status}:`, err.body);
}
}

Zod Schemas

All API responses are validated at runtime using Zod schemas:

SchemaValidates
NeonApiServiceConfigSchemaService config
NeonProjectSchemaProject objects
NeonBranchSchemaBranch objects
NeonEndpointSchemaEndpoint objects
NeonDatabaseSchemaDatabase objects
NeonOperationSchemaOperation objects
NeonConnectionUriSchemaConnection URI

Sequence: Admin Branch Management

sequenceDiagram
    participant Admin as Admin Dashboard
    participant Svc as NeonApiService
    participant API as Neon REST API
    participant PG as Neon PostgreSQL

    Admin->>Svc: createBranch(projectId, { name })
    Svc->>API: POST /projects/{id}/branches
    API-->>Svc: { branch, operations }
    Svc-->>Admin: Parsed result

    Admin->>Svc: getConnectionUri(projectId, branchId)
    Svc->>API: GET /projects/{id}/connection_uri
    API-->>Svc: { uri }
    Svc-->>Admin: Connection string

    Admin->>Svc: querySQL(uri, "SELECT ...")
    Svc->>PG: @neondatabase/serverless
    PG-->>Svc: Row[]
    Svc-->>Admin: Typed results