Mark parameters as optional by adding a ? after the segment:
app.get('/posts/:id/comments?', (c) => { return c.text('Post with optional comments path')})// GET /posts/123 -> Matches// GET /posts/123/comments -> Matches
Optional parameters are handled by the router during route registration, creating multiple route entries.
The checkOptionalParameter utility in src/utils/url.ts handles this:
// From src/router/linear-router/router.ts:16for ( let i = 0, paths = checkOptionalParameter(path) || [path], len = paths.length; i < len; i++) { this.#routes.push([method, paths[i], handler])}
Use regular expressions to constrain parameter values:
// Only match numeric IDsapp.get('/users/:id{[0-9]+}', (c) => { const id = c.req.param('id') return c.text(`User ID: ${id}`)})// GET /users/123 -> Matches// GET /users/abc -> Does not match// Date formatapp.get('/date/:date{[0-9]{4}-[0-9]{2}-[0-9]{2}}', (c) => { const date = c.req.param('date') return c.text(`Date: ${date}`)})// GET /date/2024-01-15 -> Matches// GET /date/invalid -> Does not match
From src/router/pattern-router/router.ts:24, patterns are converted to regex:
const match = part.match(/^\/:([^{]+)(?:{(.*)})?/)return match ? `/(?<${match[1]}>${match[2] || '[^/]+'})` : part === '/*' ? '/[^/]+' : part.replace(/[.\\+*[^\]$()]/g, '\\$&')
// Match any subpathapp.get('/api/*', (c) => { return c.text('API route')})// GET /api/users -> Matches// GET /api/users/123 -> Matches// GET /api/v1/users/123 -> Matches
With TypeScript, Hono provides type-safe parameter access:
const app = new Hono()const route = app.get('/users/:id', (c) => { const id = c.req.param('id') // Type: string return c.json({ id })})// The route type includes parameter information