router icon indicating copy to clipboard operation
router copied to clipboard

fix(start-server-core): return 404 for API routes without GET handler

Open s3847243 opened this issue 1 month ago • 2 comments

Fixes #5473:
API routes without a GET handler returned 200 + SPA shell instead of 404 JSON.

Previously:

  • Visiting /api/xyz where only a POST handler existed returned 200 OK with the HTML shell.

Now:

  • Routes without a matching method (and no ANY fallback) correctly return 404 JSON ({ "error": "Not Found" }).
  • HEAD requests behave correctly (empty 404 if no GET handler).

Summary by CodeRabbit

  • New Features

    • Improved HTTP method handling: normalized method matching and automatic HEAD→GET mapping when GET exists.
  • Bug Fixes

    • Clearer 404 behavior: HEAD returns an empty response when unmapped; other methods return JSON { error: "Not Found" } and stop processing.
  • Tests

    • Added unit tests for method resolution, HEAD behavior, ANY-handler fallback, and missing-handler cases.
    • Added test helpers and mocks to support routing and start-up scenarios.
  • Chores

    • Updated dev build config to alias test helpers for easier testing.

✏️ Tip: You can customize this high-level summary in your review settings.

s3847243 avatar Nov 05 '25 07:11 s3847243

Walkthrough

Normalize server route handler keys to uppercase, map HEAD to GET when a GET handler exists, consult an ANY fallback, and return early with 404 responses when no handler is found. Adds unit tests, test mocks, and Vite aliases pointing to those mocks for testing.

Changes

Cohort / File(s) Summary
Core handler logic update
packages/start-server-core/src/createStartHandler.ts
Normalize handler keys to uppercase, treat HEAD as GET when GET exists, consult ANY fallback, and return early with 404 (special-case HEAD response body) when no handler is found.
Unit tests
packages/start-server-core/tests/createStartHandler.test.ts
New tests covering GET/POST/HEAD/ANY resolution and 404 behavior across method/handler configurations and SPA fallback simulation.
Test mocks
packages/start-server-core/tests/mocks/injected-head-scripts.ts, packages/start-server-core/tests/mocks/router-entry.ts, packages/start-server-core/tests/mocks/start-entry.ts, packages/start-server-core/tests/mocks/start-manifest.ts
New mock modules: empty injectedHeadScripts export, mutable currentHandlers and fake router, startInstance.getOptions() stub, and a minimal start manifest helper.
Vite test config
packages/start-server-core/vite.config.ts
Add resolve.alias mappings to point virtual/module IDs to the new test mock files (adds import path from 'node:path' and uses path.resolve for aliases).

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Server as createStartHandler
    participant Lookup as Handler Lookup (normalized)
    participant HandlerFn as Selected Handler
    participant Response

    Client->>Server: HTTP Request (method, path)
    Server->>Lookup: Normalize handlers keys → UPPERCASE
    Server->>Lookup: Lookup by method
    alt method is HEAD and GET exists
        Lookup-->>Server: select GET handler
    else no method match -> try ANY
        Lookup-->>Server: select ANY handler (if present)
    end

    alt Handler found
        Server->>HandlerFn: invoke handler
        HandlerFn-->>Response: handler response
        Response-->>Client: handler-defined status/body
    else No handler
        alt request method is HEAD
            Server->>Response: 404 with empty JSON content-type
        else
            Server->>Response: 404 with {"error":"Not Found"} JSON body
        end
        Response-->>Client: 404
    end

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Areas to review closely:
    • Uppercasing/normalization logic vs. existing handler registrations
    • HEAD→GET mapping precedence relative to explicit HEAD and ANY
    • 404 response shape and content-type differences for HEAD vs other methods
    • Vite alias correctness and path.resolve usage

Possibly related PRs

  • TanStack/router#5593 — Overlapping edits to createStartHandler.ts affecting method-to-handler resolution and fallback behavior.
  • TanStack/router#5896 — Related changes touching server route handler selection and dispatch flow in createStartHandler.ts.

Suggested reviewers

  • schiller-manuel

Poem

🐇
I hopped through keys and made them bright,
HEAD follows GET when it’s in sight,
ANY listens when others flee,
404s now honest, plain to see,
Mocks snug in burrows — tests sleep tight.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main fix: returning 404 status for API routes without GET handlers, which directly addresses issue #5473.
Linked Issues check ✅ Passed The PR successfully addresses all coding requirements from issue #5473: returns proper 404 JSON for missing HTTP methods, handles HEAD requests correctly, and prevents incorrect 200 responses with HTML.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the issue: the core handler logic, comprehensive test coverage, Vite config aliases for test mocks, and necessary test mock files are all aligned with addressing the 404 handling requirements.
✨ Finishing touches
  • [ ] 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • [ ] Create PR with unit tests
  • [ ] Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot] avatar Nov 05 '25 07:11 coderabbitai[bot]

View your CI Pipeline Execution ↗ for commit e3ccdddf24fdff44c2d764fe68953490b282a664

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ⛔ Cancelled 14s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 23s View ↗

☁️ Nx Cloud last updated this comment at 2025-11-19 20:49:10 UTC

nx-cloud[bot] avatar Nov 19 '25 20:11 nx-cloud[bot]