7839

雑草魂エンジニアブログ

【JS】Fetch API のResponse には気をつけて。axios とは違うぞ。。。

昨日、Fetch API を使うにあたり、大きなミスを犯して、無駄な時間を潰してしまったので、反省を込めて、この備忘録を残しておく。

Fetch APIとは

Fetch APIとは、XMLHttpRequestと同じく、HTTPリクエストを送信することができる API である。

developer.mozilla.org

ハマった罠

つい axios と同じようなノリで以下のように書いてしまうと、同じ罠にハマることになるので、ご注意を。

await fetch(process.env.API_URL + '/oauth2/token/', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  body: formDataString
}).then((responce) => {
  console.log(responce)
})

これを実行すると、Response には、API の 返却値の中身が入っておらず、Google Chrome の Developer Tools の Network タブで確認しても、Response が空の状態で焦りました。。。Status Code は 200 OK なのに、どういうことだ???と。

fetch API の定義を TypeScript で確認してみる。

declare function fetch(input: RequestInfo, init?: RequestInit): Promise<Response>
// This Fetch API interface represents the response to a request.
interface Response extends Body {
    readonly headers: Headers;
    readonly ok: boolean;
    readonly redirected: boolean;
    readonly status: number;
    readonly statusText: string;
    readonly trailer: Promise<Headers>;
    readonly type: ResponseType;
    readonly url: string;
    clone(): Response;
}
interface Body {
    readonly body: ReadableStream<Uint8Array> | null;
    readonly bodyUsed: boolean;
    arrayBuffer(): Promise<ArrayBuffer>;
    blob(): Promise<Blob>;
    formData(): Promise<FormData>;
    json(): Promise<any>;
    text(): Promise<string>;
}

Response には、body: ReadableStreamAPIの返却値が入っていて、直接データを参照できないようになっている。。。

response.json().then(data => {
  // data を使用した処理を実行する
})

json() メソッドを使うことで、Responseストリームを取得して、APIの返却値を取得するようになっていたのである。json() メソッドを使うことで、ようやくデータを取得することができた。

Fetch API のラッパー関数を作った

axios と同じように使えるように、以下のようなラッパー関数を作ったので参考で紹介しておく。

import cookie from './cookie-storage'
const api = {
  get: async (url: string) => {
    const response = await fetch(process.env.API_URL + url, {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
        Authorization: 'Bearer ' + cookie.get('accessToken'),
      },
    })
    if (!response.ok) {
      const err = await response.json()
      throw new Error(err)
    }
    return await response.json()
  },

  post: async (url: string, data: { [key: string]: string }) => {
    const response = await fetch(process.env.API_URL + url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: 'Bearer ' + cookie.get('accessToken'),
      },
      body: JSON.stringify(data),
    })
    if (!response.ok) {
      const err = await response.json()
      throw new Error(err)
    }
    return await response.json()
  },
}

export default api

呼び出して使う場合は、以下のようにする。

import api from 'utils/api'
await api
  .get('/companies/')
  .then((data) => {
    setCompanyInfo(data)
  })
  .catch((error) => {
    console.log(error)
  })

axios と同じように使えて、これで安心して HTTP リクエストを送ることができる。

まとめ

今回は、Fetch API の使い方に関して、整理を行った。

本来は、axios を使って、Django REST frameworkAPIを叩きたかったのであるが、どうしても以下の CORS 問題が解決できなかった為、苦肉の策での Fetch API 利用であった。

Access to XMLHttpRequest at 'http://localhost:8000/api/test/' from origin 'http://localhost:3000' has been blocked by CORS policy: Request header field access-control-allow-origin is not allowed by Access-Control-Allow-Headers in preflight response.

Django側では、django-cors-headers を使っており、Fetch APIでは、CORSのエラーは出ておらず、Django側の設定は問題ないようである。Fetch API も、axios と同じように、異なるオリジンに対してリクエストする時に、OPTIONSメソッドで pre-flight のリクエストを送信する。エラー文にも書いてあるように、ここでエラーが発生している。どうしても解決策が見出せなかった。もし axios、Django に詳しくて、わかる方がいたら、是非教えて欲しい。切に願っている。。。

またこれについても、解決策がわかったら、備忘録としてブログに書いておきたいと思う。

それでは、ステキな開発ライフを。