Skip to content

Commit 88875b4

Browse files
authored
Merge pull request #178 from cuappdev/ai-endpoint-issues
refactoring . revert if broke won't release....
2 parents 0d7139b + 93fcd0b commit 88875b4

47 files changed

Lines changed: 3550 additions & 734 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/neon-postgres/SKILL.md

Lines changed: 376 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
# `@neon/sdk` — the TypeScript client for the Neon API
2+
3+
`@neon/sdk` is the official TypeScript client for the [Neon API](https://neon.com/docs/reference/api-reference): **Fetch-based, zero-dependency, ESM-only**, generated from Neon's [OpenAPI spec](https://neon.com/api_spec/release/v2.json) with an ergonomic layer on top. It is the successor to [`@neondatabase/api-client`](https://www.npmjs.com/package/@neondatabase/api-client) (axios-based, generated-only). The old client is **not deprecated** and is safe to keep using, but new code should prefer `@neon/sdk`.
4+
5+
Use this reference when writing typed, programmatic control of Neon resources in TypeScript — provisioning projects, managing branches/databases/endpoints, transferring projects across orgs, snapshots/restore, consumption metrics, and the beta services (Object Storage, Functions, AI Gateway, scoped credentials).
6+
7+
## When to reach for it (vs MCP / CLI)
8+
9+
- **Neon MCP server and CLI** are for **local development** — a coding agent in your editor or terminal.
10+
- **`@neon/sdk`** is for **programmatic integration**: CI/CD pipelines where the CLI isn't enough, non-trivial dev scripts, and full platforms that provision and manage fleets of Neon databases (the same open API behind Replit, Netlify DB, Laravel Cloud, and Vercel's Neon marketplace integration). All it needs is a Neon API key.
11+
12+
## Install
13+
14+
```bash
15+
npm install @neon/sdk
16+
```
17+
18+
Requires Node.js ≥ 20.19, or any runtime with a global `fetch` (Bun, Deno, edge, browser).
19+
20+
## Two layers, one package
21+
22+
```ts
23+
import { createNeonClient, raw } from "@neon/sdk";
24+
```
25+
26+
- **`createNeonClient`** — the high-level ergonomic client: auth once, `{ data, error }` results, typed errors, retries, readiness polling, auto-pagination, and multi-step workflows, organized into resource namespaces (`neon.projects`, `neon.branches`, `neon.postgres`, …).
27+
- **`raw`** — the full generated 1:1 surface: every endpoint as a standalone, tree-shakeable function (also at the `@neon/sdk/raw` subpath). Speaks the **same** `{ data, error }` / `throwOnError` contract as the ergonomic client.
28+
29+
## Quick start
30+
31+
```ts
32+
import { createNeonClient } from "@neon/sdk";
33+
34+
const neon = createNeonClient({ apiKey: process.env.NEON_API_KEY! });
35+
36+
// Create a project and get a ready-to-use connection string in one call.
37+
const { data, error } = await neon.projects.createAndConnect({ name: "my-app" });
38+
if (error) throw error;
39+
const { project, connectionString } = data;
40+
```
41+
42+
## Client configuration
43+
44+
`createNeonClient(config)`:
45+
46+
| Option | Type | Default | Description |
47+
| --- | --- | --- | --- |
48+
| `apiKey` | `string \| (() => string \| Promise<string>)` | — (required) | Neon API key, or a function returning it. Sent as a Bearer token. |
49+
| `throwOnError` | `boolean` | `false` | `true` → methods return the resource directly and **throw** on error. `false` → return `{ data, error }`. **Narrows return types** at the type level. |
50+
| `waitForReadiness` | `boolean` | `false` | `true` → mutations block until their provisioning `operations` finish, so the returned resource is ready. |
51+
| `wait` | `{ pollIntervalMs?; timeoutMs? }` | `1000` / `300000` | Readiness poller tuning. |
52+
| `retries` | `number` | `2` | Automatic retries on always-safe statuses (`423`, `429`, `503`) with backoff. |
53+
| `orgId` | `string` || Default org for project create/list and as the transfer source org. Overridable per call. |
54+
| `baseUrl` | `string` | `https://console.neon.tech/api/v2` | Override the API base URL. |
55+
| `fetch` | `typeof fetch` | global `fetch` | Custom fetch (proxies, tests, non-global runtimes). |
56+
57+
Every option except `apiKey` is also accepted **per call** via the trailing `options` arg (`{ throwOnError?, waitForReadiness?, signal? }`), overriding the client default.
58+
59+
## The result model
60+
61+
By default every method resolves to a discriminated `{ data, error }` envelope — no `try/catch`:
62+
63+
```ts
64+
const { data, error } = await neon.projects.get("late-frost-12345");
65+
if (error) return; // error is a typed NeonError union
66+
data; // narrowed to Project
67+
```
68+
69+
Set `throwOnError` (on the client or per call) to get the bare resource and throw instead — the return type narrows accordingly:
70+
71+
```ts
72+
const neon = createNeonClient({ apiKey, throwOnError: true });
73+
const project = await neon.projects.get(""); // Project (throws)
74+
const res = await neon.projects.get("", { throwOnError: false }); // { data, error }
75+
```
76+
77+
## Errors
78+
79+
The `error` channel carries a typed hierarchy (all `Error` subclasses with a `kind` discriminant); the same value is thrown when `throwOnError` is set.
80+
81+
| Class | `kind` | Notable fields |
82+
| --- | --- | --- |
83+
| `NeonError` | (base) | `message`, `kind` |
84+
| `NeonApiError` | `"api"` | `status`, `code`, `requestId`, `response`, `body` |
85+
| `NeonNotFoundError` | `"not_found"` | 404 — extends `NeonApiError` |
86+
| `NeonAuthError` | `"auth"` | 401/403 |
87+
| `NeonRateLimitError` | `"rate_limit"` | 429 (after retries) |
88+
| `NeonOperationError` | `"operation"` | `operationId`, `status` — an awaited operation failed |
89+
| `NeonTimeoutError` | `"timeout"` | readiness/wait deadline exceeded |
90+
| `NeonNetworkError` | `"network"` | transport failure (no response) |
91+
| `NeonError` | `"client"` | SDK-side errors (e.g. ambiguous connection-string selection) |
92+
93+
```ts
94+
const { error } = await neon.branches.get(pid, "nope");
95+
if (error?.kind === "not_found") { /**/ }
96+
```
97+
98+
## Pagination
99+
100+
Cursor-paginated `list()` methods return a lazy `Paginated<T>`:
101+
102+
```ts
103+
const { data: all } = await neon.projects.list().all(); // every page → { data, error }
104+
const { data: page } = await neon.projects.list().page(); // one page
105+
for await (const project of neon.projects.list()) { … } // stream; throws on a page error
106+
```
107+
108+
## Readiness & workflows
109+
110+
Neon mutations are asynchronous — they return `operations`. `waitForReadiness` blocks until they settle; the **workflow** methods (`createAndConnect`, `createWithCompute`) default it on and hand back a connection string in one call. The underlying primitive is `neon.operations.waitFor(operations)`.
111+
112+
## API surface (ergonomic client)
113+
114+
Legend: **[P]** returns `Paginated<T>` · **[W]** workflow (multi-step) · **→void** resolves to `void`.
115+
116+
### `neon.projects`
117+
118+
| Method | Returns | Notes |
119+
| --- | --- | --- |
120+
| `list(query?)` | **[P]** `ProjectListItem` | `{ search?, org_id?, limit? }` |
121+
| `get(id)` | `Project` | |
122+
| `create(input?)` | `Project` | `{ name?, region_id?, pg_version?, org_id?, autoscaling_limit_min_cu?, autoscaling_limit_max_cu?, settings? }` |
123+
| `createAndConnect(input?, { pooled? })` | **[W]** `{ project, connectionString }` | one call + readiness; `pooled` default `true` |
124+
| `update(id, input)` | `Project` | `{ name?, settings? }` |
125+
| `delete(id)` | `Project` | |
126+
| `transfer({ fromOrgId?, toOrgId, projectIds })` | **→void** | `fromOrgId` defaults to client `orgId` |
127+
| `transferFromUser({ toOrgId, projectIds })` | **→void** | personal account → org |
128+
| `recover(id)` | `Project` | beta — recover a soft-deleted project |
129+
| `permissions.list / grant / revoke` | `ProjectPermission`(`[]`) | share a project by email |
130+
131+
```ts
132+
// Provision a project and get a pooled connection string in one call
133+
const { data } = await neon.projects.createAndConnect(
134+
{ name: "tenant-42", region_id: "aws-us-east-1" },
135+
{ pooled: true },
136+
); // data: { project, connectionString }
137+
138+
// Upgrade path: move projects from a sponsored (free) org to the paid org
139+
await neon.projects.transfer({
140+
fromOrgId: sponsoredOrgId, // defaults to the client's `orgId`
141+
toOrgId: paidOrgId,
142+
projectIds: ["late-frost-12345"],
143+
});
144+
```
145+
146+
### `neon.branches`
147+
148+
| Method | Returns | Notes |
149+
| --- | --- | --- |
150+
| `list(projectId, query?)` | **[P]** `Branch` | `{ search?, sort_by?, sort_order?, include_deleted? }` |
151+
| `get(projectId, branchId)` | `Branch` | |
152+
| `create(projectId, input?)` | `Branch` | `{ name?, parent_id?, parent_lsn?, parent_timestamp?, protected? }` |
153+
| `update(projectId, branchId, input)` | `Branch` | `{ name?, protected?, expires_at? }` |
154+
| `delete(projectId, branchId)` | **→void** | |
155+
| `createWithCompute(projectId, input, { pooled? })` | **[W]** `{ branch, endpoint, connectionString }` | `input`: `{ name?, parentId?, compute?: { minCu?, maxCu?, suspendTimeoutSeconds? } }` |
156+
| `getDefault(projectId)` / `setDefault(projectId, branchId)` | `Branch` | resolve/set the default branch |
157+
| `recover(projectId, branchId)` | `Branch` | beta — recover within the 7-day window |
158+
| `finalizeRestore(projectId, branchId, { name? }?)` | **→void** | commit a restore previewed with `snapshots.restore` |
159+
160+
```ts
161+
// Branch off the default branch with its own compute — returns a ready connection string
162+
const { data: prod } = await neon.branches.getDefault(projectId);
163+
const { data } = await neon.branches.createWithCompute(projectId, {
164+
name: "preview/pr-123",
165+
parentId: prod?.id,
166+
compute: { minCu: 0.25, maxCu: 2 },
167+
}); // data: { branch, endpoint, connectionString }
168+
```
169+
170+
### `neon.postgres`
171+
172+
The Postgres data plane of a branch. `neon.postgres.connectionString(params, options?)` resolves a URI, **auto-selecting** the default branch and the sole role/database when omitted:
173+
174+
```ts
175+
const { data: uri } = await neon.postgres.connectionString({
176+
projectId, // branchId?, endpointId?, databaseName?, roleName?, pooled? all optional; pooled default true
177+
});
178+
```
179+
180+
Nested namespaces:
181+
182+
- **`neon.postgres.endpoints`**`list / get / create / update / delete`, plus `start` / `suspend` / `restart` and `listByBranch(projectId, branchId)`.
183+
- **`neon.postgres.roles`**`list / get / create / delete`, plus `password(...)` (reveals) and `resetPassword(...)` (rotates; result carries the new password).
184+
- **`neon.postgres.databases`**`list / get / create / update / delete`.
185+
- **`neon.postgres.dataApi`**`get / create / update / delete` the branch's Data API.
186+
187+
### Beta services
188+
189+
- **`neon.storage`** — branch object storage. `get(projectId, branchId)``BranchStorage`; nested `buckets` (`list / create / delete`) and `objects` (`list / get / delete / deleteByPrefix / presign`). Use `presign(..., { operation: "upload" | "download" })` for direct S3-style transfers.
190+
- **`neon.functions`** — branch Neon Functions. `list` **[P]** `/ get / update / delete`, and `deploy(projectId, branchId, slug, { zip?, runtime?, environment? })` (multipart; poll `get` until `current_deployment.status === "completed"`).
191+
- **`neon.credentials`** — branch scoped credentials. `list / create / revoke`; secrets (`api_token`, `s3_secret_access_key`) are returned **once** on `create`. Scopes: `storage:read`, `storage:write`, `ai_gateway:invoke`, `functions:invoke`.
192+
- **`neon.aiGateway`**`get(projectId, branchId)``BranchAiGateway` (404 when the gateway is not enabled on the branch). See the `neon-ai-gateway` skill for calling the gateway itself.
193+
194+
### `neon.snapshots`
195+
196+
| Method | Returns | Notes |
197+
| --- | --- | --- |
198+
| `list(projectId)` | `Snapshot[]` | |
199+
| `create(projectId, branchId, input?)` | `Snapshot` | `{ name?, timestamp?, lsn?, expiresAt? }` (point-in-time) |
200+
| `update(projectId, snapshotId, input)` | `Snapshot` | `{ name?, expiresAt? }``expiresAt: null` clears the TTL |
201+
| `delete(projectId, snapshotId)` | **→void** | |
202+
| `restore(projectId, snapshotId, input?)` | `Branch` | see below |
203+
| `getSchedule` / `setSchedule(projectId, branchId, …)` | `BackupSchedule` / **→void** | |
204+
205+
`restore` input: `{ name?, targetBranchId?, finalize?, preview?, keepOnAbort? }`.
206+
- Restoring **as a new branch** (no `targetBranchId`) finalizes by default → ready to use.
207+
- Restoring **onto an existing branch** doesn't finalize by default, so you can preview first.
208+
- **Transaction-style** `preview`: restores un-finalized, runs your callback, then **finalizes (commit)** on `true` or **deletes the preview branch (abort)** on `false` (unless `keepOnAbort`):
209+
210+
```ts
211+
await neon.snapshots.restore(projectId, snapshotId, {
212+
targetBranchId,
213+
preview: async (branch) => (await checks(branch)) === "ok", // true → commit · false → abort
214+
});
215+
```
216+
217+
### `neon.operations` / `neon.consumption` / `neon.apiKeys` / `neon.regions` / `neon.user` / `neon.auth`
218+
219+
- **`operations`**`list` **[P]** `/ get`, and `waitFor(operations, { pollIntervalMs?, timeoutMs?, signal? })` (the readiness primitive).
220+
- **`consumption`** — cursor-paginated billing metrics: `perProject`, `perProjectV2`, `perBranchV2` (each takes `{ from, to, granularity, project_ids?, org_id? }`; `perBranchV2` requires `project_ids`). Consumption requires a Scale plan or above.
221+
- **`apiKeys`**`list / create(keyName) / revoke`; the created `key` token is shown **once**.
222+
- **`regions.list()`**, **`user.me()` / `user.organizations()`**.
223+
- **`auth`** — branch-scoped Neon Auth: `get / create / disable / updateConfig`, plus `oauthProviders`, `trustedDomains`, and `users` sub-resources.
224+
225+
## Drop down to the raw client
226+
227+
The ergonomic namespaces don't wrap every endpoint. For anything else, `raw` exposes every endpoint 1:1 — pass `neon.client` so the call reuses the client's auth:
228+
229+
```ts
230+
import { raw } from "@neon/sdk";
231+
// or, for guaranteed tree-shaking: import { getProjectBranchSchema } from "@neon/sdk/raw";
232+
233+
const { data, error } = await raw.getProjectBranchSchema({
234+
client: neon.client,
235+
path: { project_id, branch_id },
236+
query: { db_name: "neondb" }, // db_name is required
237+
});
238+
```
239+
240+
The raw layer speaks the same result contract: `{ data, error }` by default, or pass `throwOnError: true` for the bare resource. All request/response/error **types** are re-exported flat from `@neon/sdk` for `import type { Project, Branch, … }`.
241+
242+
Wait on operations from a raw mutation with the readiness primitive:
243+
244+
```ts
245+
const { data } = await raw.createProjectBranch({
246+
client: neon.client,
247+
path: { project_id: projectId },
248+
body: { branch: { name: "wip" } },
249+
});
250+
const { error } = await neon.operations.waitFor(data!.operations, { timeoutMs: 120_000 });
251+
```
252+
253+
## Migrating from `@neondatabase/api-client`
254+
255+
There's no rush — `@neondatabase/api-client` is not being deprecated. When you do move: swap axios-style `try/catch` for the `{ data, error }` envelope (or set `throwOnError: true` to keep throwing), and replace hand-rolled operation polling with `waitForReadiness` / the workflow methods (`createAndConnect`, `createWithCompute`).
256+
257+
## Further reading
258+
259+
- npm: https://www.npmjs.com/package/@neon/sdk
260+
- Neon TypeScript SDK docs: https://neon.com/docs/reference/typescript-sdk.md
261+
- Neon API reference: https://neon.com/docs/reference/api-reference
262+
- Building a platform on Neon: the `neon-for-agent-platforms` skill (`npx skills add neondatabase/neon-for-agent-platforms`) ships runnable `@neon/sdk` scripts for provisioning, branching, snapshots, project transfer, and consumption metrics.

0 commit comments

Comments
 (0)