Hono Clientを利用して、APIからの情報をJSXで表示する
この記事では、Honoで作成したAPIをHono Clientを使用して呼び出す方法が紹介されています。APIの準備からクライアントアプリの作成までが詳しく解説されており、Honoを使用する魅力や効率的な開発手法について触れられています。APIを実装し、クライアントを通じて呼び出すことで、フロントエンドとAPIを1つのプロジェクトで管理できる便利な方法が示唆されています。
目次
この記事では、Honoで作成したAPIをHonoで実装したフロントエンドアプリケーションから呼び出す方法として、Hono Clientを利用する方法を紹介します。「Hono Advent Calendar 2024」23日目の遅刻記事です。
やりたいこと
Honoの特徴として、作成したAPIをプロジェクト内で呼び出すためのクライアントSDKを作る仕組みがあります。これのことをHono Clientとよぶみたいで、基本的にAPIばかり作る自分としてはあまり使う機会がありませんでした。ただ、最近Webサイトも作ってみようという気持ちになってきたため、その前準備としてコードを書いて動きを調べてみようと思います。
APIを用意する
まずはHono Clientで利用するAPIを作りましょう。シンプルなGETのAPIを2つ用意しました。
const apiApp = new Hono()
.get('/hello', c => {
return c.json({
message: 'Hello hono'
})
})
.get('/bye', c => {
return c.json({
message: 'Good bye'
})
})
type ApiAppType = typeof apiApp
hono/client
で利用するため、APIパス・メソッドはメソッドチェーンで定義必要があるとのことです。この方法で実装すると、Honoにinput / outputなどの型情報も推論してもらえるようになります。
HonoでAPIを作っていると、次のようなメソッドチェーンではない形で作りたくなります。
const apiApp = new Hono()
apiApp.get('/hello', c => {
return c.json({
message: 'Hello hono'
})
})
apiApp.get('/bye', c => {
return c.json({
message: 'Good bye'
})
})
type ApiAppType = typeof apiApp
ただしこちらの場合は、うまく推論してもらえない様子でした。この辺りの背景などはあまりわかっていませんが、今回は「そういうものらしい」ということで進めていきます。
hono/clientを使って、SSR JSXでAPIを呼び出す
APIの準備ができたので、呼び出すクライアントアプリもHonoで作りましょう。クライアントアプリはapiApp
とは別にnew Hono
しておきます。API側のルートを登録するため、クライアントアプリ側でapp.route
を利用して登録するようにしました。Next.js Page Routerをよく使っていたので、/api
から始まるものがREST APIという設計で進めていきます。
const app = new Hono()
app.route('/api', apiApp)
app.get('/', async (c) => {
return c.render(
<main>
<h1>Hello hono</h1>
</main>
)
})
あとはhono/client
を使ってAPIを呼べるようにしましょう。APIのURLは開発・テスト・ステージング・本番環境によって変わるのでリクエストヘッダーを元に作るようにしています。また、app.route
にて/api
から始まるパスであると定義していますので、これもhc
関数の引数で補完します。
const host = c.req.header('host')
const protocol = c.req.header('x-forwarded-proto') ?? host?.startsWith('localhost') ? 'http' : 'https'
const apiBase = `${protocol}://${host}`
const client = hc<ApiAppType>(`${apiBase}/api`)
最後のこのClientを使ってAPIを実装しましょう。
app.get('/', async (c) => {
const host = c.req.header('host')
const protocol = c.req.header('x-forwarded-proto') ?? host?.startsWith('localhost') ? 'http' : 'https'
const apiBase = `${protocol}://${host}`
const client = hc<ApiAppType>(`${apiBase}/api`)
const res = await client.hello.$get()
const data = res.ok ? await res.json(): null
return c.render(
<main>
<h1>Hello hono</h1>
<div>
<h2>Message from Server</h2>
<p>{data?.message}</p>
</div>
</main>
)
})
これでアプリにアクセスすると、動作します。
やってみて
フロントエンドとAPI両方を1プロジェクトで管理できるのも魅力的ですが、このようにSDKライクなクラスまで使えるとなると相当便利だなと思えてきました。SSRでAPIを作らずに処理しても良いのですが、こちらの方法を知っておくことで抽象化やAPIとしての提供なども見えてくるのは良さそうです。