🔥

Honoのv3が出ました

2023/02/21に公開

僕がCreatorのHonoの新しいメジャーバージョンである「v3.0.0」が出ました。

https://github.com/honojs/hono/releases/tag/v3.0.0

このリリースノートに全て書いたのですが、補足を含めてこちらにも残しておきます。

Honoのステータス

v3の説明の前に現在のHonoのステータスです。

GitHubスターは3.5Kです。

Star

Cloudflare WorkersのSDK、Deno、Bun、それぞれのプロジェクトにHonoの文字が入ってます。

SS

SS

SS

プロダクションやライブラリでも使われています。

いい感じです。

v3のスローガン

v3へのバージョンアップにあたってのスローガンはズバリこれでした。

Do Everything, Run Anywhere, But Small, And Faster.

では、各機能紹介をしていきます。

HonoRequest

これまでHonoではリクエストをRequestを拡張したもので扱ってきたのですが、それだと各ランタイムで微妙にAPIと実装、それに型が異なる場合、型の不一致などが起こり使いにくいことになってました。そこで新たにHonoRequestを作りました。Requestオブジェクトをラップしたものになっており、オリジナルのRequestにはreq.rawでアクセスできます。また、HonoRequestではHono特有のreq.param()req.query()の他にRequestの標準的なメソッドやプロパティをバイパスしているので今まで同じように使えます。

// c.req is HonoRequest
const id = c.req.param('id')
const query = c.req.query('q')
const data = await c.req.json()

// originalRequest is Request
const originalRequest = c.req.raw

パフォーマンス向上

クエリパラメータの処理やヘッダの処理のチューニングを施し、とあるベンチマークスクリプトでは「2%〜11%」速くなりました。

SS

RegExpRouterが最速のルーターになりました。

RegExpRouterがJavaScript界最速のルーターになりました 🎉

どう測ったかというと、Fastifyの中で使われている"find-my-way"というルーターはとても速いのですが、そこが自前でベンチマークスクリプトを公開しています。

https://github.com/delvedor/router-benchmark

Expressからradix-treeを使ったガチ速いのもあります。これに「@medley/router」というこれもまた速いルーターを加えます。つまりNodeの強いのを全て集めました。それにHonoのTrieRouerとRegExpRouterを加えて上記と同じスクリプトをmitataというベンチマークライブラリでNode.jsとBunの両方で回しました。mitataはクロスプラットフォームなので、Bunの界隈ではよく使われています。

SS

いくつか項目があるのですが、多くの場合RegExpRouterが速く、全てをあわせたものだとかなり差をつけて1番でした。特にBunは正規表現が速いのでBun上だと顕著です。

スクリプトはこちらです。

さようならStaticRouter

RegExpRouterが十分速くなったおかげで、StaticRouterが必要なくなりました。さようなら!

新しいValidator

過去のValidator Middlewareは廃止になりました。その代わり、新しいValidatorを使ってください。ただ、これはだいぶ薄いので、ZodなどのサードパーティーのValidatorと一緒に使うことを推奨します。

特にZodの場合は"Zod Validator Middleware"があるので、それを使うとこう書けます。

import { zValidator } from '@hono/zod-validator'
import { Hono } from 'hono'
import { z } from 'zod'

const app = new Hono()

app.post(
  '/posts',
  zValidator(
    'json',
    z.object({
      id: z.number(),
      title: z.string(),
    })
  ),
  (c) => {
    const { id, title } = c.req.valid('json')
    return c.text(`${id} is ${title}!`)
  }
)

RPCモード

さてここからはマジックの時間です。

我々はなんとHTTPクライントを作りました。hcと呼んでいます。これを使うとAPIのスペックをサーバーとクライントでTypeScriptの「型」を通して共有できます。tRPCのようですが、Honoに統合されてとても簡単に使えます。

RPCモードを有効にするには、これまでjson()を使っていたところをjsonT()に変えます。そして、appの変数routeの型をとりそれをexportします。変えるのはこれだけです。

// server.ts
const route = app.post( // 変数にする
  '/posts',
  zValidator(
    'json',
    z.object({
      id: z.number(),
      title: z.string(),
    })
  ),
  (c) => {
    const { id, title } = c.req.valid('json')
    // ...
    return c.jsonT( // ここ
      {
        success: true,
        message: 'created!',
      },
      201
    )
  }
)

export type AppType = typeof route // ここ

クライントサイドでは、上記でexportした型AppTypeimportしてhc()関数にジェネリクスとして渡します。

import { hc } from 'hono/client'
import type { AppType } from './index'

const client = hc<AppType>('/api')

これで準備はできました。ご覧ください。

SC

感謝

