typescriptrpchono

Hono RPC offers no type assistance when using abstracted routes


I'm not getting any type hints from my Hono RPC client. I know there are warnings not to use the factory method, but as far as I knew this was the only way to have abstracted routes. I'm open to any way that allows me to have routes in separate files.

type Env = {
  Variables: {
    db: typeof db
    mqtt: typeof mqtt
    redis: typeof redis
  }
}

function initApp(app: Hono<Env, BlankSchema, "/">) {
  app.use(async (ctx, next) => {
    ctx.set("db", db)
    ctx.set("mqtt", mqtt)
    ctx.set("redis", redis)
    await next()
  })
}

const factory = createFactory<Env>({ initApp })
export default factory

// ———————————————————————————————————————————
// Route Index

const app = factory.createApp()
  .route("/", root)
  .route("/config", config)
  .route("/redis", redis)

export type App = typeof app

// ———————————————————————————————————————————
// Client

const client = hc<App>(`http://${HOSTNAME}:${PORT}`)
export default client

Solution

  • The code you've shared looks fine. As far as I know, it's ok to use the createApp method. The warnings were removed after a few weeks, so I'd consider it stable: https://hono.dev/docs/helpers/factory#factory-createapp

    For reference, you don't need to use createApp to abstract your routes, but it is recommended since it couples typing and implementation more closely, and avoids the repetition of passing AppType around.

    I did run into an edge case, where typeof app is unknown if no methods are defined for the handlers, but that shouldn't affect a real app:

    const root = factory.createApp();
    const config = factory.createApp();
    const redis = factory.createApp();
    

    I've modified the example you shared to remove any imports/dependencies. The intellisense/typing works fine. I'm using hono@4.7.4, but I'd expect it to work with any recent version. You should be able to copy+paste it into an empty file in any Hono project. It will also work if you pull everything into separate files, but I consolidated for convenience:

    import { Hono } from "hono"
    import { hc } from "hono/client"
    import { createFactory } from "hono/factory"
    
    type Env = {
      Variables: {
        db: { db: string }
        mqtt: { mqtt: string }
        redis: { redis: string }
      }
    }
    
    const factory = createFactory<Env>({ 
      initApp: (app) => {
        app.use(async (ctx, next) => {
          // ctx.set("db", db)
          // ctx.set("mqtt", mqtt)
          // ctx.set("redis", redis)
          await next()
        })
      }
     })
    
    // can use factory.createApp for the typing
    const root = factory.createApp()
      .get('/', async (c) => c.text('root'))
    // or vanilla hono if you don't need it
    const config = new Hono()
      .patch('/', async (c) => c.text('config'))
    const redis = factory.createApp()
      .post('/', async (c) => c.text('redis'))
    
    
    const app = factory.createApp()
     .route('/', root)
     .route('/config', config)
     .route('/redis', redis)
    
    
    type App = typeof app
    
    const client = hc<App>(`http://<HOSTNAME>:<PORT>`)
    
    const getRoot = client.index.$get
    const patchConifg = client.config.$patch
    const postRedis = client.redis.$post
    
    

    Without additional context about your issue, I'd suspect the problem relates to the way your routes are initialized and/or typed.

    If you'd like help troubleshooting, I'd recommend the Hono Discord. I've found the community to be really welcoming and supportive: https://discord.gg/KMh2eNSdxV