Nuxt.js(TypeScript)で開発していく際にaspidaというOSSを使用すると開発が捗ったので紹介します
主に以下のような不満を解消してくれるものになると思います
- APIも型定義したい
- フロントから叩いているAPI一覧が分からん
- フロントで利用するAPIを一元管理したい
- APIの返り値が分からんからconsole.log()で出力してみるってのを毎回やってる...
aspidaとは
ギリシャ語で「盾」って意味らしいです
型安全な HTTP クライアントラッパーを提供してくれるOSSです
日本語ドキュメントのページでかなり詳細に説明されていました
こちらに使用例まで用意されています
APIのInterfaceを静的に定義できるので、VSCodeで補完が効くようになったり、APIが仕様変更で返り値やエンドポイントが変更になっても血眼でgrepせずともapis/ 以下の定義を変えれば良くなります
個人的に好きなのは
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では以下のように書きます
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にコマンド登録しておくと良いです
"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とやりとりしている箇所はもちろんそのまま機能するので、
徐々に移行できる点が良いですね
あのエンドポイントだけはめちゃくちゃ複雑だから型定義しておきたい...
のような需要も満たしてくれそうです
また、上記のサンプルコードで$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 })
このようにスッキリします
const user = app.$api.users._userId(params.userId).$get();
TypeScriptを導入済みならすぐにでもaspidaは導入できるし、徐々に移行していこうってスタンスが取れるのがaspidaの良いところかなと思います
APIの型定義の際に手動でCLIを叩きますが現在、Swaggerをaspidaの型定義に変換するモジュールを開発中のようです
フロントから利用するHTTPクライアントが型安全になる世界が来るのも遅くはなさそうですね
また、aspidaは最近公開されたOSSでContributeどしどし募集しているようです