Skip to content

feat(api): Read the API base path from the spec's server URL - #20787

Merged
BYK merged 1 commit into
masterfrom
feat/api-base-path-from-servers
Nov 13, 2026
Merged

BYK merged 1 commit into
masterfrom
feat/api-base-path-from-servers

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Merge order: getsentry/sentry-api-schema#148 → getsentry/sentry-docs#20787 → getsentry/sentry#131065 → the getsentry/toolkit bump, after @sentry/api 2.0.0 is published. Docs has to merge before sentry#131065. When that merges, the spec sync commits the new spec to sentry-api-schema, which auto-releases @sentry/api and auto-bumps the schema in sentry-docs. Without this PR merged first, the docs would drop /api/0 from paths and curl examples. This PR also works with the current spec.

DESCRIBE YOUR PR

Sentry's OpenAPI spec is moving /api/0 out of every path and into its server URL, so clients read the base path (and the API version in it) from the spec (getsentry/sentry-api-schema#148, then the getsentry/sentry change). The API reference ignored the spec-level servers, so after that change, pages and curl examples would drop /api/0.

apiLocation() joins the server URL's path to each operation path. The server it shows is only the origin, still https://sentry.io by default, or the operation's own server.

Before, with the new spec shape:

GET /organizations/
curl https://sentry.io/organizations/

After, with either spec shape:

GET /api/0/organizations/
curl https://sentry.io/api/0/organizations/

No visual change, so no screenshots. I ran apiCategories() against a spec built from getsentry/sentry master and against the same spec built with the change. The page data was byte-identical, including the per-operation https://{region}.sentry.io server on /api/0/seer/models/.

Checks: the resolveOpenAPI.spec.ts vitest suite passes, with one new table test covering both spec shapes and an operation-level server. ESLint and Prettier pass on the changed files.

IS YOUR CHANGE URGENT?

  • Urgent deadline (GA date, etc.): YYYY-MM-DD
  • Other deadline: YYYY-MM-DD
  • No deadline: Not urgent, can wait up to 1 week+

It does block the getsentry/sentry spec change from merging.

PRE-MERGE CHECKLIST

  • Checked Vercel preview for correctness, including links
  • PR was reviewed and approved by any necessary SMEs (subject matter experts)
  • PR was reviewed and approved by a member of the Sentry docs team

via @BYK.

--

View Session [Sentry]

Sentry's OpenAPI spec is moving /api/0 out of every path and into its
server URL (https://{region}.sentry.io/api/0). The API reference ignored
the spec-level server, so pages and curl examples would lose /api/0.
Join the server URL's path to each operation path, and keep only the
origin as the server. Both spec shapes render the same pages.

Co-Authored-By: BYK <126780+BYK@users.noreply.github.com>
@sentry-junior
sentry-junior Bot requested a review from BYK November 13, 2026 12:29
@vercel

vercel Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
develop-docs Ready Ready Preview Nov 13, 2026 12:36pm UTC
sentry-docs Ready Ready Preview Nov 13, 2026 12:36pm UTC

Request Review

@github-actions github-actions Bot added the Priority: Normal Docs review has no urgent deadline label Nov 13, 2026
@cursor

cursor Bot commented Nov 13, 2026

Copy link
Copy Markdown
Contributor

Getting ready to monitor this change · Rollout details

Status Service Environment
⏳ Getting ready to monitor this change

BYK added a commit to getsentry/sentry-api-schema that referenced this pull request Nov 13, 2026
…148)

