Route groups and separate files
As routes multiply, src/index.ts turns into a long list. In Hono a group of routes is written as a separate small app, new Hono(), in its own file, and the main app mounts it with app.route() under a common address prefix.
A file with post routes
Create a src/routes folder with a posts.ts file in it:
// src/routes/posts.ts
import { Hono } from 'hono'
const posts = new Hono()
posts.get('/', (c) => c.text('List of posts'))
posts.get('/:id', (c) => c.text(`Post ${c.req.param('id')}`))
posts.post('/', (c) => c.text('Post created', 201))
export default postsAddresses here are relative to the future prefix: / is the list itself and /:id is one post.
Mounting
In src/index.ts import the group and mount it:
// src/index.ts
import { serve } from '@hono/node-server'
import { Hono } from 'hono'
import posts from './routes/posts.js'
const app = new Hono()
app.route('/posts', posts)
serve({ fetch: app.fetch, port: 3000 })Now GET /posts returns "List of posts" and GET /posts/7 returns "Post 7". The .js extension in the import is required by the NodeNext module setting, even though the file is named posts.ts.
Nested resources
Comments belong to a post, so their address starts with the post number. Create src/routes/comments.ts:
// src/routes/comments.ts
import { Hono } from 'hono'
const comments = new Hono()
comments.get('/:postId/comments', (c) => {
return c.text(`Comments on post ${c.req.param('postId')}`)
})
export default commentsAnd mount it under the same prefix:
// src/index.ts
import comments from './routes/comments.js'
app.route('/posts', comments)The address /posts/3/comments returns "Comments on post 3". Several groups can share one prefix.
A common API prefix
If every API address should start with /api, set it once with basePath():
// src/index.ts
const app = new Hono().basePath('/api')
app.route('/posts', posts)
app.route('/posts', comments)Now the post list lives at /api/posts and the comments at /api/posts/3/comments.
Project structure
src/
├── index.ts the app, mounting groups, starting the server
└── routes/
├── posts.ts
└── comments.tsFrom here on, post routes are written in src/routes/posts.ts.
Common mistake
Repeating the prefix inside the group file:
// src/routes/posts.ts
posts.get('/posts/:id', (c) => c.text('Post'))After app.route('/posts', posts) this route ends up at /posts/posts/5, and /posts/5 returns 404. Inside a group, write addresses without the prefix.