Skip to content

Answer unmatched /api paths with 404 ProblemDetails instead of the SPA bundle - #217

Merged
Sev7eNup merged 1 commit into
mainfrom
fix/api-404-instead-of-spa-fallback
Aug 16, 2026
Merged

Answer unmatched /api paths with 404 ProblemDetails instead of the SPA bundle#217
Sev7eNup merged 1 commit into
mainfrom
fix/api-404-instead-of-spa-fallback

Conversation

@Sev7eNup

Copy link
Copy Markdown
Owner

Problem

MapFallbackToFile("index.html") matches whatever no endpoint claimed — including unmatched /api paths. A typo, an endpoint that moved, or a route parameter that failed its type constraint all answered 200 text/html with the SPA bundle.

Measured against a 1.2.6 lab install:

Request Before
GET /api/triggers 200 text/html (SPA)
GET /api/secrets 200 text/html (SPA)
GET /api/global-variables/not-a-guid 200 text/html (SPA)
GET /api/workflows/<unknown-guid> 404 application/problem+json ✅ (a real action answered)

np, the MCP server and the SPA's own error handling all treated the HTML page as a valid response body. A missing endpoint is exactly the failure that must be loud — and swallowing it also hides routing regressions from tests.

Change

A dedicated MapFallback("/api/{**rest}") registered before the SPA catch-all returns 404 ProblemDetails with code: NOT_FOUND. Its literal prefix outranks the SPA's {*path:nonfile}, so the scope is exactly the API surface — deep links the SPA owns (/workflows/<id> and friends) still reach index.html.

AllowAnonymous, so a missing endpoint reports as missing rather than as unauthorized. 401-vs-200 was already distinguishable before, so this exposes nothing new.

Tests

tests/NodePilot.Api.Tests/Hosting/ApiNotFoundFallbackTests.cs — 5 cases over the real Program.cs pipeline: three unmatched shapes (unknown path, real prefix without an action, failed :guid constraint) assert 404 + application/problem+json + no <!doctype; one asserts a non-API deep link still routes to the SPA fallback; one asserts a matched endpoint is unshadowed.

Ran: dotnet test tests/NodePilot.Api.Tests — 2407 passed.

…A bundle

MapFallbackToFile claims every path no endpoint matched, which included unmatched
/api paths. A typo, an endpoint that moved, or a route parameter failing its type
constraint all answered 200 text/html with index.html, and every client read that
as success. Measured against a lab install: GET /api/triggers and GET
/api/global-variables/not-a-guid both returned a 200 HTML page, which np, the MCP
server and the SPA's own error handling each accepted as a valid response body. A
missing endpoint is precisely the failure that has to be loud, and swallowing it
also hides routing regressions from tests.

A dedicated MapFallback("/api/{**rest}") now returns 404 ProblemDetails with
code NOT_FOUND. Its literal prefix outranks the SPA catch-all, so the scope is
exactly the API surface; deep links the SPA owns still reach index.html. Declared
AllowAnonymous so a missing endpoint reports as missing rather than as
unauthorized -- 401 vs 404 was already distinguishable before, so this exposes
nothing new.
@Sev7eNup
Sev7eNup merged commit 1ce3814 into main Aug 16, 2026
9 checks passed
@Sev7eNup
Sev7eNup deleted the fix/api-404-instead-of-spa-fallback branch August 16, 2026 15:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant