🔖
シンプルなaxiosのラッパー
APIを叩くクライアントまわりは、いつも迷うが、最終的に薄いラッパースクリプトになった。
- エラーハンドリング
- axiosに渡すデフォルトの設定
などを意識して作った。
import {
AxiosError,
AxiosRequestConfig,
AxiosResponse,
AxiosResponseHeaders,
} from 'axios';
import { nanoid } from 'nanoid';
import { getAccessToken, TokenError } from 'your-access-token-service';
export type ResponseAPI<Body = unknown, Header = unknown> =
| {
isSuccess: true;
body: Body; // これbodyがないとき、body: undefinedって指定しないといけないので不便。なにか他に方法ないのか。
header: Header;
}
| {
isSuccess: false;
error: string;
};
const isAxiosError = (error: any): error is AxiosError => {
return !!error.isAxiosError;
};
/**
* axiosのエラーハンドリングとデフォルトの設定をやるヘルパー関数。
* @param request 引数1requestIdは自動生成id。引数2defaultConfigはbaseURLとアクセストークンの設定
* @param defaultErrorText
* @returns
* @example
* // axios単体で使う
* requestAxios(
* () =>
* axios.post<LineTokenResponse>(
* 'https://example.com',
* { hoge: 'hoge' },
* {
* headers: { 'Content-Type': 'application/json' },
* },
* ),
* 'hoge作成に失敗しました!',
* );
*
* // openapi generator の sdkと一緒に使う
* const hogeApi = new HogeApi();
*
* requestAxios(
* (requestId, defaultConfig) =>
* HogeApi.getById(
* hogeId,
* defaultConfig,
* ),
* 'hogeの取得に失敗しました!',
*);
*/
export const requestAxios = async <T = unknown>(
request: (
requestId: string,
defaultConfig: AxiosRequestConfig,
) => Promise<AxiosResponse<T>>,
defaultErrorText: string,
isSkipToSetToken?: boolean,
): Promise<ResponseAPI<T, Record<string, string>>> => {
try {
const accessToken = isSkipToSetToken ? '' : await getAccessToken();
const defaultRequestId = nanoid();
const defaultConfig = {
headers: isSkipToSetToken
? undefined
: {
Authorization: `Bearer ${accessToken}`,
},
// 残念ながらurlを絶対パスで指定されている状態でaxios.request()するとbasePathが無視されるみたい。
// openapi generator で生成されたSDKはurlを絶対パスを渡すため、この設定は無視される。
// baseURLの代わりにbasePathをConfigurationから渡すこと。
baseURL: process.env.API_URL,
};
const res = await request(defaultRequestId, defaultConfig);
return {
isSuccess: true,
body: res.data,
header: res.headers,
};
} catch (err) {
console.error(err);
if (isAxiosError(err)) {
if (err.response) {
if (err.response.status >= 500) {
return {
isSuccess: false,
error: `${defaultErrorText} サーバーエラーです。しばらく経ってからもう一度試してみてください。`,
};
}
if (err.response.status >= 400) {
return {
isSuccess: false,
error:
err.response.data?.message ?? `${defaultErrorText} リクエストエラーです。`,
};
}
} else if (err.request) {
return {
isSuccess: false,
error: `${defaultErrorText} リクエストがタイムアウトしました。`,
};
} else {
return {
isSuccess: false,
error: `${defaultErrorText} 原因不明のエラーが発生しました。`,
};
}
}
if (err instanceof TokenError) {
return {
isSuccess: false,
error: 'セッションの期限切れです。ログインし直してください。',
};
}
return {
isSuccess: false,
error: `${defaultErrorText} ネットワークエラーです。`,
};
}
};
使い方
const res = await requestAxios(
(requestId, defaultConfig) =>
axios.post(
'https://example.com',
{ hoge: 'hoge' },
defaultConfig
),
'hoge作成に失敗しました!',
);
if(!res.isSuccess){
console.error(res.error);
return;
}
console.log(res.body);
Discussion