LoginSignup
4

More than 3 years have passed since last update.

aspidaでフロントエンドからAPIを見える化する

Last updated at Posted at 2020-03-15

Nuxt.js(TypeScript)で開発していく際にaspidaというOSSを使用すると開発が捗ったので紹介します

主に以下のような不満を解消してくれるものになると思います

  • APIも型定義したい
  • フロントから叩いているAPI一覧が分からん
  • フロントで利用するAPIを一元管理したい
  • APIの返り値が分からんからconsole.log()で出力してみるってのを毎回やってる...

aspidaとは

ギリシャ語で「盾」って意味らしいです
型安全な HTTP クライアントラッパーを提供してくれるOSSです
日本語ドキュメントのページでかなり詳細に説明されていました

こちらに使用例まで用意されています

APIのInterfaceを静的に定義できるので、VSCodeで補完が効くようになったり、APIが仕様変更で返り値やエンドポイントが変更になっても血眼でgrepせずともapis/ 以下の定義を変えれば良くなります :clap: :clap:

個人的に好きなのは
APIのパス定義がNuxtのpages/ と同じような作りで見やすい点です

例えば、
任意のユーザの任意の記事の取得と更新をするエンドポイントがそれぞれ

GET
users/:user_id/article/:article_id
PUT
users/:user_id/article/:article_id

user_id    => autoincrementで生成される数字
article_id => ハッシュ値

で定義されていたとします
その際クライアントサイドでは以下の様なディレクトリを切って

/apis                          <- /apis以下に作られる(一応設定で変えられる
|--$api.ts                     <- ビルドするとAPI定義が生成されるファイル
|--users
   |--_userId@number           <- 変数の型も定義できる
      |--articles
         |--_articleId@string
            |--index.ts        <- このファイルでAPIのやりとりを定義する

index.tsでは以下のように書きます

index.ts
export type Article = {
  id?: string
  user_id?: string
  title: string
  body: string
}

export interface Methods {
  get: {
    // APIの返り値
    resBody: Article
  }
  put: {
    // put時に投げる
    reqBody: {
      title: string
      bodyParser: string
    }
    // APIの返り値
    resBody: Article
  }
}

そして、aspida --buildを叩きます
めんどくさいのでscriptsにコマンド登録しておくと良いです

package.json
  "scripts": {
    "api": "aspida --build",
  },

コマンド登録してnpm run apiコマンドが無事成功すると
apis/$api.ts was built successfully. と出力されます
これで準備完了です

apis/$api.tsを見にいくとなにやらいっぱい書かれていると思いますが
このファイルはlockファイルみたいなもので、触っちゃいけません

アプリケーションでの利用

アプリケーションではインポートして利用するのですが、
pluginでinjectしておくと便利です
pluginでinjectするやり方は、こちらの記事(aspidaの作者さんの記事です)がとても参考になるのでここでは省きます
設定が済めば、以下のようにappやthisのコンテキストから利用できます

<script lang="ts">
import Vue from 'vue'

~()~

export default Vue.extend({
  async asyncData ({ app, params, error }) {
    const article = await app.$api.users
      ._userId(parseInt(params.userId))
      .articles
      ._articleId(params.articleId)
      .$get()
      .catch(() => { error({ statusCode: 404 }) });

    return { article };
  },

  methods: {
    submit (article: Article) {
      this.$api.users
        ._userId(parseInt(this.$store.state.user.id))
        .articles
        ._articleId(this.$route.params.articleId)
        .put({ data: article })
        .then(({ data }) => {
          if (data.statusCode === 200) ...
          if (data.statusCode === 204) ...
        })
        .catch((err) => {
          ...
        })
    }
  }
</script>

既存のプロジェクトでthis.$axiosとかでAPIとやりとりしている箇所はもちろんそのまま機能するので、
徐々に移行できる点が良いですね

あのエンドポイントだけはめちゃくちゃ複雑だから型定義しておきたい...
のような需要も満たしてくれそうです :clap: :clap:

また、上記のサンプルコードで$get()post()など$が付いたり付かなかったりのHTTP Methodが出てきました
これはapis/$api.tsの中を見るとわかるのですが、
Response.dataが欲しいかResponseが欲しいかって違いです

Response.metaから取れるメタ情報からハンドリングをする必要があれば$なしを利用して、
単純にデータが欲しければ$ありを利用する感じです

また、$ありを利用すれば
axiosを使っていると、こんな感じで冗長になりがちなコードが

const user = app.$axios.$get(`/users/${params.userId}`).then(({ data }) => { return data })

このようにスッキリします :thumbsup:

const user = app.$api.users._userId(params.userId).$get();

TypeScriptを導入済みならすぐにでもaspidaは導入できるし、徐々に移行していこうってスタンスが取れるのがaspidaの良いところかなと思います
APIの型定義の際に手動でCLIを叩きますが現在、Swaggerをaspidaの型定義に変換するモジュールを開発中のようです
フロントから利用するHTTPクライアントが型安全になる世界が来るのも遅くはなさそうですね :clap: :clap:

また、aspidaは最近公開されたOSSでContributeどしどし募集しているようです :muscle: :muscle:

GitHub: aspida日本語ドキュメント

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
4