microCMS の API を aspida で型安全に利用する
はじめに
aspida は API のリクエスト/レスポンスに型定義ができる HTTP クライアントラッパーです。
API から取得する内容に予め型を定義しておくことで型安全に API を利用することを目的にしています。
インストール
yarn add @aspida/fetch
@aspida/fetch
のほかにも、@aspida/axios
/ @aspida/ky
/ @aspida/node-fetch
が用意されています。
microCMS で API スキーマを設定
microCMS でテスト用のデータを作成しましょう、今回は複数のタグが設定出来るブログ記事を想定しています。
記事に紐付けるタグを作ります。リスト形式で中身のオブジェクトは name だけです。
記事データを作ります。こちらもリスト形式でタグの種類は 複数コンテンツ参照
で先程作ったタグを紐付けます。
スキーマが設定終われば、適当なタグと記事の内容を登録しておきます。
GET リクエストで一覧を取得
レスポンスの確認
https://[your-service-name].microcms.io/api/v1/post
にリクエストすると、下記のようなレスポンスが取得できます。
{
"contents": [
{
"id": "lxwc41jwt",
"createdAt": "2020-10-13T11:52:05.153Z",
"updatedAt": "2020-10-13T11:52:05.153Z",
"publishedAt": "2020-10-13T11:52:05.153Z",
"title": "記事2",
"tag": [
{
"id": "rxayo0c4e",
"createdAt": "2020-09-21T06:31:30.797Z",
"updatedAt": "2020-09-21T06:31:30.797Z",
"publishedAt": "2020-09-21T06:31:30.797Z",
"name": "React"
},
{
"id": "KAYq3aCF0",
"createdAt": "2020-09-28T12:12:31.134Z",
"updatedAt": "2020-09-28T12:12:31.134Z",
"publishedAt": "2020-09-28T12:12:31.134Z",
"name": "Next.js"
}
],
"body": "<p>React と Next.js の記事</p>"
},
{
"id": "aqbxzshbtj",
"createdAt": "2020-10-13T11:51:19.794Z",
"updatedAt": "2020-10-13T11:51:19.794Z",
"publishedAt": "2020-10-13T11:51:19.794Z",
"title": "記事1",
"tag": [
{
"id": "Bs89dObS_m",
"createdAt": "2020-10-06T14:35:19.719Z",
"updatedAt": "2020-10-06T14:35:19.719Z",
"publishedAt": "2020-10-06T14:35:19.719Z",
"name": "TypeScript"
},
{
"id": "rxayo0c4e",
"createdAt": "2020-09-21T06:31:30.797Z",
"updatedAt": "2020-09-21T06:31:30.797Z",
"publishedAt": "2020-09-21T06:31:30.797Z",
"name": "React"
}
],
"body": "<p>TypeScript と React の記事</p>"
}
],
"totalCount": 2,
"offset": 0,
"limit": 10
}
型の定義
このレスポンスの型を定義していきます。contents 内の id, createdAt, updatedAt, publishedAt と
totalCount, offset, limit は共有のプロパティで毎回使うので apis/common.ts に定義しておきます。
// microCMS から返ってくるオブジェクトの共通プロパティ
export interface CommonItem {
id: string
createdAt: string
updatedAt: string
publishedAt: string
}
// microCMS から返ってくるリストの共通プロパティ T に上記のオブジェクトが入る
export interface CommonList<T> {
contents: T[]
totalCount: number
offset: number
limit: number
}
次に apis/post/index.ts を作成します、このパスは API のエンドポイントと同じパスにしてください。
import { CommonItem, CommonList } from '../common'
interface TagItem extends CommonItem {
name: string
}
// 後述の記事を1件取得する時に使うので export しています
export interface ArticleItem extends CommonItem {
title: string
description: string
tags: TagItem[]
markdown: string
}
// APIの型定義
export interface Methods {
get: {
resBody: CommonList<ArticleItem>
}
}
aspida の型定義をビルドする
package.json に npm スクリプトを追加します。
{
"scripts": {
"api:build": "aspida"
}
}
yarn api:build
を実行すると apis/$api.ts が生成されます。
/* eslint-disable */
import { AspidaClient } from 'aspida'
import { Methods as Methods0 } from './article'
const api = <T>({ baseURL, fetch }: AspidaClient<T>) => {
const prefix = (baseURL === undefined ? '' : baseURL).replace(/\/$/, '')
const PATH0 = '/article'
const GET = 'GET'
return {
article: {
get: (option?: { config?: T }) =>
fetch<Methods0['get']['resBody']>(prefix, PATH0, GET, option).json(),
$get: (option?: { config?: T }) =>
fetch<Methods0['get']['resBody']>(prefix, PATH0, GET, option).json().then(r => r.body),
$path: () => `${prefix}${PATH0}`
}
}
}
export type ApiInstance = ReturnType<typeof api>
export default api
fetch からリクエスト
fetch を使ってリクエストする際には aspida をラッパーに使用します。
microCMS の場合、ヘッダーには X-API-KEY を含める必要があります。
import aspida from '@aspida/fetch';
import api from '~/apis/$api';
const url = 'https://[your-service-name].microcms.io/api/v1';
const config = {headers: {'X-API-KEY': 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'}};
const getList = async () => {
const _fetch = api(aspida(fetch, {baseURL: `${url}/post`}));
// res が CommonList<ArticleItem> 型になっている
const res = await _fetch.article.$get({config});
};
GET リクエストで記事を 1 件取得
https://[your-service-name].microcms.io/api/v1/post/lxwc41jwt
にリクエストすると、下記のようなレスポンスが取得できます。先程取得した一覧の contents の中身が 1 件返ってきます。
{
"id": "lxwc41jwt",
"createdAt": "2020-10-13T11:52:05.153Z",
"updatedAt": "2020-10-13T11:52:05.153Z",
"publishedAt": "2020-10-13T11:52:05.153Z",
"title": "記事2",
"tag": [
{
"id": "rxayo0c4e",
"createdAt": "2020-09-21T06:31:30.797Z",
"updatedAt": "2020-09-21T06:31:30.797Z",
"publishedAt": "2020-09-21T06:31:30.797Z",
"name": "React"
},
{
"id": "KAYq3aCF0",
"createdAt": "2020-09-28T12:12:31.134Z",
"updatedAt": "2020-09-28T12:12:31.134Z",
"publishedAt": "2020-09-28T12:12:31.134Z",
"name": "Next.js"
}
],
"body": "<p>React と Next.js の記事</p>"
}
aspida の型定義は apis/article/_contentId@string/index.ts に記述します。
ファイル名の _
で始まる部分がパス変数で@
の後ろにnumber
もしくはstring
の型を指定します。デフォルトはnumber | string
です。
import { ArticleItem } from '../'
export interface Methods {
get: {
resBody: ArticleItem
}
}
型が定義できたら先ほどと同様に yarn api:build
を実行して apis/$api.ts
を更新します。
リクエストを投げる際は _contentId()
に変数を渡して $get()
します。
import aspida from '@aspida/fetch';
import api from '~/apis/$api';
const url = 'https://[your-service-name].microcms.io/api/v1';
const config = {headers: {'X-API-KEY': 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'}};
const getPost = async (id: string) => {
const _fetch = api(aspida(fetch, {baseURL: `${url}/post`}));
// res が ArticleItem 型になっている
const res = await _fetch.article._contentId(id).$get({config});
};
まとめ
今回は aspida を使って GET レスポンスの型を定義する方法だけを紹介しましたが、
そのほかにもリクエスト時のパラメタ、PUST
/ PUT
/ PATCH
/ DELETE
といったほかのメソッドの型も定義できます。
Discussion