CORE CONCEPTS
Route Patterns
Futon route patterns support five segment types. Every matched parameter value is automatically URL-decoded.
Required Parameters
router.get('/users/:id', async (ctx) => { const { id } = ctx.params; // string return json({ id }); });
A request to /users (no id) does not match this pattern — it falls through to your 404 handler.
Optional Parameters
router.get('/users/:id?', async (ctx) => { const { id } = ctx.params; // string | undefined if (!id) return json(await listUsers()); return json(await getUser(id)); });
Both /users and /users/42 match. ctx.params.id is undefined for the former.
Named Wildcards
router.get('/files/:path*', async (ctx) => { const { path } = ctx.params; // e.g. "a/b/c" return json({ path }); });
:path* captures every remaining segment into a single string, joined by /.
Bare Catch-all
router.get('/static/*', async (ctx) => { // The captured remainder is available under the "_" param key const remainder = ctx.params['_']; return json({ remainder }); });
Use the named wildcard (:path*) when you want a descriptive param name; use the bare * for a quick catch-all where the value doesn't need a name.
Matching Rules
- Trailing slashes are normalised —
/users/and/usersmatch the same route (except the root/, which is preserved as-is). - Route matching happens against the pathname only; query strings are parsed separately via
ctx.query(see Context). - Patterns are matched in the order the tree resolves them: static segments win over
:param, which wins over:param?/:param*/*— so/users/meand/users/:idcan coexist safely, with/users/metaking priority.
Router
Route Groups
