【JavaScript】Fetch APIを良い感じにメソッド化するリファクタ例

JavaScript

今回やりたいこと

Fetch APIを利用してGETとPOSTを実行する以下のようなコードがあったとします。

const getTodoResponse = async () => {
  const response = await fetch("http://localhost:4000/todos/1", {
    method: "GET",
  });
  return response.json(); // { id: 1, content: 'go somewhere', completed: true }
};

// 実行結果
// async関数はPromiseを返すので、then()を利用してデータを参照する
getTodoResponse().then((todo) => console.log(todo));
// { id: 1, content: 'go somewhere', completed: true }
const postTodoResponse = async () => {
  const postItem = { content: "go shopping", complated: false };
  const response = await fetch("http://localhost:4000/todos", {
    method: "POST",
    body: JSON.stringify(postItem),
    headers: {
      "Content-Type": "application/json",
    },
  });
  return response.json();
};

// 実行結果
postTodoResponse().then((todo) => console.log(todo));
// { content: 'go shopping', complated: false, id: 3 }

上記のコードに記述されていたFetch APIの処理を以下のようにメソッド化するのが今回のゴールです。

const getTodoResponse = async () => {
  const response = await getTodo(1)
  return response.json(); // { id: 1, content: 'go somewhere', completed: true }
};

// 実行結果
// async関数はPromiseを返すので、then()を利用してデータを参照する
getTodoResponse().then((todo) => console.log(todo));
// { id: 1, content: 'go somewhere', completed: true }
const postTodoResponse = async () => {
  const postItem = { content: "go shopping", completed: false };
  const response = await postTodo(postItem);
  return response.json();
};

// 実行結果
postTodoResponse().then((todo) => console.log(todo));
// { content: 'go shopping', complated: false, id: 3 }

『fetchメソッドの作成』をメソッド化する

引数を利用してfetchメソッドを生成するようにします。

src/lib/api.ts

const request = (method: string, input: RequestInfo, init?: RequestInit) =>
  fetch(input, {
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
    },
    method,
    // 必要に応じてオプションを追加
    ...init,
  });

export const getTodo = (id: number) =>
  request("GET", `http://localhost:4000/todos/${id}`);

type PostTodoParams = {
  content: string;
  completed?: boolean;
};

// completedの指定がない場合、completedはfalseにする
export const postTodo = ({ content, completed = false }: PostTodoParams) =>
  request("POST", `http://localhost:4000/todos`, {
    body: JSON.stringify({ content, completed }),
  });

エイリアスを用意する

記述を簡単にするため、HTTPメソッドを指定するメソッド(fetchメソッドのエイリアス)を用意します。

const request = (method: string) => (input: RequestInfo, init?: RequestInit) =>
  fetch(input, {
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
    },
    method,
    // 必要に応じてオプションを追加
    ...init,
  });

// エイリアスの作成
const get = request("GET");
const post = request("POST");

export const getTodo = (id: number) => get(`http://localhost:4000/todos/${id}`);

type PostTodoParams = {
  content: string;
  completed?: boolean;
};

export const postTodo = ({ content, completed = false }: PostTodoParams) =>
  post(`http://localhost:4000/todos`, {
    body: JSON.stringify({ content, completed }),
  });

最終的なコードと実行結果

最終的なコードは以下の通りです。

src/lib/api.ts

// URLの共通部分を定数化
const baseURL = "http://localhost:4000";

const request = (method: string) => (input: RequestInfo, init?: RequestInit) =>
  fetch(input, {
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
    },
    method,
    // 必要に応じてオプションを追加
    ...init,
  });

const get = request("GET");
const post = request("POST");

export const getTodo = (id: number) => get(`${baseURL}/todos/${id}`);

type PostTodoParams = {
  content: string;
  completed?: boolean;
};

export const postTodo = ({ content, completed = false }: PostTodoParams) =>
  post(`${baseURL}/todos`, {
    body: JSON.stringify({ content, completed }),
  });

実行結果は以下の通りです。

const getTodoResponse = async () => {
  const response = await getTodo(1);
  return response.json(); // { id: 1, content: 'go somewhere', completed: true }
};

// 実行結果
getTodoResponse().then((todo) => console.log(todo));
// { id: 1, content: 'go somewhere', completed: true }
const postTodoResponse = async () => {
  const postItem = { content: "go shopping", completed: false };
  const response = await postTodo(postItem);
  return response.json();
};

// 実行結果
postTodoResponse().then((todo) => console.log(todo));
// { content: 'go shopping', complated: false, id: 3 }

さいごに

Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!

参考資料