Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/api-package-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,16 @@ jobs:
- name: Regenerate API package
run: pnpm generate
working-directory: packages/api
env:
SUPABASE_API_URL: https://api.supabase.com

- name: Format API package
run: pnpm exec nx run @supabase/api:fmt:fix

- name: Check for generated changes
id: check
run: |
if git diff --ignore-space-at-eol --exit-code --quiet packages/api/src/generated; then
if git diff --ignore-space-at-eol --exit-code --quiet packages/api/src/generated packages/api/scripts/openapi-source.json; then
echo "No generated changes detected."
echo "has_changes=false" >> "$GITHUB_OUTPUT"
else
Expand Down Expand Up @@ -61,7 +63,7 @@ jobs:
body: |
This PR was automatically created to sync the generated `@supabase/api` package with the latest Management API OpenAPI document.

Changes were detected in the upstream OpenAPI document exposed by `https://api.supabase.com/api/v1-json`.
Changes were detected in the upstream OpenAPI documents exposed by `https://api.supabase.com/api/v1-json` and `https://api.supabase.com/api/v2-json`.
branch: sync/api-package
base: develop

Expand Down
9 changes: 9 additions & 0 deletions apps/cli/tests/helpers/legacy-mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -642,8 +642,17 @@ export function mockLegacyPlatformApiService(
},
});

// The legacy shell is a Go-parity port and only calls v1 operations, so v2
// has no stub support — any v2 call from legacy code is a wiring bug.
const v2Proxy = new Proxy({} as ApiClient["v2"], {
get(_target, prop: string) {
return () => Effect.die(`Unmocked LegacyPlatformApi.v2.${prop}`);
},
});

const layer = Layer.succeed(LegacyPlatformApi, {
v1: v1Proxy,
v2: v2Proxy,
// Direct-service consumers don't exercise the raw-execute escape hatch.
executeRaw: () => Effect.die("Unmocked LegacyPlatformApi.executeRaw"),
} as ApiClient);
Expand Down
81 changes: 76 additions & 5 deletions packages/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,14 @@ import { createApiClient } from "@supabase/api";
const client = await createApiClient({ accessToken: "<token>" });

const projects = await client.v1.listAllProjects();
const projectConfig = await client.v2.getProjectConfig({ ref: "<project-ref>" });
```

Operations are namespaced by version, derived from the leading path segment (`/v1/...` or
`/v2/...`). Same-named operations can coexist under separate namespaces: `client.v1.listOrganizationMembers`
and `client.v2.listOrganizationMembers` are distinct operations hitting `/v1/...` and `/v2/...`
respectively.

`baseUrl` defaults to `https://api.supabase.com` and `accessToken` can also come from
`SUPABASE_ACCESS_TOKEN`.

