🔥

Honoのv3.5が出てます

2023/09/06に公開

Honoのv3.5.xが出てます。というかもうすぐv3.6.0が出るのですが、その話も含めて、前回「Honoのv3.2が出ました」という記事を書いてからのアップデートです。

Honoのステータス

現在のHonoのステータス。GitHubのスターは6.1Kとなっております。

Stars

新しい機能

v3.3以降、現在v3.5.xまでで入った新しい機能です。

  • Server-Timing Middleware
  • Lambda@Edge Adapter
  • Netlify Adapter
  • Cookie Middleware supports Signed Cookies
  • Secure Headers Middleware
  • Introducing "Helpers"
  • Zod OpenAPI
  • Deprecated Features

ひとつづつ見ていきましょう。

Server-Timing Middleware

パフォーマンスの計測は重要です。v3.3.0でServer-Timing Middlewareが導入されました。これはハンドラの中の処理のパフォーマンスをServer-Timing APIを使って計測できるというものです。

import { Hono } from 'hono'
import { endTime, setMetric, startTime, timing } from 'hono/timing'

const app = new Hono()

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

app.get('/', async (c) => {
  // add custom metrics
  setMetric(c, 'region', 'europe-west3')

  // add custom metrics with timing, must be in milliseconds
  setMetric(c, 'custom', 23.8, 'My custom Metric')

  // start a new timer
  startTime(c, 'db')
  const data = ['foo'] // DB process
  endTime(c, 'db')

  return c.json({ response: data })
})

export default app

結果はChromeのDevToolsなどで見ることができます。

DevTools

@PassiDelさんが作ってくれました。

Lambda@Edge Adapter

HonoではすでにAWS Lambdaのアダプタがありますが、「Lambda@Edge」のサポートはまだでした。v3.3.0ではLambda@Edgeのアダプタが追加されました。使うのは簡単です。

import { Hono } from 'hono'
import { handle } from 'hono/lambda-edge'

const app = new Hono()

app.get('/', (c) => c.text('Hello Hono!'))

export const handler = handle(app)

もし、あなたがBasic認証をして、認証後に処理を続けたければ、c.env.callback()を使えばOKです。

import { Callback, CloudFrontRequest, handle } from 'hono/lambda-edge'

type Bindings = {
  callback: Callback
  request: CloudFrontRequest
}

const app = new Hono<{ Bindings: Bindings }>()

app.get(
  '*',
  basicAuth({
    username: 'a',
    password: 'b'
  })
)

app.get('/index.html', async (c, next) => {
  await next()
  c.env.callback(null, c.env.request)
})

export const handler = handle(app)

@watany-devさんの貢献のおかげです。

Netlify Adapter

v3.4.0ではNetlify Adapterが入りました。HonoのアプリケーションをNetlify Edge Functionsで動かすことができます。

import { Hono } from 'https://deno.land/x/hono@v3.4.1/mod.ts'
import { prettyJSON } from 'https://deno.land/x/hono@v3.4.1/middleware.ts'
import { handle, type Env } from 'https://deno.land/x/hono@v3.4.1/adapter/netlify/mod.ts'

const app = new Hono<Env>()

app.get('/', prettyJSON(), (c) =>
  c.json({
    'You are in': c.env.context.geo.country?.name
  })
)

export default handle(app)

これは、create-honoコマンドですぐに試せます。

npm create hono@latest my-app

これにより、Honoのスターターには12個のテンプレートが登録されることになりました。これら多くのプラットフォームでHonoが動くのです!すごくないですか?

  1. aws-lambda
  2. bun
  3. cloudflare-pages
  4. cloudflare-workers
  5. deno
  6. fastly
  7. lagon
  8. lambda-edge
  9. netlify
  10. nextjs
  11. nodejs
  12. vercel

Cookie HelperがSigned Cookieに対応しました。getSignedCookie()setSignedCookie()ヘルパー関数が使えます。

app.get('/signed-cookie', async (c) => {
  const secret = 'secret ingredient'
  const allSignedCookies = await getSignedCookie(c, secret)
  const fortuneCookie = await getSignedCookie(c, secret, 'fortune_cookie')
  // ...
  const anotherSecret = 'secret chocolate chips'
  await setSignedCookie(c, 'great_cookie', 'blueberry', anotherSecret)
  deleteCookie(c, 'great_cookie')
  //
})

@torteさんが作ってくれました。

