はじめに
ページネーション、ローディング状態管理、キャッシュ、トークンの自動更新——API通信まわりのコードは、機能が増えるたびに気づけば同じようなロジックがあちこちに散らばっていきます。axiosやfetchを薄くラップしただけの自前フックが、プロジェクトごとに少しずつ違う形で量産されていく経験がある方も多いのではないでしょうか。
そんな「リクエスト周りの車輪の再発明」に終止符を打つのが、今回紹介するAlovaです。単なるHTTPクライアントではなく、リクエストにまつわる典型的な処理をあらかじめ戦略として用意してくれる「リクエスト戦略レイヤー」というのが最大の特徴です。
Alovaとは
Alovaは「The request strategy layer for JavaScript」を掲げるライブラリで、20以上の組み込み戦略によってリクエスト関連のコード量を最大70%削減できるとされています。React・Vue・Svelte・Angular・Solidなど15以上のフレームワークに対応し、Next.jsやNuxt 3のようなSSR環境、さらにNode.js・Bun・Denoといったサーバーサイドやミニプログラムまでカバーする、フレームワーク非依存の設計になっています。
主な特徴
- 豊富な組み込み戦略 - ページネーション、データ監視リクエスト、プリロード、フォーム送信、トークン自動更新など、よくあるUIパターンをフックとして提供
- マルチフレームワーク対応 - React・Vue・Svelte・Angular・Solid・Preact・Qwik・Litなど、使っているUIライブラリを問わず同じ書き方で使える
- サーバーサイド戦略も内蔵 - リトライ、レート制限、分散ロックによるアトミック処理など、バックエンドで使える戦略も用意されている
- 柔軟なキャッシュ機構 - メモリ・永続化キャッシュを設定でき、同一リクエストの重複実行を抑制できる
インストール
npmを使う場合は以下のコマンドでインストールできます。
npm i alova
Yarnやpnpmを使っている場合も同様です。
yarn add alova
# または
pnpm add alova
利用するUIフレームワーク用のアダプター(例: alova/react、alova/vue)も併せて必要になります。
基本的な使い方
まずはAlovaのインスタンスを作成し、APIエンドポイントを定義します。
import { createAlova } from 'alova';
import adapterFetch from 'alova/fetch';
import ReactHook from 'alova/react';
export const alovaInstance = createAlova({
baseURL: '/api',
requestAdapter: adapterFetch(),
statesHook: ReactHook,
responded: response => response.json()
});
// リクエストメソッドの定義
export const getTodos = () => alovaInstance.Get('/todos');
Reactコンポーネント内では、useRequestフックでリクエストを実行し、ローディングやエラーの状態をまとめて受け取れます。
import { useRequest } from 'alova/client';
import { getTodos } from './api';
function TodoList() {
const { loading, data, error } = useRequest(getTodos);
if (loading) return <p>読み込み中...</p>;
if (error) return <p>エラーが発生しました</p>;
return (
<ul>
{data?.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}
useRequestを使うだけで、キャッシュの有無に応じたリクエストの重複排除や再実行のハンドリングが自動的に行われます。
実践的なユースケース
ページネーション付き一覧の実装
一覧画面でありがちなページネーション処理も、usePaginationを使えばわずかな行数で実装できます。
import { usePagination } from 'alova/client';
import { alovaInstance } from './alova';
const getTodoList = (page, pageSize) =>
alovaInstance.Get('/todos', {
params: { page, pageSize }
});
function TodoPage() {
const { loading, data, page, pageCount, total, onNext, onPrev } =
usePagination(getTodoList, {
initialPage: 1,
initialPageSize: 10
});
return (
<div>
{loading ? '読み込み中...' : data.map(item => <div key={item.id}>{item.title}</div>)}
<button onClick={onPrev} disabled={page <= 1}>前へ</button>
<span>{page} / {pageCount}(全{total}件)</span>
<button onClick={onNext}>次へ</button>
</div>
);
}
ページ数の計算やローディング状態の管理を自前で書く必要がなくなり、UIロジックに専念できます。
検索条件変更時の自動再取得
検索キーワードやフィルター条件が変わるたびにリクエストを送りたいケースでは、useWatcherが便利です。
import { useWatcher } from 'alova/client';
import { useState } from 'react';
function SearchResults() {
const [keyword, setKeyword] = useState('');
const { loading, data } = useWatcher(
() => alovaInstance.Get('/search', { params: { keyword } }),
[keyword],
{ debounce: [500] } // 500ms のデバウンスで無駄なリクエストを抑制
);
return (
<div>
<input
value={keyword}
onChange={e => setKeyword(e.target.value)}
placeholder="検索キーワードを入力"
/>
{loading ? '検索中...' : data?.map(item => <p key={item.id}>{item.title}</p>)}
</div>
);
}
依存する値が変化するたびにデバウンス付きでリクエストが実行されるため、検索ボックスの実装にありがちな「入力のたびにAPIが飛んでしまう」問題をシンプルに解決できます。
まとめ
Alovaは、リクエストにまつわる「よくある処理」を戦略として切り出すことで、API通信まわりのコードを大幅にシンプルにしてくれるライブラリです。ページネーションや検索の自動再取得だけでなく、フォーム送信管理やトークン自動更新といった戦略も用意されているため、実務でありがちなユースケースの多くをカバーできます。
React QueryやSWRのようなデータフェッチライブラリに近い立ち位置ですが、フレームワーク非依存かつサーバーサイド戦略まで内蔵している点がAlovaならではの強みです。次にAPI通信のコードが肥大化してきたと感じたら、一度試してみてはいかがでしょうか。