Expand All @@ -31,7 +37,10 @@ import { makeApiClient } from "@supabase/api/effect";
const program = Effect.gen(function* () {
const client = yield* makeApiClient({ accessToken: "<token>" });

return yield* client.v1.listAllProjects();
const projects = yield* client.v1.listAllProjects();
const projectConfig = yield* client.v2.getProjectConfig({ ref: "<project-ref>" });

return { projects, projectConfig };
});
```

Expand All @@ -51,6 +60,7 @@ The only callable client surface is the versioned namespace:

```ts
const projects = await client.v1.listAllProjects();
const projectConfig = await client.v2.getProjectConfig({ ref: "<project-ref>" });
```

For tools that need the raw generated spec:
Expand All @@ -74,14 +84,75 @@ The public binary input contract is:
## Development

```sh
pnpm check:all # Run all quality checks in parallel
pnpm fix:all # Auto-fix lint, format, and unused exports in parallel
pnpm test # Run tests
pnpm generate # Refresh the OpenAPI spec and regenerate the SDK
pnpm check:all # Run all quality checks in parallel
pnpm fix:all # Auto-fix lint, format, and unused exports in parallel
pnpm test # Run tests
pnpm generate # Refresh the OpenAPI spec and regenerate the SDK
pnpm generate:check # Regenerate in place and fail on any resulting diff
```

## Spec pipeline

The spec is built from two upstream OpenAPI documents, `{baseUrl}/api/v1-json` and
`{baseUrl}/api/v2-json`. They are fetched and merged into a single document (paths and
`components.schemas` are unioned, and `info.title` is normalized to `Supabase API`), then
overrides from `scripts/openapi-overrides.json` are applied to the merged document. The result is
validated — operation ids must be unique, and version-prefixed operation ids must match the
path's leading segment — before being written to `src/generated/openapi.json`. The merged
document keeps only the keys the generator consumes (`openapi`, `info`, `paths`,
`components.schemas`); upstream extras such as `servers`, `tags`, and `components.securitySchemes`
are dropped so the snapshot never contains keys a regeneration would remove.

The committed snapshot and the generated modules are also checked against each other offline in
ordinary test runs: `scripts/generated-output-sync.unit.test.ts` re-renders every generated file
from the committed snapshot and requires byte equality, and `src/generated-contract-sync.unit.test.ts`
asserts the operation-level bijection. Hand edits to `src/generated` fail both.

The base URL is resolved in this order:

1. `SUPABASE_API_URL` environment variable
2. `scripts/openapi-source.json`, a committed sidecar file (`{ "baseUrl": ... }`) that is
rewritten after every successful `pnpm generate` run
3. `https://api.supabase.com`

To refresh from staging instead of production:

```sh
SUPABASE_API_URL=https://api.supabase.green pnpm generate
```

`pnpm generate` is the single command to regenerate the spec and SDK. `pnpm generate:check`
regenerates in place, formats, and fails if that produces any diff in `src/generated` or
`scripts/openapi-source.json` — useful for verifying the committed snapshot is still current. If a
failed check leaves an unwanted diff, discard it with:

```sh
git restore -- src/generated scripts/openapi-source.json
```

The hourly [`api-package-sync.yml`](../../.github/workflows/api-package-sync.yml) workflow runs
`generate` against production and opens a PR against `develop` whenever it detects drift, acting
as the automated drift detector for the committed snapshot.

### Overrides

`scripts/openapi-overrides.json` is a JSON-Patch-_like_ array applied to the merged document. It
supports:

- `test` — assert a value at `path` before proceeding (as in RFC 6902)
- `add` — add a value at `path`; throws if the key already exists
- `replace` — replace the value at `path`
- `remove` — remove the value at `path` **if present**

`remove` is deliberately remove-if-present rather than RFC 6902's strict "must exist" semantics,
because the upstream documents differ between environments — staging's `v2-json` is currently
served by two backend variants that disagree about some paths. Entries may carry a `$comment`
field to document why an override exists.

### Known limitation: `deepObject` query parameters

Three v2 operations declare object-valued query parameters with `style: deepObject`:
`v2-list-organization-members`, `v2-list-organization-projects`, and
`v2-list-organization-github-connections`. The client currently serializes these as JSON strings
rather than the expected `page[size]=...` form. Do not rely on those parameters until this is
fixed.
4 changes: 3 additions & 1 deletion packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"scripts": {
"generate:spec": "bun run scripts/download-openapi.ts",
"generate": "bun run generate:spec && bun run scripts/generate.ts",
"generate:check": "pnpm generate && pnpm exec nx run @supabase/api:fmt:fix && git diff --exit-code -- src/generated scripts/openapi-source.json",
"test": "nx run-many -t test:core test:e2e --projects=$npm_package_name",
"test:core": "nx run-many -t test:unit test:integration --projects=$npm_package_name",
"check:all": "nx run-many -t types:check lint:check fmt:check knip:check --projects=$npm_package_name",
Expand Down Expand Up @@ -45,7 +46,8 @@
"scripts/download-openapi.ts",
"scripts/download-openapi.unit.test.ts",
"scripts/generate.ts",
"scripts/generate.unit.test.ts"
"scripts/generate.unit.test.ts",
"scripts/generated-output-sync.unit.test.ts"
],
"ignoreDependencies": [
"undici",
Expand Down
Loading
Loading