Secure Headers Middleware

v3.5.0ではSecure Headers Middlewareが追加されました。これはExpressにおけるHelmet相当のもので、HTTPレスポンスヘッダをセキュアにする設定ができます。以前から要望の多かったものです。

import { Hono } from 'hono'
import { secureHeaders } from 'hono/secure-headers'

const app = new Hono()

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

app.get('/', (c) => c.text('Hello!'))

// ...

export default app

@watany-devさんが作ってくれました。グレートワーク!

Introducing "Helpers"

Helpersという概念を入れます。これは従来までのMiddlewareと似たものですが、ハンドラではなく、便利な関数を提供します。例えば前述したCookie HelperはもともとCookie Middlewareという名前だったのですが、純粋なハンドラではありません。そこでCookie Helperに移行しました。

import { getCookie, setCookie } from 'hono/cookie'

const app = new Hono()

app.get('/cookie', (c) => {
  const yummyCookie = getCookie(c, 'yummy_cookie')
  // ...
  setCookie(c, 'delicious_cookie', 'macha')
  // ...
})

といっても、破壊的変更ではありません!内部での扱い方、概念が変わっただけで、hono/cookieからimportするという使い方は変わっていません。

Zod OpenAPI

Zod OpenAPIができました。これはOpenAPIをサポートするHonoのラッパー、厳密にはHonoを拡張したclassです。Zodを用いて値と型のバリデーションをして、かつ、それを元にOpenAPIのドキュメントを出力してくれます。Zod to OpenAPIというライブラリをベースに作られています。OpenAPI対応については、以前から強い要望がありました。Zod OpenAPIはそれを叶えるためのひとつの実装となります。

以下の図を見ると特徴がわかるでしょう。

Zod OpenAPI

Deprecated Features

わりとたくさん機能が、非推奨になり、次のメジャーバージョンアップ「v4」で廃止されます。もうすぐ出る「v3.6.0」で非推奨になるものを含めると以下の通りです。

  • Validatorの中のqueriesqueryを使って同じことができます。
  • c.runtime()、Adapterというヘルパーを使ってください。
  • app.handleEvent()app.fetch()で同じことができます。
  • HonoRequest内のheaders(c.req.headerとは違う)など。c.req.rawのRequestオブジェクトからアクセスしてください。

v3.6.0

以上が、今出てるv3.5.xまでの機能ですが、もう少しで「v3.6.0」が出ます。c.render()というメソッドが追加されるのですが、実は「v4」への布石というべき機能です。

例えば、HTMLコンテンツの場合、c.setRenderer()を使って共通で使うテンプレートを設定することができます。

app.use('*', async (c, next) => {
  c.setRenderer((content) => {
    return c.html(
      <html>
        <body>
          <p>{content}</p>
        </body>
      </html>
    )
  })
  await next()
})

というミドルウェアがあると、ハンドラでc.render()を使います。

app.get('/', (c) => {
  return c.render('Hello!')
})

すると、以下のHTMLが出力されます。

<html><body><p>Hello!</p></body></html>

setRenderer()は型定義も含めて拡張することができます。

declare module 'hono' {
  interface ContextRenderer {
    (content: string, head: { title: string }): Response
  }
}

こうすると、ヘッダ内のtitleタグをハンドラごとに書き換えるってことができるようになります。

app.use('/pages/*', async (c, next) => {
  c.setRenderer((content, head) => {
    return c.html(
      <html>
        <head>
          <title>{head.title}</title>
        </head>
        <body>
          <header>{head.title}</header>
          <p>{content}</p>
        </body>
      </html>
    )
  })
  await next()
})

app.get('/pages/my-favorite', (c) => {
  return c.render(<p>Ramen and Sushi</p>, {
    title: 'My favorite',
  })
})

app.get('/pages/my-hobbies', (c) => {
  return c.render(<p>Watching baseball</p>, {
    title: 'My hobbies',
  })
})

「v4」の目玉機能は「File-based」ルーティングになる予定で、このc.render()が使われるというわけです。

まとめ

以上です!すべてのユーザー、コントリビュータ、スポンサーに感謝!

ワークショップ

9月24日(日)に「ServerlessDays Tokyo 2023」というイベントの一貫で、Cloudflare WorkersとHonoのワークショップをやります。

https://serverless.connpass.com/event/290640/

興味のある人はぜひ来てください!

Discussion