Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Draft
Sheraff wants to merge4 commits intomain
base:main
Choose a base branch
Loading
fromfeat-router-core-validate-params-while-matching

Conversation

@Sheraff
Copy link
Contributor

@SheraffSheraff commentedNov 21, 2025
edited
Loading

Warning

UNTESTED, DO NOT MERGE

Use theparams.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 foranother 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 aparams.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 fromparams.parse to avoid duplicate work after matching
  • when severalparams.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 duringparams.parse should be preserved to be set in the store or thrown ifopts?.throwOnError
  • why is thererouteParams andstrictParams?
  • support deprecatedoptions.parseParams in addition tooptions.params.parse
  • shouldnotFound andredirect errors still be respected when we're trying to "continue matching" despite the parsing error?
  • what if aparams.parse is set on a pathless layout route? This has no representation in the segment trie.
    • We could apply it to every child
      • missed opportunity to skip some work
      • duplicate work that could be done once
      • what if those children also haveparams.parse? then we need to handle an array of them??
    • We could add "pathless nodes" to the trie
      • highest priority
      • must always enqueue w/ no condition (except params.parse)
      • could be addedonly if there is aparams.parse
    • We could forbid this option on pathless layout routes
      • unintuitive DX
      • optimal performance

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 byString() (i.e. objects) because this partially parsed object will be used ininterpolatePath to generate part of thematchId

if(!existingMatch){
conststrictParseParams=
route.options.params?.parse??route.options.parseParams
if(strictParseParams){
try{
Object.assign(
strictParams,
strictParseParams(strictParamsasRecord<string,string>),
)
}catch(err:any){
if(isNotFound(err)||isRedirect(err)){
paramsError=err
}else{
paramsError=newPathParamError(err.message,{
cause:err,
})
}
if(opts?.throwOnError){
throwparamsError
}
}
}
}

@coderabbitai
Copy link
Contributor

coderabbitaibot commentedNov 21, 2025
edited
Loading

Walkthrough

This change extends the routing engine to support route-level custom parameter parsing. Route options now accept aparse 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
Addedparse field toSegmentNode<T> type andRouteLike.options.params to store and apply custom parameter parsing functions. ModifiedextractParams to return[params, state] tuple. UpdatedgetNodeMatch to apply node.parse when present. EnhancedMatchStackFrame withextract andparams fields to thread parsing state through the stack. AdjustedsortDynamic to prioritize nodes with parse functions. Updated all node creation functions (createStaticNode,createDynamicNode, etc.) to initializeparse: 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
Loading

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 toSegmentNode,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

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 nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring 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 nameStatusExplanation
Title check✅ PassedThe 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✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branchfeat-router-core-validate-params-while-matching

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

@SheraffSheraff changed the titlefeat(router-core): validate params while matchingfeat(router-core): validate params while matching [WIP]Nov 21, 2025
@nx-cloud
Copy link

nx-cloudbot commentedNov 21, 2025
edited
Loading

🤖 Nx Cloud AI Fix Eligible

An automatically generated fix could have helped fix failing tasks for this run, but Self-healing CI is disabled for this workspace. Visit workspace settings to enable it and get automatic fixes in future runs.

To disable these notifications, a workspace admin can disable themin workspace settings.


View yourCI Pipeline Execution ↗ for commit102ef3c

