Named parameters: [param]
Wrap a folder name in square brackets to make it a dynamic segment. The captured value is available on ctx.params under the same name as the folder.
server/routes/users/[id]/get.ts
ctx.params:
server/routes/orgs/[orgId]/repos/[repoId]/get.ts
Catch-all segments: [...param]
Prefix with ... to capture the rest of the path as a single string. Everything after the catch-all folder’s parent is joined with / and stored in ctx.params under the param name.
server/routes/files/[...path]/get.ts
Router precedence
When multiple route patterns could match the same URL, Anvil resolves the conflict deterministically — insertion order never matters:- Static segments win over dynamic ones (
/users/mebeats/users/[id]for the path/users/me) - Named parameters win over catch-alls
- Backtracking ensures
/a/[b]/cstill matches when/a/xexists as a static sibling
Validating params with paramsSchema
Export a paramsSchema Zod object from any route file to enable compile-time validation and MCP tool generation. anvil lint checks that every key in the schema corresponds to a real folder param in the path.
server/routes/users/[id]/get.ts
paramsSchema is a compile-time and MCP-exposure hint. At runtime, ctx.params values are always plain strings — they are not coerced or parsed by the schema. Use the schema keys as a contract that anvil lint enforces.Naming rules
Anvil enforces several rules to prevent subtle bugs and cross-platform inconsistencies:No duplicate param names
The same param name cannot appear twice in one route path.
/[id]/items/[id]/get.ts throws a RouteConflictError at scan time.No conflicting names at the same position
Two routes cannot use different param names at the same depth.
/users/[id]/get.ts and /users/[slug]/post.ts conflict — use the same name ([id]) across all files in that folder.Case-insensitive collision detection
anvil lint rejects Users/ and users/ in the same parent because Windows treats them as the same directory while Linux does not.Valid JavaScript identifiers only
Param names must match
/^[A-Za-z_$][A-Za-z0-9_$]*$/. [123] and [my-param] are rejected at scan time.