npm hono 4.3.0
v4.3.0

latest releases: 4.6.10, 4.6.9, 4.6.8...
6 months ago

Hono v4.3.0 is now available! Let's take a look at the new features.

Improve the RPC-mode

Thanks to @kosei28, @nakasyou, and @NamesMT, the RPC mode has been improved!

c.text() is typed

The response of c.text() was just a Response object, not typed.

const routes = app.get('/about/me', (c) => {
  return c.text('Me!') // the response is not typed
})

With this release, it will be a TypedResponse and you can get the type within the client created by hc.

const client = hc<typeof routes>('http://localhost:8787')

const res = await client.about.me.$get()
const text = await res.text() // text is typed as "Me!"
const json = await res.json() // json is never!

Support all JSON primitives

We added the tests for the responses of c.json() to have the correct types and support inferring all primitives. The all tests below are passed!

const app = new Hono()
const route = app
  .get('/api/string', (c) => c.json('a-string'))
  .get('/api/number', (c) => c.json(37))
  .get('/api/boolean', (c) => c.json(true))
  .get('/api/generic', (c) => c.json(Math.random() > 0.5 ? Boolean(Math.random()) : Math.random()))
type AppType = typeof route

const client = hc<AppType>('http://localhost')

const stringFetch = await client.api.string.$get()
const stringRes = await stringFetch.json()
const numberFetch = await client.api.number.$get()
const numberRes = await numberFetch.json()
const booleanFetch = await client.api.boolean.$get()
const booleanRes = await booleanFetch.json()
const genericFetch = await client.api.generic.$get()
const genericRes = await genericFetch.json()

type stringVerify = Expect<Equal<'a-string', typeof stringRes>>
expect(stringRes).toBe('a-string')
type numberVerify = Expect<Equal<37, typeof numberRes>>
expect(numberRes).toBe(37)
type booleanVerify = Expect<Equal<true, typeof booleanRes>>
expect(booleanRes).toBe(true)
type genericVerify = Expect<Equal<number | boolean, typeof genericRes>>
expect(typeof genericRes === 'number' || typeof genericRes === 'boolean').toBe(true)

// using .text() on json endpoint should return string
type textTest = Expect<Equal<Promise<string>, ReturnType<typeof genericFetch.text>>>

Status code type

If you explicitly specify the status code, such as 200 or 404, in c.json(). It will be added as a type for passing to the client.

// server.ts
const app = new Hono().get(
  '/posts',
  zValidator(
    'query',
    z.object({
      id: z.string()
    })
  ),
  async (c) => {
    const { id } = c.req.valid('query')
    const post: Post | undefined = await getPost(id)

    if (post === undefined) {
      return c.json({ error: 'not found' }, 404) // Specify 404
    }

    return c.json({ post }, 200) // Specify 200
  }
)

export type AppType = typeof app

You can get the data by the status code.

// client.ts
const client = hc<AppType>('http://localhost:8787/')

const res = await client.posts.$get({
  query: {
    id: '123'
  }
})

if (res.status === 404) {
  const data: { error: string } = await res.json()
  console.log(data.error)
}

if (res.ok) {
  const data: { post: Post } = await res.json()
  console.log(data.post)
}

// { post: Post } | { error: string }
type ResponseType = InferResponseType<typeof client.posts.$get>

// { post: Post }
type ResponseType200 = InferResponseType<typeof client.posts.$get, 200>

Improve compatibility with React

The compatibility of hono/jsx/dom has been improved. Now, these React libraries work with hono/jsx/dom!

The below demo is working with hono/jsx/dom, not React.

Google Chrome

If you want to use React libraries with hono/jsx/dom, set-up tsconfig.json and vite.config.ts like the followings:

tsconfig.json:

{
  "compilerOptions": {
    "paths": {
      "react": ["./node_modules/hono/dist/jsx/dom"],
      "react-dom": ["./node_modules/hono/dist/jsx/dom"]
    }
  }
}

vite.config.ts:

import { defineConfig } from 'vite'

export default defineConfig({
  resolve: {
    alias: {
      react: 'hono/jsx/dom',
      'react-dom': 'hono/jsx/dom'
    }
  }
})

Thanks @usualoma!

createApp() in Factory Helper

createApp() method is added to Factory Helper. If you use this method with createFactory(), you can avoid redundancy in the definition of the Env type.

If your application is like this, you have to set the Env in two places:

import { createMiddleware } from 'hono/factory'

type Env = {
  Variables: {
    myVar: string
  }
}

// 1. Set the `Env` to `new Hono()`
const app = new Hono<Env>()

// 2. Set the `Env` to `createMiddleware()`
const mw = createMiddleware<Env>(async (c, next) => {
  await next()
})

app.use(mw)

By using createFactory() and createApp(), you can set the Env only in one place.

import { createFactory } from 'hono/factory'

// ...

// Set the `Env` to `createFactory()`
const factory = createFactory<Env>()

const app = factory.createApp()

// factory also has `createMiddleware()`
const mw = factory.createMiddleware(async (c, next) => {
  await next()
})

Deprecate serveStatic for Cloudflare Workers

serveStatic exported by hono/cloudflare-workers has been deprecated. If you create an application which serves static asset files, use Cloudflare Pages instead.

Other features

  • Cookie Helper - delete cookie returns the deleted value #2512
  • Bearer Authenticate - add headerName option #2514
  • JSX/DOM - preserve the state of element even if it is repeatedly evaluated by children #2563
  • Mimes utility - expose built-in MIME types #2516
  • Serve Static - expose serve-static builder #2515
  • Secure Headers - enable to set nonce in CSP #2577
  • Server-Timing - allow crossOrigin in TimingOptions to be a function #2359
  • Client - add init option #2592

All Updates

New Contributors

Full Changelog: v4.2.9...v4.3.0

Don't miss a new hono release

NewReleases is sending notifications on new releases.