Honoのv3.5が出てます
Honoのv3.5.xが出てます。というかもうすぐv3.6.0が出るのですが、その話も含めて、前回「Honoのv3.2が出ました」という記事を書いてからのアップデートです。
Honoのステータス
現在のHonoのステータス。GitHubのスターは6.1Kとなっております。
新しい機能
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などで見ることができます。
@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が動くのです!すごくないですか?
- aws-lambda
- bun
- cloudflare-pages
- cloudflare-workers
- deno
- fastly
- lagon
- lambda-edge
- netlify
- nextjs
- nodejs
- vercel
Cookie Helper supports Signed Cookies
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はそれを叶えるためのひとつの実装となります。
以下の図を見ると特徴がわかるでしょう。
Deprecated Features
わりとたくさん機能が、非推奨になり、次のメジャーバージョンアップ「v4」で廃止されます。もうすぐ出る「v3.6.0」で非推奨になるものを含めると以下の通りです。
- Validatorの中の
queries
、query
を使って同じことができます。 -
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のワークショップをやります。
興味のある人はぜひ来てください!
Discussion