🔥

HonoのHCで外部APIのAPIクライアントを作る

2024/12/05に公開

Honoのhcを用いて、このようなAPIクライアントを型定義から生成しようという試み。

詳しくはこの記事を読む。 https://zenn.dev/yusukebe/articles/a00721f8b3b92e

これはHackerNewsのAPIのサンプルコード。

const client = hc<Api>("https://hacker-news.firebaseio.com")

const resp = await client.v0.item[":item"].$get({
  param: { item: "8863.json" },
  query: { print: "pretty" },
})

const json = await resp.json()

console.log(json)

このようにhcのジェネリクスにリクエストに関する情報を渡すことで型を定義している。

import type { Hono } from "hono"
import { hc } from "hono/client"
import type { BlankEnv } from "hono/types"
import type { StatusCode } from "hono/utils/http-status"

type Api = Hono<
  BlankEnv,
  {
    "/v0/item/:item": {
      $get: {
        input: {
          param: {
            item: string
          }
          query: {
            print?: "pretty"
          }
        }
        output: {
          type: "story" | "comment" | "job" | "poll" | "pollopt"
        }
        outputFormat: "json"
        status: StatusCode
      }
    }
  }
>

const client = hc<Api>("https://hacker-news.firebaseio.com")

基本的にパス、メソッド($get)、リクエスト内容の順に書いていくのみ。

"/v0/item/:item": {
  $get: {
    input: {}
    output: {}
  }
  $post: {
    input: {}
    output: {}
  }
}

このinputには他にも「param」「query」「form」などが渡せる。

https://github.com/honojs/hono/blob/80c7e225af5fd8f92b3a69015fe78c546b678ba9/src/types.ts#L1927

何が嬉しいか

WorkersでHonoxでライブラリを使う際にViteのESMとNode.jsやらで問題が度々発生する。例えばGoogleのライブラリはNode.jsに依存していたりする。

https://www.npmjs.com/package/googleapis

簡易的なライブラリを作る

スプレッドシートなど共有ドライブのファイルを取得するには以下のエンドポイントが使える。

https://developers.google.com/drive/api/reference/rest/v3/drives/list?hl=ja

ここでドキュメントのパスとクエリパラメーターとリクエスト本文を用いて型を定義できる。

import type { BlankEnv } from "hono/types"
import type { Hono } from "hono"
import type { StatusCode } from "hono/utils/http-status"
import type { drive_v3 } from "@googleapis/drive"

export type GoogleapisWww = Hono<
  BlankEnv,
  {
    "/drive/v3/files": {
      $get: {
        input: {
          query: {
            q?: string
            pageSize?: string
            fields?: string
            orderBy?: string
          }
        }
        output: {
          json: drive_v3.Schema$FileList
        }
        outputFormat: "json"
        status: StatusCode
      }
    }
  }
>

返り値のJSONの型はライブラリから取得できる。

import type { drive_v3 } from "@googleapis/drive"

このようにhcの引数にURLを渡して1つのオブジェクトにして管理している。

import { hc } from "hono/client"
import type { GoogleapisWww } from "~/lib/google/googleapis-www"
import type { GoogleapisOauth2 } from "~/lib/google/googleapis-oauth2"
import type { GoogleapisSheets } from "~/lib/google/googleapis-sheets"

export const googleapis = {
  /**
   * https://www.googleapis.com
   */
  www: hc<GoogleapisWww>("https://www.googleapis.com"),
  /**
   * https://oauth2.googleapis.com
   */
  oauth2: hc<GoogleapisOauth2>("https://oauth2.googleapis.com"),
  /**
   * https://sheets.googleapis.com
   */
  sheets: hc<GoogleapisSheets>("https://sheets.googleapis.com"),
}

このような感じで使えるはず。

const resp = await googleapis.www.drive.drive.v3.files.$get()

Discussion