🐾

【TypeScript】非同期処理についてまとめる。fetch,axios編

に公開

はじめに

TypeScript で非同期処理を扱うについて、自分の知識が浅い部分があったので改めてまとめて行こうと思います。(初心者のため優しい目で見てください)
第一弾目は以下に書いてます!!
https://zenn.dev/daichi09167/articles/701ed3fdc0332a

fetchについて

fetch とは、HTTPリクエストを送信する関数で、ネットワークを介してリソース(データ)を取得するための仕組みで、Promiseを返します。

// GETリクエスト
fetch('https://jsonplaceholder.typicode.com/todos/1')
  .then(response => response.json()) // レスポンスをJSONに変換
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

ポイント

  • fetch は常に Promise<Response> を返します。
  • レスポンスのボディは response.json() や response.text() などで取り出すことが可能です。
  • ネットワークエラー(サーバーがダウンしている、存在しないドメインにアクセスしたなど)は catch で捕まります。
  • HTTPステータスエラー(400番台や500番台など)は catch では捕まらないため、必要に応じて res.okres.status をチェックして明示的に処理する必要があります。
interface Todo {
  userId: number;
  id: number;
  title: string;
  completed: boolean;
}

fetch('https://jsonplaceholder.typicode.com/todos/1')
  .then(res => res.json())
  .then((data: Todo) => {
    console.log(data.title); 
  });

ポイント

  • fetchで取得するJSONの型を明示すると、型安全になります。

HTTPメソッドの使い分け

fetch は 任意のHTTPメソッド を指定できます。
CRUD処理での使い分けは以下の通りです。

メソッド 用途
GET データ取得 fetch('/api/todos')
POST 新規作成 fetch('/api/todos', { method: 'POST', body: ... })
PUT 全体更新 fetch('/api/todos/1', { method: 'PUT', body: ... })
PATCH 一部更新 fetch('/api/todos/1', { method: 'PATCH', body: ... })
DELETE 削除 fetch('/api/todos/1', { method: 'DELETE' })

POSTリクエストの例

// まず型を定義
interface Todo {
  userId: number;
  id: number;
  title: string;
  completed: boolean;
}

// POSTリクエスト用データ
const newTodo = {
  userId: 1,
  title: 'Learn TypeScript',
  completed: false
};

const createTodo = async (): Promise<void> => {
  try {
    const res = await fetch('https://jsonplaceholder.typicode.com/todos', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(newTodo)
    });

    if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`);

    // 型注釈をつける
    const data: Todo = await res.json();
    console.log('Created:', data);
  } catch (err) {
    console.error(err);
  }
};

createTodo();

クエリパラメータの付け方

fetch でクエリパラメータを付けて条件付きデータを取得する例です。
URLSearchParamsを使うと URL に付与するクエリパラメータを簡単に作れます。

interface Todo {
  userId: number;
  id: number;
  title: string;
  completed: boolean;
}

async function fetchTodos() {
  try {
    const params = new URLSearchParams({
      userId: '1',
      completed: 'false'
    });

    const res = await fetch(`https://jsonplaceholder.typicode.com/todos?${params.toString()}`);

    if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`);

    const data: Todo[] = await res.json();
    console.log(data);
  } catch (err) {
    console.error('Error:', err);
  }
}

fetchTodos();

タイムアウト制御

fetch はデフォルトでタイムアウトがなく、通信が止まったままになることがあります。
そこで AbortController を使って中断します。

async function fetchWithTimeout() {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), 5000); // 5秒で中断

  try {
    const res = await fetch('https://httpbin.org/delay/10', {
      signal: controller.signal
    });

    if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`);

    const data = await res.json();
    console.log(data);

  } catch (err: unknown) {
    if (err instanceof DOMException && err.name === 'AbortError') {
      console.error('Fetch aborted due to timeout');
    } else if (err instanceof Error) {
      console.error('Other error:', err.message);
    } else {
      console.error('Unexpected error', err);
    }
  } finally {
    clearTimeout(timeoutId);
  }
}

fetchWithTimeout();

axiosについて

axios とは、HTTPリクエストを送信するための 外部ライブラリ で、Promise ベースで動作します。
fetch と同じくデータの取得や送信が可能ですが、以下の点が特徴です。

  • JSON の送受信を自動で行ってくれる
  • HTTPステータスエラー(400/500など)も catch で捕捉できる
  • タイムアウトやリクエストヘッダーの設定が簡単
  • クエリパラメータをオブジェクトで渡せる

postリクエスト例

import axios, { AxiosError } from 'axios';

interface Todo {
  userId: number;
  id: number;
  title: string;
  completed: boolean;
}

const newTodo = {
  userId: 1,
  title: 'Learn Axios',
  completed: false
};

async function createTodo() {
  try {
    const res = await axios.post<Todo>(
      'https://jsonplaceholder.typicode.com/todos',
      newTodo,
      {
        headers: {
          'Content-Type': 'application/json'
        }
      }
    );

    console.log('Created Todo:', res.data);
  } catch (err: unknown) {
    if (axios.isAxiosError(err)) {
      // AxiosError なら response や request が安全に使える
      if (err.response) {
        console.error('HTTP error:', err.response.status, err.response.data);
      } else if (err.request) {
        console.error('No response:', err.request);
      } else {
        console.error('Axios error:', err.message);
      }
    } else if (err instanceof Error) {
      console.error('Other error:', err.message);
    } else {
      console.error('Unexpected error', err);
    }
  }
}

createTodo();

ポイント

  • axios.post<Todo>(url, data, config)
  • 型注釈 <Todo> で返却データを型安全に扱える
  • 第2引数に送信データ、第3引数にヘッダーなどの設定を指定
  • Content-Type: 'application/json' → JSON データを送る場合に必要
  • fetch と違って、HTTPステータスエラー(400/500)も catch に入る
    → err.response.status でステータスを確認可能
  • axios は自動で JSON に変換して送信してくれるので、fetch のように JSON.stringify() は不要
  • res.data に作成された Todo が返ってくる

HTTPメソッドの使い分け

メソッド 用途 axios の書き方
GET データ取得 axios.get(url)
POST 新規作成 axios.post(url, data)
PUT 全体更新 axios.put(url, data)
PATCH 一部更新 axios.patch(url, data)
DELETE 削除 axios.delete(url)

クエリパラメータの付け方

params にオブジェクトを渡すだけで自動でクエリ文字列に変換
URLSearchParams を使わなくても安全にエンコードされる
この例では userId=1 かつ completed=false の Todo だけを取得

import axios, { AxiosError } from 'axios';

interface Todo {
  userId: number;
  id: number;
  title: string;
  completed: boolean;
}

async function fetchTodos() {
  try {
    const res = await axios.get<Todo[]>('https://jsonplaceholder.typicode.com/todos', {
      params: {
        userId: 1,
        completed: false
      }
    });

    console.log(res.data);
  } catch (err: unknown) {
    if (axios.isAxiosError(err)) {
      // AxiosError 型の場合
      if (err.response) {
        console.error('HTTP error:', err.response.status, err.response.data);
      } else if (err.request) {
        console.error('No response:', err.request);
      } else {
        console.error('Axios error:', err.message);
      }
    } else if (err instanceof Error) {
      // その他のエラー
      console.error('Other error:', err.message);
    } else {
      console.error('Unexpected error', err);
    }
  }
}

fetchTodos();

タイムアウト制御

fetch の AbortController の代わりに timeout オプションで簡単に制御できる

import axios from 'axios';

async function fetchWithTimeout() {
  try {
    const res = await axios.get('https://httpbin.org/delay/10', {
      timeout: 5000 // 5秒でタイムアウト
    });

    console.log('Response received:', res.data);
  } catch (err: unknown) {
    if (axios.isAxiosError(err)) {
      // AxiosError 型の場合
      if (err.code === 'ECONNABORTED') {
        console.error('Request timed out');
      } else if (err.response) {
        console.error('HTTP error:', err.response.status, err.response.data);
      } else if (err.request) {
        console.error('No response:', err.request);
      } else {
        console.error('Axios error:', err.message);
      }
    } else if (err instanceof Error) {
      console.error('Other error:', err.message);
    } else {
      console.error('Unexpected error', err);
    }
  }
}

fetchWithTimeout();

エラーハンドリング

fetch と違い、HTTPステータスエラーも catch に入る
err.response.status でステータスコード確認可能

import axios from 'axios';

async function fetchWithErrorHandling() {
  try {
    // 存在しない URL で意図的に 404 を返す
    const res = await axios.get('https://jsonplaceholder.typicode.com/todos/9999');
    console.log(res.data);
  } catch (err: unknown) {
    if (axios.isAxiosError(err)) {
      // AxiosError 型の場合
      if (err.response) {
        console.error('HTTP error:', err.response.status, err.response.data);
      } else if (err.request) {
        console.error('No response:', err.request);
      } else {
        console.error('Axios error:', err.message);
      }
    } else if (err instanceof Error) {
      // その他の一般的なエラー
      console.error('Other error:', err.message);
    } else {
      // 予期しない型のエラー
      console.error('Unexpected error', err);
    }
  }
}

fetchWithErrorHandling();

まとめ

  • fetch

    • HTTPリクエストを送信する関数で、Promise を返す
    • データ取得や送信が可能だが、HTTPステータスエラーは自動では catch に入らない
    • タイムアウト制御は AbortController を使う必要がある
    • JSONの送受信やクエリパラメータの処理は自分で実装する必要がある
  • axios

    • 外部ライブラリで、Promise ベースで動作
    • JSON の送受信を自動で行ってくれる
    • HTTPステータスエラーも catch で捕捉可能
    • タイムアウトやヘッダー設定が簡単で、クエリパラメータもオブジェクトで安全に渡せる

Discussion