router icon indicating copy to clipboard operation
router copied to clipboard

feat(router-core): validate params while matching [WIP]

Open Sheraff opened this issue 1 month ago • 3 comments

[!WARNING] UNTESTED, DO NOT MERGE

Use the params.parse option (https://tanstack.com/router/latest/docs/framework/react/api/router/RouteOptionsType#paramsparse-method) to validate path params while matching a route. This allows us to look for another match if the validation fails.

Pros: more flexible routing. Any $param can now become a regex route segment (or any validation you like).

Cons: routes with a params.parse method cannot share the same node as other routes of otherwise the same shape. If used frequently, this increases branching, which increases the number of branches we need to explore to find a match.


Some details:

  • routes w/ params.parse have priority over routes without (all else being equal)
  • extractParams is now "resumable" so we can call it at several point while matching without repeating work (at the expense of allocating more objects)

TODO:

  • [ ] find a way to store and return the parsed params from params.parse to avoid duplicate work after matching
  • [ ] when several params.parse function are on the way, should the 2nd receive the output of the 1st? Or do they both receive the raw Record<string,string>?
  • [ ] error during params.parse should be preserved to be set in the store or thrown if opts?.throwOnError
  • [ ] why is there routeParams and strictParams?
  • [ ] support deprecated options.parseParams in addition to options.params.parse
  • [ ] should notFound and redirect errors still be respected when we're trying to "continue matching" despite the parsing error?

I think each parse function receives the output of the previous one, see below. But I also think this means we can't decode it to something not stringifyable by String() (i.e. objects) because this partially parsed object will be used in interpolatePath to generate part of the matchId

https://github.com/TanStack/router/blob/d9e403b8b32867f9f84e33120467e0d1bcc13a11/packages/router-core/src/router.ts#L1393-L1417

Sheraff avatar Nov 21 '25 20:11 Sheraff

[!IMPORTANT]

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This change extends the routing engine to support route-level custom parameter parsing. Route options now accept a parse function that is stored on segment nodes during tree construction. During route matching, parameters are extracted and parsed early in the traversal; parse errors prune the current path from consideration.

Changes

Cohort / File(s) Summary
Route-level parameter parsing support
packages/router-core/src/new-process-route-tree.ts
Added parse field to SegmentNode<T> type and RouteLike.options.params to store and apply custom parameter parsing functions. Modified extractParams to return [params, state] tuple. Updated getNodeMatch to apply node.parse when present. Enhanced MatchStackFrame with extract and params fields to thread parsing state through the stack. Adjusted sortDynamic to prioritize nodes with parse functions. Updated all node creation functions (createStaticNode, createDynamicNode, etc.) to initialize parse: null. Modified matching paths (static, dynamic, optional, wildcard) to propagate parse state consistently.

Sequence Diagram

sequenceDiagram
    participant Router as Route Matching Engine
    participant Tree as Segment Tree
    participant Node as Segment Node
    participant Parse as Parse Function
    participant Result as Match Result

    Router->>Tree: Start traversal
    Router->>Node: Check for parse function
    alt parse function exists
        Node-->>Router: parse function found
        Router->>Router: Extract params early
        Router->>Parse: Apply parse(params)
        alt parse succeeds
            Parse-->>Router: Parsed params
            Router->>Router: Attach to frame
            Router->>Result: Continue matching with parsed params
        else parse throws error
            Parse-->>Router: Error
            Router->>Router: Prune current path
            Router->>Result: Try alternate routes
        end
    else no parse function
        Node-->>Router: parse = null
        Router->>Result: Continue standard matching
    end

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Core matching logic refactoring: Dense changes to the route matching pipeline affecting parameter extraction, frame propagation, and error handling
  • Type signature changes: Multiple updates to SegmentNode, MatchStackFrame, and function signatures require careful verification
  • Behavioral changes in error handling: Parse failures now prune paths—verify this integrates correctly with existing match fallback logic
  • Sorting logic update: sortDynamic now considers parse presence; review prioritization semantics to ensure correct node ordering

Possibly related PRs

  • TanStack/router#5124: Related change to route params.parse handling in router-core—addresses when parsing runs and parse result reuse.

Suggested labels

package: router-core

Suggested reviewers

  • schiller-manuel

Poem

🐰 A rabbit hops through routes with grace,
Parse functions now find their place,
Parameters bend to custom will,
Errors prune with graceful skill,
The tree grows wise, the matches thrill! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(router-core): validate params while matching [WIP]' accurately describes the main change: introducing parameter validation during route matching through route-level param parsing hooks.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

coderabbitai[bot] avatar Nov 21 '25 20:11 coderabbitai[bot]

View your CI Pipeline Execution ↗ for commit 7f821bfb2f25536919d0ae0e86e00c580e749eea

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

☁️ Nx Cloud last updated this comment at 2025-12-22 20:43:02 UTC

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

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/arktype-adapter@5936
@tanstack/directive-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/directive-functions-plugin@5936
@tanstack/eslint-plugin-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/eslint-plugin-router@5936
@tanstack/history

npm i https://pkg.pr.new/TanStack/router/@tanstack/history@5936
@tanstack/nitro-v2-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/nitro-v2-vite-plugin@5936
@tanstack/react-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router@5936
@tanstack/react-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-devtools@5936
@tanstack/react-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-ssr-query@5936
@tanstack/react-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start@5936
@tanstack/react-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-client@5936
@tanstack/react-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-server@5936
@tanstack/router-cli

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-cli@5936
@tanstack/router-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-core@5936
@tanstack/router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools@5936
@tanstack/router-devtools-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools-core@5936
@tanstack/router-generator

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-generator@5936
@tanstack/router-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-plugin@5936
@tanstack/router-ssr-query-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-ssr-query-core@5936
@tanstack/router-utils

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-utils@5936
@tanstack/router-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-vite-plugin@5936
@tanstack/server-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/server-functions-plugin@5936
@tanstack/solid-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router@5936
@tanstack/solid-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-devtools@5936
@tanstack/solid-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-ssr-query@5936
@tanstack/solid-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start@5936
@tanstack/solid-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-client@5936
@tanstack/solid-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-server@5936
@tanstack/start-client-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-client-core@5936
@tanstack/start-fn-stubs

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-fn-stubs@5936
@tanstack/start-plugin-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-plugin-core@5936
@tanstack/start-server-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-core@5936
@tanstack/start-static-server-functions

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-static-server-functions@5936
@tanstack/start-storage-context

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-storage-context@5936
@tanstack/valibot-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/valibot-adapter@5936
@tanstack/virtual-file-routes

npm i https://pkg.pr.new/TanStack/router/@tanstack/virtual-file-routes@5936
@tanstack/vue-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-router@5936
@tanstack/vue-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-router-devtools@5936
@tanstack/vue-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-router-ssr-query@5936
@tanstack/vue-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start@5936
@tanstack/vue-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start-client@5936
@tanstack/vue-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start-server@5936
@tanstack/zod-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/zod-adapter@5936

commit: 7f821bf

pkg-pr-new[bot] avatar Nov 21 '25 20:11 pkg-pr-new[bot]

@coderabbitai review

Sheraff avatar Dec 22 '25 20:12 Sheraff

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

coderabbitai[bot] avatar Dec 22 '25 20:12 coderabbitai[bot]