Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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