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としての提供なども見えてくるのは良さそうです。

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark