Honoのv3が出ました
僕がCreatorのHonoの新しいメジャーバージョンである「v3.0.0」が出ました。
このリリースノートに全て書いたのですが、補足を含めてこちらにも残しておきます。
Honoのステータス
v3の説明の前に現在のHonoのステータスです。
GitHubスターは3.5Kです。
Cloudflare WorkersのSDK、Deno、Bun、それぞれのプロジェクトにHonoの文字が入ってます。
プロダクションやライブラリでも使われています。
- cdnjsのAPIサーバー
- Polyfill.io
- repeat.dev
- Drivly
- substats
- Ultra(DenoのReact SSRフレームワーク)
- Cloudflare 公式のブログ記事
- などなど
いい感じです。
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%」速くなりました。
RegExpRouterが最速のルーターになりました。
RegExpRouterがJavaScript界最速のルーターになりました 🎉
どう測ったかというと、Fastifyの中で使われている"find-my-way"というルーターはとても速いのですが、そこが自前でベンチマークスクリプトを公開しています。
Expressからradix-treeを使ったガチ速いのもあります。これに「@medley/router」というこれもまた速いルーターを加えます。つまりNodeの強いのを全て集めました。それにHonoのTrieRouerとRegExpRouterを加えて上記と同じスクリプトをmitataというベンチマークライブラリでNode.jsとBunの両方で回しました。mitataはクロスプラットフォームなので、Bunの界隈ではよく使われています。
いくつか項目があるのですが、多くの場合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
した型AppType
をimport
してhc()
関数にジェネリクスとして渡します。
import { hc } from 'hono/client'
import type { AppType } from './index'
const client = hc<AppType>('/api')
これで準備はできました。ご覧ください。
感謝
このRPCの機能は@cleatonさんの以下のコメントがベースになっています。
ウルトラクールなアイデアをありがとう。
Adapter
HonoはCloudflare PagesのFunctionsやNext.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で回るようになり、それぞれの環境での動作が保証されるようになりました。
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
これで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。
Discussion