【CORSエラー】初心者がJavaScriptから外部APIを叩いたときの話

初心者のつまづきポイントでした。
もし、あやまっている点や補足、実はこんな解決法がある、などありましたらどしどしご指摘ください！

# まずは「フロントから外部APIを叩いたときのCORSエラー」の結論

**3.の場合、フロントエンドからはほぼ無理！バックエンドから処理しましょう！**

フロント言語のJavascriptを使用して、ブラウザからAPIを利用するとき、大まかに以下の3つにわかれます。
1. APIキーを事前に取得し、アクセスできる（Google Map APIなど）
2. APIキー無しにアクセスできる（NHKの番組表取得など）
3. APIキー無しにアクセスできるが、フロントからではCORSエラーで **アクセスが難しい**（なんで！？）


初学者の私は丸1日格闘してしまいまして、 **「なぜ、CORSエラーが起こるか」** → **「そもそもAPIって？」** →APIを使用するための **「HTTPリクエストやHTTPレスポンス」** について調べたことをつらつら書いていきます。

さー行ってみよー！
* * *
### 目次
[そもそもAPIって](#-そもそもAPIって)
[HTTPリクエストやHTTPレスポンス](-#HTTPリクエストやHTTPレスポンス)
[なぜ、CORSエラーが起こるか](#-なぜ、CORSエラーが起こるか)
* * *
# そもそもAPIって
APIの解説や使い方について、正確な話や詳細な使い方は他の記事に譲るとして、 **「とりあえず、サクッと教えてくれよ！」** ということで少し整理したいと思います。
この説明から入るのもどうかと思いますが、APIとは「**Application Programming Interface**」の略です。つまり、アプリケーション内の **プログラムとプログラムを繋ぐもの**といったとこでしょうか。
APIの使われ方は、以下のように大きく2種類ありまして、私はまずここで引っかかってしまいました。
**※ここは今回のCORSエラーの根幹でもあったりするので、少し頭の片隅にでも**

:::note
* **今回のようにＷeb上で公開されている他者のAPI**
└皆さんが使いたいのはこちらかなと。
* **自分で書いたプログラムを別のプログラムから呼び出すためのAPI**
└そんなことあるの？って感じですが、サーバAに立てたプログラムからサーバBに立てたプログラムを **通信**して利用するというイメージです
:::

ま、 **「はよ、APIの使い方教えろや！」** ってところでしょうので、ざっくりとAPIが **通信を介してプログラムとプログラムをつなげる**ものという理解をしてもらえたところで、JavaScriptでの簡単な記述のひとつを紹介します。
今回は`fetch`というものを紹介します。
```javascript
const url = "https://hogehoge.com/hugahuga.json" // 指定するURLは各APIのドキュメントを読みましょう
fetch('url')
  .then(response => response.json())
  .then(data => console.log(data));
```
`fetch`を使うとこんなに簡単にAPIが使えたりするんですね！
少し解説・補足を。
**【解説】**
1行目、APIのURLを指定します。
2行目、`fetch`することで、後述のリクエストとレスポンスを処理します。
3行目、`Promise`の記述式である`.then`で`fetch`してきた`Promise`が完了したらそのデータを`response`という引数として取り込み、`response.json()`を処理します。
4行目、3行目と同様に3行目の処理が完了したらそのデータを`data`という引数として取り込み、`console.log(data)`という処理を行います。
**【補足】**
①`fetch`は`Promise`を返します。`Promise`の解説は[私のこちらの記事](https://qiita.com/rahydyn/items/dbdf62a0e6543a7fe48a)にまとめています。参考とした記事も多く載せているので、マスターしたい方はそちらで！
②アロー関数で記述していますが、普通の書き方でも問題ないです。

:::note
`Promise`についてざっくり。
* **Javascriptは非同期処理：** 原則上から下に処理が進みますが、DBからのgetなどのような時間がかかる処理は並行して進みます。 
* **`Promise`は処理の状態をもったオブジェクト：** 処理完了（resolved）などのような「状態」を持たせることができ、処理の順序をコントロールすることができるようになります。
* **`.then`は`Promise`が`resolved`という状態であるときに行う処理：** 条件分岐のようなもので、`Promise`の処理が失敗したときに行われる`.catch`というものもあったりします。
* **非同期処理関数を定義する`async`と演算子`await`：** `async`で定義した関数内で`await`を使用すると演算子の後に来るものが`resolved`になったら進みます。
:::

さて、サンプルとして郵便番号から住所を取得するzipcloudのAPIを試してみましょう。
指定するURLのパラメータ（URLの?以降の `zipcode=1000001`）などはAPIのドキュメントに使い方が書いてあるので、使いたいAPIのものを見てみましょう。（ちなみに郵便番号100-0001は皇居のある東京都千代田区千代田です）
```javascript
async function apiRequest() {
  const downloadUrl =
    "http://zipcloud.ibsnet.co.jp/api/search?zipcode=1000001";
  const response = await fetch(downloadUrl);
  const json = await response.json();
  const string = JSON.stringify(json);
  console.log(string);
}

apiRequest();
```
# HTTPリクエストやHTTPレスポンス
これまた、解説や使い方について、正確な話や詳細な使い方は他の記事に譲るとして、 **「とりあえず、サクッと教えてくれよ！」** ということで少し整理したいと思います。（1億万回こすられている話だと思いますが...）

正確性を無視して、インターネットを使うということは、おおまかに以下のような手順を踏んでいます。
**人間**「○○のサイトの情報をみたい！」
**ブラウザ**「では、○○のサイトのサーバにアクセスして情報を取ってきます」
**ブラウザ**「○○のサイトさん、情報ください！」
**サーバ**「良いですよ、どーぞ」
**ブラウザ**「人間さん、取ってきました！どーぞ」
**人間**「ふむふむなるほど」

上記で、 **「指定のサーバにアクセスして情報をとってこい」** という指示を書いているものが、（おそらく）あなたのJavaScriptのプログラムで、**「情報ください」** というものが **リクエスト**、**「良いですよ、どーぞ」** というものが **レスポンス** になります。

:::note
前項の`fetch`はリクエストの指示を出してレスポンスを受け取るところまでやります。
:::

リクエストとレスポンスの詳細については、大きく割愛します。
ざっくりと、HTTP（**超すげーテキストを送受信する規則**Hyper Text Transfer Protocol）リクエストでは、やりとりの種類を指定するHTTPリクエストメソッドがあります。（`GET`参照、`POST`新規作成、`PUT`更新、`DELETE`削除など）
**リクエスト**には、このようなメソッドや、キャッシュの扱い、`POST`時などの送信内容などなど様々な指示/情報が含まれています。
**レスポンス**には、リクエストされた結果や返されるデータなどが含まれます。
`fetch`でのリクエストの指示内容は以下のように第二引数に記述することができます。それぞれの詳細はドキュメントをご参照ください。なお、コメントの*印はデフォルトの値です。
（前項ではこの第二引数を指定していなかったので、`GET`メソッドのように、**デフォルトで処理されていた** ということですね！）
```javascript
fetch(url, {
  method: "GET", // *GET, POST, PUT, DELETE, etc. 
  mode: "cors", // no-cors, *cors, same-origin
  cache: "default", // *default, no-cache, reload, force-cache, only-if-cached
  credentials: "same-origin", // include, *same-origin, omit
  headers: {
    "Content-Type": "application/json",
  },

});
```

`fetch`のドキュメントは以下

https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

# なぜ、CORSエラーが起こるか
ようやく、本題です。
まず、CORSとはなんだ。
CORS（**オリジン間のリソース共有**Cross-Origin Resource Sharing）、**オリジン**というものは、ドメインと変換して良いかと思います。（正確にはドメイン+プロトコル+ポート番号）
例えば、以下のような異なるドメインからアクセスしたときが対象となります。
1. **あなた** のローカルサーバから**外部**APIのサーバにアクセスするとき
2. **あなた**のサーバAから**あなた**のサーバBにアクセスするとき

:::note alert
ネットにあるCORSエラーの対処法(**=解決できるもの**)はほとんどが**レスポンス側を管理できる**2つ目の場合です。
つまり、今回の場合(フロントからJavaScriptで**外部**APIをたたく場合）はほぼ無理という結論に落ち着きました。
└**使いたいときはバックエンドから処理しましょう**
理由は後述します。
:::

異なるオリジンからの**不正アクセスを禁止する**という目的で、このCORSの制限があるということです。
そうなると、この世のほとんどの通信は異なるオリジンからのアクセスということになってしまうので、**「○○というオリジンからのアクセスは許可するよ」** という指示が必要になります。
前項で説明した**リクエスト**にはオリジンというものが含まれております。**レスポンス**には「リクエストされたオリジンからのアクセスを許可するよ」という記述を含むことができます。
※ブラウザの検証モードでNetworkというタブを開くとリクエスト&レスポンスの中身を見ることができます。

### 結論の理由
つまり、**「CORSでのアクセスを許可するかどうか」** は **「レスポンス側に委ねられている」** のであって、**「リクエスト側である私たちではどうしようもない」** ということなのです。（当たり前っちゃ当たり前ですが...）

ネット上にあるCORSエラーの対処法のほとんどは上記の2.「あなたのサーバAからあなたのサーバBにアクセスするとき」のものであり、**レスポンス側の設定を変えましょう**というものがほとんどでした。
なお、CORS回避するためのChrome拡張機能などありますが、本番環境にアップロードすると考えている場合は、普通にバックエンドから処理しましょう。

とほほ...