Path parameters and query
An address often carries data: the post number in /posts/5 or the page number in /posts?page=2. The first is called a path parameter, the second a query string. Hono reads them with c.req.param() and c.req.query().
A path parameter
A parameter is marked with a colon in the route address:
// src/index.ts
app.get('/posts/:id', (c) => {
const id = c.req.param('id')
return c.text(`Post number ${id}`)
})Open http://localhost:3000/posts/5 and you see "Post number 5". A parameter value is always a string, even when the address contains digits.
Finding a post by number
Combine the parameter with the post list from the previous lesson:
// src/index.ts
app.get('/posts/:id', (c) => {
const id = Number(c.req.param('id'))
const post = posts.find((p) => p.id === id)
if (!post) {
return c.json({ error: 'Post not found' }, 404)
}
return c.json(post)
})/posts/1 returns the first post, and /posts/99 returns an error object with code 404.
Several parameters
// src/index.ts
app.get('/posts/:postId/comments/:commentId', (c) => {
const { postId, commentId } = c.req.param()
return c.text(`Comment ${commentId} on post ${postId}`)
})Without an argument, c.req.param() returns an object with all parameters. TypeScript knows their names from the route address and suggests them in the editor.
Restricting a parameter
After the name you can put a regular expression in curly braces:
// src/index.ts
app.get('/posts/:id{[0-9]+}', (c) => c.text(`Post ${c.req.param('id')}`))/posts/12 matches, /posts/hello does not, and Hono returns 404.
The query string
Parameters after ? are not described in the route; you read them in the handler:
// src/index.ts
app.get('/search', (c) => {
const q = c.req.query('q') ?? ''
const page = Number(c.req.query('page') ?? 1)
return c.json({ q, page })
})The address /search?q=hono&page=2 returns {"q":"hono","page":2}. When a parameter is missing, c.req.query() returns undefined, so the default value is set with ??.
| Method | Result for ?tag=js&tag=ts |
|---|---|
c.req.query('tag') | 'js' — the first value |
c.req.queries('tag') | ['js', 'ts'] — all values |
c.req.query() | an object with all parameters |
Common mistake
Comparing a parameter with a number without converting it:
// src/index.ts
const post = posts.find((p) => p.id === c.req.param('id')) // always undefined'1' === 1 is false, so the post is never found. TypeScript underlines such a comparison, but in a JavaScript project the bug goes unnoticed. Convert the value with Number() or check it with a zod schema, as shown in the validation lesson.