このRPCの機能は@cleatonさんの以下のコメントがベースになっています。

https://github.com/honojs/hono/issues/582#issuecomment-1279598527

ウルトラクールなアイデアをありがとう。

Adapter

HonoはCloudflare PagesのFunctionsNext.jsのEdge API Routesとも相性がいいです。で、よりそれらの上でHonoを簡単に使うためにAdapterができました。以下がNext.jsの例です。

// pages/api/[...route].ts
import { Hono } from 'hono'
import { handle } from 'hono/nextjs'

export const config = {
  runtime: 'edge',
}

const app = new Hono()

app.get('/hello', (c) => {
  return c.json({
    message: 'Hello from Hono!',
  })
})

export default handle(app, '/api')

このAdapterとRPCモードの組合わせはとてもパワフルです。RESTのAPIをPagesのFuntionsやNext.jsのAPI Routesで書き、そのスペックをシェアして、フロントではReactのアプリケーションをhcとともに型安全に書くってことができます。

AdapterはPagesやNext.jsだけではなく、Cloudflare Workers、Deno、Bun用にもできました。それに伴い、これまでMiddlewareとして提供していたServe StaticがAdapterから提供されるようになりました。

import { Hono } from 'hono'
import { serveStatic } from 'hono/cloudflare-workers'

const app = new Hono()

app.get('/static/*', serveStatic({ root: './' }))
app.get('/favicon.ico', serveStatic({ path: './favicon.ico' }))

これでランタイム固有の挙動をすべてAdapter以下に追いやることができてスッキリしました。

HTTPException

これまで、あえて例外を作ってこなかったのですが、認証時のエラーなどはthrowした方がいいので、HTTPExcpetionを作りました。これでより安全です。

複数のランタイムのCI

Honoは多くのJavaScriptランタイムで動きます。今回は以下5つのランタイムのテストがCIで回るようになり、それぞれの環境での動作が保証されるようになりました。

SS

WinterCG Runtime Keysのサポート

HonoはWinterCGの動向に気を使っていて、まさに「Web Standardのデファクト・スタンダード」を目指しています。そこでc.runtimeで取得できるラインタイムキーを WinterCG's Runtime Keysと同じにしました。

app.get('/', (c) => {
  if (c.runtime === 'workerd') {
    return c.text('You are on Cloduflare')
  } else if (c.runtime === 'bun') {
    return c.text('You are on Bun')
  }
  ...
})

有効なキーは以下です。ただ、Fastly Compute@EdgeはWinterCGにはあまり入ってきておらずWinterCGのキーにはないので「fastly」としました。

  • node
  • deno
  • bun
  • workerd - Cloudflare Workers
  • fastly
  • edge-light - Vercel Edge Functions
  • lagon
  • other

create-honoコマンド

"create-hono"というコマンドラインができました。これを使えば、30秒でHonoのアプリケーションが立ち上がります。

npm create hono@latest my-app

もしくはこちら。

yarn create hono my-app

SS

これでHonoまだの人は簡単に試せます!

@honojsから@hono

@honojsというnpmの名前空間が非推奨になりました。その代わりに@honoが使えます!実は、npmには@honoさんというアカウントが既に存在していたのですが、活動が活発じゃなかったので、npm本体に連絡してアカウント名をくれないか?と打診していたのでした。それが見事叶いました!

ですので、以下のミドルウェアは@hono以下にリネームされます。

  • @hono/graphql-server
  • @hono/sentry
  • @hono/firebase-auth

また、Node.jsのアダプタは@hono/node-serverになります。

新しいミドルウェア

それに加えて、以下の新しいミドルウェアが追加されました。

  • @hono/trpc-server - tRPC Server Middleware
  • @hono/qwik-city - Qwik City Middleware
  • @hono/zod-validator - Zod Validator Middleware

サードパーティーミドルウェアはコミュニティベースでこれから増えていく予定です。

新しいウェブサイト

最後に伝えたいことがあります。我々は新しいウェブサイトをめちゃくちゃかっこいいドメインと共に手に入れました。

hono.dev です!!

また、サイトをHugoからVitePressベースに変更したことによりJavaScriptとの親和性がよくなりより貢献しやすくなりました。


と、以上盛りだくさんのv3バージョンアップでした。ぜひ使ってみてください。特にRPCモードは楽しいと思います。そしてなにより使ったら感想をどこかへシェアしていただけると作者冥利につきます。よろしく。

YAPC::Kyoto 2023

そういえば、今日、YAPC:Kyoto 2023のタイムテーブルが公開されました。RegExpRouter作者の@usualomaさんと連チャンでふたりともHonoの話をするので、実質Hono Conferenceです。Haha。

SS

Discussion