**Merge order:**
[#148](#148)
→
[getsentry/sentry-docs#20787](getsentry/sentry-docs#20787)
→
[getsentry/sentry#131065](getsentry/sentry#131065)
→ the getsentry/toolkit bump, after `@sentry/api` 2.0.0 is published.
Docs has to merge before sentry#131065. When that merges, the spec sync
commits the new spec to sentry-api-schema, which auto-releases
`@sentry/api` and auto-bumps the schema in sentry-docs. Without
sentry-docs#20787 merged first, the docs would drop `/api/0` from paths
and curl examples. This PR supersedes #147, which hard-coded
`API_PREFIX`.

Per @BYK, the server decides the base path. `/api/0` comes from the
spec's server URL. It's exported as `API_BASE_PATH`, and every path in
the package is relative to it. Nothing here hard-codes `0`. A future
`/api/1` spec would produce `API_BASE_PATH = "/api/1"` with no code
change.

**Design**
- `lib/api-base-path.mjs` reads the base path from the path of
`servers[0].url`, makes `paths` relative, and trims server URLs to their
origin. That includes the per-operation override on `listSeerModels`.
- **Until the sentry PR lands,** the base path is the `/api/<version>`
prefix that every path shares. It's marked TODO. Both spec shapes build
the same `src/`; I checked this with `diff -r`. So the sentry change
only syncs a byte-identical package through the bot.
- **`baseUrl` stays the Sentry origin.** That's what users have,
including self-hosted URLs and `links.regionUrl`. The patched `buildUrl`
builds the request URL as origin + `API_BASE_PATH` + operation path.
- **Exported** from `@sentry/api`, `@sentry/api/client` and
`@sentry/api/operations`.
- **The four `/api/0` copies are gone:** in `core.ts`,
`buildSentryApiUrl`, `parseApiPathScope` and cache invalidation. So are
the copies in the idempotent matcher, `normalize-spec` and
`source-analysis`. Matching still finds the base path anywhere in the
URL, as before.

**Before / After**
```ts
// @sentry/api/operations
{ functionName: "listOrganizations", path: "/api/0/organizations/" }
{ functionName: "listOrganizations", path: "/organizations/" }

import { API_BASE_PATH } from "@sentry/api"; // "/api/0", from servers[0].url

// raw requests: url is relative to API_BASE_PATH
sentry.client.prepareRequest({ method: "GET", url: "/api/0/organizations/" }); // before
sentry.client.prepareRequest({ method: "GET", url: "/organizations/" });       // after
// both send https://sentry.io/api/0/organizations/
```

**Breaking: major bump to 2.0.0**
- `operations[].path` and `IdempotentOperation.path` are relative.
Build-time lists derived from the catalog must be regenerated, such as
toolkit's `idempotent-operations.json`.
- Raw `client.request`, `client.get` and `prepareRequest` take relative
`url`s. An old `/api/0/...` URL now gets a doubled base path.
- Generated function names, types and the request URLs of typed
operations don't change: `sentry-client.gen.ts` is byte-identical.

Co-Authored-By: BYK <126780+BYK@users.noreply.github.com>

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A640c5343d03e547097aa9b8a711daaf5 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A640c5343d03e547097aa9b8a711daaf5)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A640c5343d03e547097aa9b8a711daaf5/?project=4510944073809921)

<!-- junior-session-footer:end -->

---------

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: BYK <126780+BYK@users.noreply.github.com>
BYK added a commit to getsentry/toolkit that referenced this pull request Nov 13, 2026
**Depends on:** `@sentry/api` 2.0.0, which is published
([getsentry/sentry-api-schema#148](getsentry/sentry-api-schema#148)).
This is the last step of the chain sentry-api-schema#148 →
[sentry-docs#20787](getsentry/sentry-docs#20787)
→ [sentry#131065](getsentry/sentry#131065) →
this PR.

Sentry's OpenAPI spec is moving `/api/0` out of every path and into its
server URL. `@sentry/api` 2.0 reads the base path from that URL, exports
it as `API_BASE_PATH`, and makes operation paths relative. This PR drops
toolkit's private `/api/0` copies and uses the export instead, so the
version number lives only in the spec.

Before:
```ts
const API_PREFIX = "/api/0";                          // cli/src/commands/api.ts
this.apiPrefix = `${protocol}://${host}/api/0`;       // mcp-core api-client
path: operation.path,                                 // "/api/0/organizations/" in 1.x
```
After:
```ts
import { API_BASE_PATH } from "@sentry/api/client";
this.apiPrefix = `${protocol}://${host}${API_BASE_PATH}`;
path: `${API_BASE_PATH}${operation.path}`,            // still "/api/0/organizations/"
```

- **CLI endpoint index:** it keeps full request paths, so `sentry
schema` and `sentry api` still show and accept `/api/0/...`.
- **Requests:** CLI raw requests now pass relative URLs to
`prepareRequest`.
- **Unchanged on purpose:** user-facing help text and examples still say
`/api/0`.
- **Breaking:** only the `@sentry/api` 1.x → 2.0.0 bump. CLI and MCP
behavior doesn't change, so no toolkit major is needed.

**#2229 (stacked on #1807):** on 2.0 it needs two changes:
- replace its local `API_PREFIX = "/api/0"` in `error-reporting.ts` with
`API_BASE_PATH`;
- build `api-path-templates.json` from `` `${API_BASE_PATH}${op.path}`
``, because `op.path` is now relative.

**Checks:**
- `pnpm run tsc` and `pnpm run lint` pass.
- mcp-core vitest: 2301 passed.
- The CLI api, schema, sentry-client, error-reporting, issue-links and
script tests: 82 files, 1685 passed.
- `pnpm install --frozen-lockfile` passes with the
`minimumReleaseAgeExclude` entry moved from 1.83.0 to 2.0.0.

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A640c5343d03e547097aa9b8a711daaf5 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A640c5343d03e547097aa9b8a711daaf5)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A640c5343d03e547097aa9b8a711daaf5/?project=4510944073809921)

<!-- junior-session-footer:end -->

---------

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: BYK <126780+BYK@users.noreply.github.com>
@BYK
BYK merged commit e99454f into master Nov 13, 2026
29 checks passed
@BYK
BYK deleted the feat/api-base-path-from-servers branch November 13, 2026 14:37
sentry-junior Bot added a commit that referenced this pull request Nov 13, 2026
Keep the deletion of src/build/open-api/types.ts and the @sentry/api/docs reader, and carry master's full-path display (#20787) over to it: API paths now get the docs data's apiBasePath prepended. apiLocation and its tests are dropped, because @sentry/api/docs already splits the base path out of the server URLs.
BYK added a commit to getsentry/sentry that referenced this pull request Nov 13, 2026
…131065)

> **Depends on:**
[getsentry/sentry-api-schema#148](getsentry/sentry-api-schema#148)
and
[getsentry/sentry-docs#20787](getsentry/sentry-docs#20787).
**Merge both first.** `openapi.yml` syncs this spec to sentry-api-schema
on push to master. That repo then auto-releases `@sentry/api` and
cascades into sentry-docs, and both still assume `/api/0` in every path
until those PRs land.

The public OpenAPI spec's server URL was the bare origin, and every path
spelled out `/api/0`. Clients had to know the prefix, and the API
version in it, instead of reading it from the spec. Now the server URL
carries the versioned base path and paths are relative to it.

Before:
```json
"servers": [{ "url": "https://{region}.sentry.io", "variables": { "region": { ... } } }],
"paths": { "/api/0/organizations/{organization_id_or_slug}/releases/": { ... } }
```
After:
```json
"servers": [{ "url": "https://{region}.sentry.io/api/0", "variables": { "region": { ... } } }],
"paths": { "/organizations/{organization_id_or_slug}/releases/": { ... } }
```

**How:**
- `SPECTACULAR_SETTINGS["SERVERS"]` sets the URL to
`https://{region}.sentry.io/api/0`.
- A new last postprocessing hook, `relative_paths_hook`, strips that
base path from every path, so the hooks before it still see full paths.
A path outside the base path fails the build.
- The hook also appends the base path to per-endpoint `servers` (only
`/seer/models/` today: `https://{region}.sentry.io/api/0`). Endpoint
classes keep naming origins.
- I didn't use drf-spectacular's `SCHEMA_PATH_PREFIX_TRIM`: it doesn't
trim the `APPEND_PATHS` we use, and `custom_postprocessing_hook` matches
on full paths.

**Versioning:** the spec names `/api/0` once. A future v1 is a new
server entry or spec with `/api/1`, and downstream clients read the path
from `servers[0].url`. I picked this over an `/api/{version}` server
variable because a v1 overhaul would come with different paths and
schemas, which one spec can't describe. Self-hosted stays as before:
clients swap the origin and keep the path.

**Breaking change** for anyone reading the spec: paths no longer start
with `/api/0`, so join them to the server URL. Known consumers:
- `@sentry/api`: handled by sentry-api-schema#148, which ships as 2.0.0.
It reads both shapes and generates byte-identical clients from this spec
and the master spec.
- docs.sentry.io API reference: handled by sentry-docs#20787. It renders
the same page data from both shapes.
- getsentry/toolkit consumes `@sentry/api` and follows after the 2.0.0
release.

Nothing else in this repo reads the spec, apart from the `openapi.yml`
sync and the `openapi-diff` comment workflow. The private investigation
schema reuses these settings and now has the same shape; nothing
consumes it.

**Checks** (run locally):
- `make build-api-docs` and `make build-investigation-api-docs` both
pass. Against master, the derefed spec differs only in `servers`, the
path keys and the seer op server.
- `pnpm run validate-api-examples` passes.
- `pytest tests/apidocs` passes (167). Tests changed:
  - `RelativePathsHookTest` is new.
- `APIDocsTestCase` keeps the server URL path when it swaps the origin.
Without that, openapi-core can't match any request (`ServerNotFound`).
- Two endpoint tests read schema entries by path and now use relative
keys.
- prek and mypy pass on the changed files.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A640c5343d03e547097aa9b8a711daaf5 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A640c5343d03e547097aa9b8a711daaf5)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A640c5343d03e547097aa9b8a711daaf5/?project=4510944073809921)

<!-- junior-session-footer:end -->

---------

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: BYK <126780+BYK@users.noreply.github.com>

This branch was successfully deployed

2 active deployments
Preview – sentry-docs — b6aa0c28 Deployed Nov 13, 2026 by vercel[bot]
Preview – develop-docs — b6aa0c28 Deployed Nov 13, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Priority: Normal Docs review has no urgent deadline

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant