feat: API_URL_PREFIX setting — let the SPA call a separately-mounted django-admin-rest-api (#559)#560
Merged
Conversation
…API (#559) Today the SPA reaches the JSON API by an *implicit* convention: `django_admin_react.urls` inline-includes `django-admin-rest-api` at `<spa-mount>/api/v1/`, and the SPA derives its API base from `request.path`. That's plug-and-play but locks the consumer into the assumption that the API lives **under** the SPA's mount. A new optional `DJANGO_ADMIN_REACT["API_URL_PREFIX"]` setting lets a consumer point the SPA at a separately-mounted `django-admin-rest-api` so the API can be a single shared mount (also reachable by `django-admin-mcp-api` or non-browser clients), and the SPA hits **that** URL instead of the inline include. ## What changes - `conf.py`: new `"API_URL_PREFIX": None` default. Unset → today's behaviour, unchanged. - `urls.py`: when `API_URL_PREFIX` is set, the inline `include("django_admin_rest_api.api.urls")` is **skipped** so there's no double-mount. - `views.py`: `_resolve_api_prefix(request)` resolves the prefix (consumer override → trailing-slash-normalized; else `<mount>/api/v1/`), injected into the SPA template. - `templates/admin_react/index.html`: new `<meta name="dar-api-prefix" content="…">`. - Frontend `main.tsx`: reads the meta + passes `apiPrefix` to the `ApiClient`. - Frontend `client.ts`: `ApiClient` accepts optional `apiPrefix` and uses it to build every URL; defaults to `<mount>api/v1/` so consumers who don't set the override see no change. ## Tests - Backend (`test_spa_index.py`): default → meta is `<mount>/api/v1/`; override → meta is the override verbatim; missing-trailing-slash → resolver adds one. Full suite **45 passed**. - Frontend (`client.test.ts`): override routes every request through it; missing-trailing-slash gets one; omitted prefix keeps the legacy `<mount>api/v1/` path. Full vitest **145 passed**. Closes #559 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #559.
Why
Today the SPA reaches the JSON API by an implicit convention:
django_admin_react.urlsinline-includesdjango-admin-rest-apiat<spa-mount>/api/v1/, and the SPA derives its API base fromrequest.path. Plug-and-play, but it locks the consumer into "API is under the SPA mount."For shared deployments where one
/api/mount also servesdjango-admin-mcp-apiand non-browser clients, the SPA needs to call that URL instead.What
New optional
DJANGO_ADMIN_REACT["API_URL_PREFIX"]:urls.pyskips the inlineapi/v1/include when the override is set (no double-mount).views.py.SpaIndexViewresolves the prefix (override or<mount>/api/v1/default, trailing-slash-normalized) and injects it as<meta name="dar-api-prefix">.main.tsxreads the meta + passesapiPrefixtoApiClient;client.ts.url()uses it directly. When omitted, the client defaults to<mount>api/v1/so existing consumers see no change.Verification
test_spa_index.py): meta defaults to<mount>/api/v1/, honours the override verbatim, and adds a trailing slash if the consumer omits it. Full suite 45 passed.client.test.ts): override routes every request, missing-trailing-slash gets one, omitted prefix preserves the legacy URL. Full vitest 145 passed.Tier 4-ish (backend + frontend, no contract surface change — additive setting + additive meta tag).
🤖 Generated with Claude Code