CommandStatusDurationResult
nx affected --targets=test:eslint,test:unit,tes...❌ Failed1m 53sView ↗
nx run-many --target=build --exclude=examples/*...✅ Succeeded1sView ↗

☁️Nx Cloud last updated this comment at2025-11-30 18:08:04 UTC

@pkg-pr-new
Copy link

pkg-pr-newbot commentedNov 21, 2025
edited
Loading

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-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/zod-adapter

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

commit:102ef3c

Copy link
Contributor

@coderabbitaicoderabbitaibot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/router-core/src/new-process-route-tree.ts (1)

917-919:Silent error handling may hinder debugging.

Parse errors are caught and silently cause the route to be skipped viacontinue. While this enables fallback to alternative routes, it provides no visibility into why a route was rejected, making debugging difficult for developers.

Consider adding optional logging or a debug mode that reports parse failures, especially during development.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and betweenbc79c97 andd9e403b.

📒 Files selected for processing (1)
  • packages/router-core/src/new-process-route-tree.ts (21 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: FatahChanRepo: TanStack/router PR: 5475File: e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx:8-0Timestamp: 2025-10-14T18:59:33.990ZLearning: In TanStack Router e2e test files, when a route parameter is validated at the route level (e.g., using zod in validateSearch or param validation), switch statements on that parameter do not require a default case, as the validation ensures only expected values will reach the switch.
Learnt from: nlynzaadRepo: TanStack/router PR: 5284File: e2e/react-start/basic/server.js:50-0Timestamp: 2025-09-28T21:41:45.233ZLearning: In Express v5, catch-all routes must use named wildcards. Use `/*splat` to match everything except root path, or `/{*splat}` (with braces) to match including root path. The old `*` syntax is not allowed and will cause "Missing parameter name" errors. This breaking change requires explicit naming of wildcard parameters.
Learnt from: nlynzaadRepo: TanStack/router PR: 5182File: e2e/react-router/basic-file-based/tests/non-nested-paths.spec.ts:167-172Timestamp: 2025-09-22T00:56:53.426ZLearning: In TanStack Router, underscores are intentionally stripped from route segments during path parsing, but preserved in base path segments. This is the expected behavior implemented in PR #5182.
📚 Learning: 2025-10-08T08:11:47.088Z
Learnt from: nlynzaadRepo: TanStack/router PR: 5402File: packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts:19-21Timestamp: 2025-10-08T08:11:47.088ZLearning: Test snapshot files in the router-generator tests directory (e.g., files matching the pattern `packages/router-generator/tests/generator/**/routeTree*.snapshot.ts` or `routeTree*.snapshot.js`) should not be modified or have issues flagged, as they are fixtures used to verify the generator's output and are intentionally preserved as-is.

Applied to files:

  • packages/router-core/src/new-process-route-tree.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Test
  • GitHub Check: Preview
🔇 Additional comments (4)
packages/router-core/src/new-process-route-tree.ts (4)

375-376:Verify parse prioritization is always desired.

The sort function unconditionally prioritizes nodes with parse functions over those without. While this makes sense for validation (try more specific routes first), it's hardcoded with no way to customize priority.

Consider whether this ordering is always appropriate, especially when multiple routes with different parse functions compete. The current tie-breaking logic (lines 377-394) would then determine order, which might be arbitrary.


236-244:Node reuse prevented when parse is present.

Routes with parse functions cannot share segment nodes, even if they're otherwise identical. This conservative approach is safe (prevents incorrect sharing of different parse functions), but increases memory usage if many routes have parse functions.

This is likely acceptable for correctness, but worth noting for performance-sensitive applications with large route trees.

Also applies to: 278-286


765-767:LGTM: Param extraction correctly resumes from saved state.

The logic properly handles incremental param extraction:

  1. Resumes fromleaf.extract state if present (line 765-767)
  2. Extracts params from remaining nodes
  3. Merges previously extracted params back in (line 823)

This avoids re-extracting and re-validating params when multiple parse functions exist in the route path.

Also applies to: 823-824


534-547:LGTM: Type definitions are consistent with implementation.

The parse field is properly typed acrossSegmentNode,RouteLike, andMatchStackFrame. The function signature(params: Record<string, string>) => any accurately reflects that params are strings and the return type is currently unused.

Comment on lines 908 to 920
if(node.parse){
// if there is a parse function, we need to extract the params that we have so far and run it.
// if this function throws, we cannot consider this a valid match
try{
;[params,extract]=extractParams(path,parts,frame)
// TODO: can we store the parsed value somewhere to avoid re-parsing later?
node.parse(params)
frame.extract=extract
frame.params=params
}catch{
continue
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

⚠️ Potential issue |🟠 Major

🧩 Analysis chain

Parse function return value is ignored.

The parse function is typed to returnany (line 546), but the return value is discarded on line 914. This creates ambiguity about whether parse should validate (throw on error) or transform (return modified params). The current implementation only supports validation.

Consider either:

  • Documenting that parse is validation-only and updating the return type tovoid
  • Or storing and using the return value to support param transformation

The TODO on line 913 hints at storing results, which would enable transformation. If transformation isn't needed, clarify this in the type signature.


🌐 Web query:

Does TanStack Router support parameter transformation or only validation in parse functions?

💡 Result:

Short answer: parse functions can transform (coerce/convert) parameters — not just validate them. The router calls your params.parse with the raw string values and uses whatever typed/transformed values you return (stringify is the reverse). That said, there are a few reported type-inference/coercion issues in some versions/adapters (see the Zod issue). [1][2]

Sources:

  • TanStack Router docs — custom parameter parsing (params.parse / params.stringify) and search‑param parsing/validation. [1]
  • GitHub issue reporting type/coercion/inference problems with validators/adapters (example with Zod). [2]

Parse function return value must be captured and used for parameter transformation.

The web search confirms that TanStack Router parse functions can transform (coerce/convert) parameters, and the router uses whatever typed/transformed values the parse function returns. However, the PR code on line 914 callsnode.parse(params) but discards the return value, preventing parameter transformation.

Fix required at line 908-920: Capture the parse return value and use it to update params:

try{;[params,extract]=extractParams(path,parts,frame)params=node.parse(params)??params// Capture and use transformed valueframe.extract=extractframe.params=params}catch{continue}

Without this fix, routes with parse functions cannot perform parameter type coercion/conversion.

🤖 Prompt for AI Agents
In packages/router-core/src/new-process-route-tree.ts around lines 908 to 920,the parse function's return value is ignored so transformed/coerced params arenot applied; update the try block to capture node.parse's return and assign itback to params (using a nullish fallback to the original params if parse returnsundefined), then set frame.extract and frame.params to the updated values; keepthe existing error handling (catch/continue) intact.

@birkskyumbirkskyum marked this pull request as draftNovember 21, 2025 23:56
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@coderabbitaicoderabbitai[bot]coderabbitai[bot] left review comments

Assignees

No one assigned

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

2 participants

@Sheraff

[8]ページ先頭

©2009-2025 Movatter.jp