Typing nested controlled

Hi, how can I get this param typed?
import { Hono } from "hono"

// reviews controller
export const reviewsController = new Hono()
.get("/", (c) => c.json({ message: "Hello, world!" }))
.get("/:reviewId", (c) => {
const bookId = c.req.param("bookId") // This is not typed. :(
const reviewId = c.req.param("reviewId")
return c.json({ bookId, reviewId })
})

// books controller
export const booksController = new Hono()
.get("/", (c) => c.json({ message: "Hello, world!" }))
.get("/:bookId", (c) => c.json({ message: c.req.param("bookId") }))
.post("/", (c) => c.json({ message: "Hello, world!" }))
.put("/:bookId", (c) => c.json({ message: c.req.param("bookId") }))
.delete("/:bookId", (c) => c.json({ message: c.req.param("bookId") }))
.route("/:bookId/reviews", reviewsController)

// main controller
export const controller = new Hono().route("/api/books", booksController)
import { Hono } from "hono"

// reviews controller
export const reviewsController = new Hono()
.get("/", (c) => c.json({ message: "Hello, world!" }))
.get("/:reviewId", (c) => {
const bookId = c.req.param("bookId") // This is not typed. :(
const reviewId = c.req.param("reviewId")
return c.json({ bookId, reviewId })
})

// books controller
export const booksController = new Hono()
.get("/", (c) => c.json({ message: "Hello, world!" }))
.get("/:bookId", (c) => c.json({ message: c.req.param("bookId") }))
.post("/", (c) => c.json({ message: "Hello, world!" }))
.put("/:bookId", (c) => c.json({ message: c.req.param("bookId") }))
.delete("/:bookId", (c) => c.json({ message: c.req.param("bookId") }))
.route("/:bookId/reviews", reviewsController)

// main controller
export const controller = new Hono().route("/api/books", booksController)
I know this is not recommended https://hono.dev/docs/guides/best-practices but I have a 1000+ nested controlled that I need to refactor
Best Practices - Hono
Web framework built on Web Standards for Cloudflare Workers, Fastly Compute, Deno, Bun, Vercel, Node.js, and others. Fast, but not only fast.
3 Replies
Gary, el Pingüino Artefacto
I got away doing this but idk if it is correct
import { Hono } from "hono"

// reviews controller
export const reviewsController = new Hono<object, {}, "/api/books/:bookId/reviews">()
.get("/", (c) => c.json({ message: "Hello, world!" }))
.get("/:reviewId", (c) => {
const bookId = c.req.param("bookId")
const reviewId = c.req.param("reviewId")
return c.json({ bookId, reviewId })
})

// books controller
export const booksController = new Hono()
.get("/", (c) => c.json({ message: "Hello, world!" }))
.get("/:bookId", (c) => c.json({ message: c.req.param("bookId") }))
.post("/", (c) => c.json({ message: "Hello, world!" }))
.put("/:bookId", (c) => c.json({ message: c.req.param("bookId") }))
.delete("/:bookId", (c) => c.json({ message: c.req.param("bookId") }))
.route("/:bookId/reviews", reviewsController)

// main controller
export const controller = new Hono().route("/api/books", booksController)
import { Hono } from "hono"

// reviews controller
export const reviewsController = new Hono<object, {}, "/api/books/:bookId/reviews">()
.get("/", (c) => c.json({ message: "Hello, world!" }))
.get("/:reviewId", (c) => {
const bookId = c.req.param("bookId")
const reviewId = c.req.param("reviewId")
return c.json({ bookId, reviewId })
})

// books controller
export const booksController = new Hono()
.get("/", (c) => c.json({ message: "Hello, world!" }))
.get("/:bookId", (c) => c.json({ message: c.req.param("bookId") }))
.post("/", (c) => c.json({ message: "Hello, world!" }))
.put("/:bookId", (c) => c.json({ message: c.req.param("bookId") }))
.delete("/:bookId", (c) => c.json({ message: c.req.param("bookId") }))
.route("/:bookId/reviews", reviewsController)

// main controller
export const controller = new Hono().route("/api/books", booksController)
ambergristle
ambergristle6d ago
hey! pulling out routes (what you're calling a controller) is totally standard when the hono docs are talking about controllers, they're referring to the logic of your route handlers your typing solution is fine, though perhaps a little fragile. to improve the typing a smidge, you could
import { Hono, type Env } from "hono"
const reviews = new Hono<Env, {}, '...'>
import { Hono, type Env } from "hono"
const reviews = new Hono<Env, {}, '...'>
but i'd suggest adjusting the way you're breaking out routes, for better DX
const booksById = new Hono()
.basePath('/:bookId')
// all following handlers are aware of the bookId path param
.get('/', /** */)
.put('/', /** */)
.delete('/', /** */)
.get('/reviews', /** */)

const books = new Hono()
.get('/', /** */)
.post('/', /** */)
.route('/*', booksById)
const booksById = new Hono()
.basePath('/:bookId')
// all following handlers are aware of the bookId path param
.get('/', /** */)
.put('/', /** */)
.delete('/', /** */)
.get('/reviews', /** */)

const books = new Hono()
.get('/', /** */)
.post('/', /** */)
.route('/*', booksById)
Gary, el Pingüino Artefacto
Got it, thanks 😄

Did you find this page helpful?