fix(express): content-negotiated 404 for unmatched routes (#3975)#3977
Conversation
The catch-all route previously sent the friendly "Devkit Node Api" HTML
snippet with an implicit 200 for every unmatched path, including /api/*
typos. API consumers and automated agents relying on status codes for
error handling saw false-positive success responses.
Register the root route explicitly (keeps the friendly HTML at 200,
only reached when static hosting served no index.html) before the
catch-all, since Express 5's `/{*path}` also matches `/`. The catch-all
now returns 404 with JSON `{ error: 'not_found' }` for /api/* paths or
when the request explicitly accepts application/json, and a minimal
HTML 404 otherwise. Non-GET methods are unaffected (already 404 by
default).
Adds unit tests covering: unknown /api path -> JSON 404, /api without
trailing slash -> JSON 404, unknown page with Accept: text/html -> HTML
404, unknown page with Accept: application/json -> JSON 404, and root
route unchanged at 200.
|
Warning Review limit reached
Next review available in: 37 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (4)
WalkthroughExpress routing now serves the landing page only at ChangesNot-found routing
Estimated code review effort: 2 (Simple) | ~10 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #3977 +/- ##
=======================================
Coverage 93.11% 93.12%
=======================================
Files 170 170
Lines 5610 5615 +5
Branches 1807 1808 +1
=======================================
+ Hits 5224 5229 +5
Misses 311 311
Partials 75 75
Flags with carried forward coverage won't be shown. Click here to find out more. Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
…nsensitive api match (#3975)
|
The content-negotiated 404 fix is correct, but it exposed 3 integration tests that had baked in the old buggy 200-catch-all behavior. Updated them to assert the new (correct) contract instead of weakening the fix:
Nit fixes also applied per review:
Results: |
What
The Express catch-all route (
app.get('/{*path}')) sent the friendly "Devkit Node Api" HTML snippet with an implicit HTTP 200 for every unmatched path — including any/api/*typo. API consumers and automated agents that rely on status codes for error handling saw false-positive success responses with an HTML body.Why
Unknown routes should return a proper 404, content-negotiated for the caller: JSON for API/JSON-accepting clients, minimal HTML for browsers. A downstream consumer reported this.
Change
In
lib/services/express.js(initModulesServerRoutes), after theconfig.files.routesloop:app.get('/', ...)first — keeps the friendly root HTML page at 200 (only reached when static hosting served noindex.html). This must come before the catch-all since Express 5's/{*path}also matches/.404 { error: 'not_found' }JSON when the path starts with/apior the request explicitly acceptsapplication/json; otherwise a minimal HTML 404.Non-GET methods are unaffected (Express's default 404 already applied there — out of scope).
Tests
New
lib/services/tests/express.notfound.unit.tests.js(supertest + mocked deps, following the existingexpress.errorroutes.unit.tests.jspattern):GET /api/nope→ 404 JSON{ error: 'not_found' }GET /api(no trailing slash) → 404 JSONGET /nopewithAccept: text/html→ 404 HTML, no JSONGET /nopewithAccept: application/json→ 404 JSONGET /→ 200, root page unchangedVerify
npm run lint— clean on changed filesnpm run test:unit— 165 suites / 2207 tests passing (full unit suite, including the 5 new tests)Closes #3975
Summary by CodeRabbit