github honojs/hono v3.1.0

latest releases: v4.6.2, v4.6.1, v4.6.0...
18 months ago

Hono v3.1.0 has been released now. Introduce the new features.

Improve RPC-mode

This release includes some bug fixes for RPC-mode. In addition, there are several new features for it.

These make RPC-mode more usable.

Support type transformations

For example, Zod has a "transform" function.

z.string()
  .regex(/^[0-9]+$/)
  .transform(Number)

This means that the input type(string) and output type(number) are different. But, Hono did not support different input and output types. If we use "transform" in Zod Validator Middleware, the value from c.req.valid() will be "input type" not "output type":

const app = new Hono().post(
  '/foo',
  zValidator(
    'json',
    z.object({
      id: z
        .string()
        .regex(/^[0-9]+$/)
        .transform(Number),
    })
  ),
  (c) => {
    c.req.valid('json').id // expected to be `number`

SS

With this release, the validator treats input and output types as different, so it will support Zod's transformation.

SS

This feature is not only for the Zod Validator, but for general purposes.

Support param validation

Now, the validator supports validating "path params".

const schema = z.object({
  id: z
    .string()
    .regex(/^[0-9]+$/)
    .transform((v) => {
      return Number(v)
    }),
  title: z.string(),
})
app.get('/users/:id/books/:title', zodValidator('param', schema), (c) => {
  const { id, title } = c.req.valid('param')
  return c.jsonT({
    id, // number
    title, // string
  })
})

query supports array params

For example, before this release, it was difficult to validate multiple params like tag using query:

http://localhost/search?page=123&tag=a&tag=b

Or we have to use queries but if we do so, they all will be array.

From now, the values we could get from query will be Record<string, string | string[]>. So we can write the followings:

const app = new Hono()

const schema = z.object({
  q: z.string(),
  tag: z.array(z.string()),
})

const route = app.get('/post', zValidator('query', schema), (c) => {
  const { q, tag } = c.req.valid('query')
  return c.jsonT({
    queryString: q,
    tags: tag,
  })
})

env for getting environment variables for multi-runtimes

env function to get environment variables for any runtimes, not only variables of Cloudflare Workers' Bindings.

We can write the code with the same syntax at any runtime:

import { env } from 'hono/adapter'

app.get('/env', (c) => {
  const { NAME } = env<{ NAME: string }>(c)
  return c.text(NAME)
})

Supported runtimes:

  • Cloudflare Workers
  • Deno
  • Bun
  • Lagon
  • Node.js
  • Vercel
  • Fastly

AWS Lambda Adapter (Experimental)

With this Adapter, you can run applications using Hono on AWS Lambda, and you can also publish them with AWS API Gateway.

You can try it by using "hono-create":

npm create hono@latest my-app

Caution

This feature is experimental. The API might be changed.

Usage

The usage is almost the same as the Cloudflare Pages and Next.js adapters. Import handle, give the Hono application, and export it.

import { Hono } from 'hono'
import { handle } from 'hono/aws-lambda'
import { logger } from 'hono/logger'

const app = new Hono()

app.use('*', logger())

app.get('/hello', (c) => {
  return c.json({
    message: 'Hono meets Lambda',
  })
})

export const handler = handle(app)

This is all you need to do to get your Hono application running in Lambda.

Settings

Use AWS API Gateway to publish applications run on Lambda.

SS

In API Gateway, create a Route ANY /{proxy+}. This way, the Hono application can handle all methods and paths.

SS

Deploy

The simplest way is to use esbuild to transpile to JavaScript, compress it to Zip, and upload it using aws CLI. For example, package.json could look like this.

{
  "name": "lambda-hello",
  "type": "module",
  "scripts": {
    "build": "esbuild --bundle --outfile=./dist/index.js --platform=node --target=node18 ./src/index.ts",
    "zip": "zip -j lambda.zip dist/index.js",
    "update": "aws lambda update-function-code --zip-file fileb://lambda.zip --function-name hello",
    "deploy": "run-s build zip update"
  }
}

basePath()

app.route() with a single argument is renamed app.basePath().

const app = new Hono()
const route = app
  .basePath('/book')
  .get('/post', (c) => c.text('Book list'))
  .post('/post', (c) => c.text('Created!', 201))

c.req.path

This simple feature allows you to get the URL's path by accessing c.req.path.

app.get('/search', (c) => c.text(c.req.path)) // "search"

Allow passing RequestInit to c.json() etc.

It was very tedious to customize Basic Auth errors.

app.onError((e, c) => {
  if (e instanceof HTTPException) {
    const res = e.getResponse()
    res.headers.set('content-type', 'application/json')
    return new Response(
      JSON.stringify({
        message: 'Unauthorized!',
      }),
      res
    )
// ...

Now it become easier because we can pass ResponseInit, i.e. meta information such as headers, to c.json().

app.onError((e, c) => {
  if (e instanceof HTTPException) {
    return c.json(
      {
        message: Unauthorized!',
      },
      e.getResponse()
    )
  }

This feature is not only used in the above cases but can also be used when we want to modify and return the content retrieved from the outside using fetch.

app.get('/mirror', async (c) => {
  const res = await fetch('http://example.com')
  return c.json({ foo: 'bar' }, res)
})

All changes

  • feat(context): allow passing unknown to executionCtx.waitUntil by @yusukebe in #957
  • fix(type): remove unnecessary distribution by @yusukebe in #951
  • feat: env support enviroment variables for multi runtimes by @yusukebe in #949
  • feat: route() with one argument is renamed basePath(). by @usualoma in #964
  • fix(type): add forgotten BasePath by @yusukebe in #967
  • feat(context): allow passing RequestInit to c.json() etc. by @yusukebe in #959
  • feat(validator): support type transformation by @yusukebe in #969
  • feat(middleware): support overwriting response after next() by @yusukebe in #970
  • refactor: remove lint warnings by @yusukebe in #976
  • test(bun): use test instead of wiptest! by @yusukebe in #977
  • fix(types): make MergePath<'/api', '/'> work well by @yusukebe in #971
  • fix(client): removeIndexString supports /sub/index by @yusukebe in #978
  • feat(pages): enable getting eventContext by @yusukebe in #980
  • feat(page/nextjs): show "deprecated" for 2nd arg of handle() by @yusukebe in #981
  • fix(types): infer types correctly by @yusukebe in #983
  • feat(validator): Allow validator to apply validation to path param. by @usualoma in #982
  • feat(validator): query supports array params by @yusukebe in #984
  • feat(req): introduce c.req.path by @yusukebe in #988
  • feat(adapter): introduce AWS Lambda Adapter by @yusukebe in #987
  • Merge next into main by @yusukebe in #989

Full Changelog: v3.0.5...v3.1.0

Don't miss a new hono release

NewReleases is sending notifications on new releases.