Web API フロント側

Web APIを使ってCRUD操作していきます。


### CRUDとHTTPメソッドの対応

|CRUD|HTTPメソッド|意味|
|---|---|---|
|Create|POST|作成|
|Reading|GET|取得|
|Update|PATCH|更新|
|Delete|DELETE|削除|
※PUTは今回扱いません

### 送信データの形式

送信は3通りの形式がありますが、本記事ではJSON形式で進めます。

- JSON形式(application/json)
- フォーム形式(multipart/form-data)
- application/x-www-form-urlencoded形式


## 1.コードを書かないで体験してみる
最初はコードを書かずに、json-serverとPostmanを使ってWeb APIでのCRUD操作のシミュレーションをしてみます。


||本番|本章|
|---|---|---|
|フロント|JavaScript|Postman|
|サーバー|サーバーサイドの言語(PHP、Pubyなど)|json-server|

### JSONファイルの作成
サンプルとして、文豪と著書のリストをJSONで作成します。
novelsという部分がAPIアクセス時のエンドポイントの文字列になります。
(route.jsonを使うと別の文字列に変更することも可能です。)

任意のフォルダの中にdb.jsonファイルを作成します。

```db.json
{
  "novels": [
    {
      "id": 1,
      "title": "坊っちゃん",
      "author": "夏目漱石"
    },
    {
      "id": 2,
      "title": "羅生門",
      "author": "芥川龍之介"
    },
    {
      "id": 3,
      "title": "走れメロス",
      "author": "太宰治"
    },
    {
      "id": 4,
      "title": "雪国",
      "author": "川端康成"
    }
  ]
}
```


### json-server

#### インストール

```:terminal
npm install -g json-server
```

#### 起動

```:terminal
json-server --watch db.json
```

#### 動作確認

下記URLにブラウザでアクセスすると、先程作成したdb.jsonの内容が表示されます。
http://localhost:3000/novels
(URLは環境によって異なる場合があります)

### Postman

#### インストール

下記URLからダウンロードして、インストールします。
https://www.postman.com/

#### 起動
インストールができたら起動します。

### 操作してみる

#### GET
db.jsonのnovelsの内容を取得してみます。

![postman_get.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451263/793fa44f-e4e0-f9e4-8ccc-c38ff558eec4.jpeg)


①
1.methodにGETを指定
2.URL欄に　http://localhost:3000/novels/　を入力
3.「Send」ボタンを押す

②
db.jsonの内容が表示されます。

#### POST
db.jsonのnovelsに新規のデータを追加してみます。

![postman_post.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451263/94f1c631-c809-0de2-7673-2d2b50ddcab0.jpeg)



①
1.methodにPOSTを指定
2.URL欄に　http://localhost:3000/novels/　を入力

②
Bodyタブを開いて、rawを選択
入力欄に下記を入力

```:json
{
  "title": "細雪", 
  "author": "谷崎潤一郎" 
}
```

「Send」ボタンを押す

③
登録したデータが表示されます。

#### DELETE
db.jsonのnovelsから指定したデータを削除してみます。
(今回はID4を削除)

![postman_delete.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451263/97f53936-6c86-e83f-8f3f-8ce59a140daa.jpeg)


①
1.methodにDELETEを指定
2.URL欄に　http://localhost:3000/novels/4　を入力
3.「Send」ボタンを押す

データが削除されます。

#### PATCH
db.jsonのnovelsから指定したデータを更新してみます。
(今回はID1を更新)

![postman_patch.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451263/f15e6513-ac43-629a-9e1c-0c625bde3470.jpeg)


①
1.methodにPATCHを指定
2.URL欄に　http://localhost:3000/novels/1　を入力

②
Bodyタブを開いて、rawを選択
入力欄に下記を入力

```:json
{
  "title": "吾輩は猫である"
}
```

「Send」ボタンを押す

③
更新したデータが表示されます。
指定していないauthorはそのまま変更されていません。



## 2.JavaScriptでCRUD
Postmanを使っていた部分をJavaScriptで行っていきます。
(Postmanはもう不要なので終了して良いです。)

db.jsonに変更を加えたので、初期の状態に戻しておいてください。

fetch(Promise、async await)、axiosの3つの方法で操作してみます。



||本番|本章|
|---|---|---|
|フロント|JavaScript|JavaScript|
|サーバー|サーバーサイドの言語(PHP、Pubyなど)|json-server|




### fetch(promise)の場合

#### GET
db.jsonのnovelsの全データを取得してみます。

```:js
fetch("http://localhost:3000/novels", {
  //取得なのでGETを指定(GETは省略可能)
  method: "GET",
})
.then((res) => {
  //responseオブジェクトからJSONオブジェクトを抽出
  return res.json();
})
.then((json) => {
  //取得できたJSONオブジェクトをJSON文字列化して表示
  console.log(JSON.stringify(json));
});
```

#### POST
谷崎潤一郎の「細雪」を追加してみます。

```:js 
//postするJSONオブジェクト作成
const json = { title: "細雪", author: "谷崎潤一郎" };

fetch("http://localhost:3000/novels", {
  //新規追加なのでmethodにPOSTを指定
  method: "POST",
 
  //bodyにJSONオブジェクトをJSON文字列化して指定
  body: JSON.stringify(json),
 
  //headersで"Content-Type": "application/json"を指定
  headers: { "Content-Type": "application/json" },
});
``` 
 

#### DELETE
ID4を指定して、川端康成の「雪国」を削除してみます。

```:js
fetch("http://localhost:3000/novels/4", {
  //削除なのでmethodにDELETEを指定
  method: "DELETE",
});
``` 

#### PATCH
ID1を指定して、夏目漱石の「坊っちゃん」を、夏目漱石は変更せず「吾輩は猫である」に変更してみます。

```:js
//PATCHに使うJSONオブジェクト作成
const json = { title: "吾輩は猫である" };

fetch("http://localhost:3000/novels/1", {
  //更新なのでmethodにPATCHを指定(PATCHを小文字にすると動作しないので必ず大文字にする)
  method: "PATCH",

  //bodyにJSONオブジェクトをJSON文字列化して指定
  body: JSON.stringify(json),

  //headersで"Content-Type": "application/json"を指定
  headers: { "Content-Type": "application/json" },
});
```


### fetch(async await)の場合

#### GET
db.jsonのnovelsの全データを取得してみます。

```:js
getData = async () => {
  const res = await fetch("http://localhost:3000/novels", {
    method: "GET",
  });

  //responseオブジェクトからJSONオブジェクトを抽出
  const json = await res.json();

  //取得できたJSONオブジェクトをJSON文字列化して表示
  console.log(JSON.stringify(json));
};

getData();
```

#### POST
谷崎潤一郎の「細雪」を追加してみます。

```:js
//postするJSONオブジェクト作成
const json = { title: "細雪", author: "谷崎潤一郎" };

postData = async () => {
  await fetch("http://localhost:3000/novels", {
    //新規追加なのでmethodにPOSTを指定
    method: "POST",

    //bodyにJSONオブジェクトをJSON文字列化して指定
    body: JSON.stringify(json),

    //headersで"Content-Type": "application/json"を指定
    headers: { "Content-Type": "application/json" },
  });
};

postData();
```

#### DELETE
ID4を指定して、川端康成の「雪国」を削除してみます。

```:js
deleteData = async () => {
  await fetch("http://localhost:3000/novels/4", {
    //削除なのでmethodにDELETEを指定
    method: "DELETE",
  });
};

deleteData();
```

#### PATCH
ID1を指定して、夏目漱石の「坊っちゃん」を、夏目漱石は変更せず「吾輩は猫である」に変更してみます。

```:js
//PATCHに使うJSONオブジェクト作成
const json = { title: "吾輩は猫である" };

patchData = async () => {
  await fetch("http://localhost:3000/novels/1", {
    //更新なのでmethodにpatchを指定(PATCHを小文字にすると動作しないので必ず大文字にする)
    method: "PATCH",

    //bodyにJSONオブジェクトをJSON文字列化して指定
    body: JSON.stringify(json),

    //headersで"Content-Type": "application/json"を指定
    headers: { "Content-Type": "application/json" },
  });
};

patchData();
``` 




### axiosの場合
ライブラリの導入が必要ですが、fetchと違いIE11にも対応可能で、「Body.json()が不要」など、記述も少なくて済みます。

npmでのインストール

```:terminal
npm install axios
```

CDNを使用する

```:html
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
```


### GET
db.jsonのnovelsの全データを取得してみます。

```:js 
axios.get("http://localhost:3000/novels").then((res) => {
  console.log(res.data);
});
``` 

#### POST
谷崎潤一郎の「細雪」を追加してみます。

```:js
//postに使うJSONオブジェクト作成
const json = { title: "細雪", author: "谷崎潤一郎" };

axios.post("http://localhost:3000/novels", json);
```

### DELETE
ID4を指定して、川端康成の「雪国」を削除してみます。

```:js
axios.delete("http://localhost:3000/novels/4");
``` 

#### PATCH
ID1を指定して、夏目漱石の「坊っちゃん」を、夏目漱石は変更せず「吾輩は猫である」に変更してみます。

```:js
//patchするJSONオブジェクト作成
const json = { title: "吾輩は猫である" };
 
axios.patch("http://localhost:3000/novels/1", json);
``` 






## 3.ブラウザ上の操作でデータを更新できるようにしてみる


||本番|本章|
|---|---|---|
|フロント|JavaScript|JavaScript|
|サーバー|サーバーサイドの言語(PHP、Pubyなど)|json-server|



```:ファイル構成
 server
 ├ db.json
 ├ package-lock.json
 └ node_modules/
            
 front
 ├ index.html
 ├ main.js
 └ style.css
```
      
サーバー側は前章までと同様json-serverを使います。
フロント側はVS Codeでlive serverを起動するのが簡単だと思います。

### 画面

初期表示
![gui1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451263/3875a45a-c082-6abb-d95b-8219f3ae4cee.png)


新規追加
![gui2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451263/7b28c832-ca85-9b4f-8ada-47b4678c44b0.png)




新規追加後
![gui3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451263/05ab2f40-cf6c-675d-5a39-c04cb2062ce4.png)



ID4削除
![gui4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451263/19bca4ee-e1c3-914b-10b8-1c5050213380.png)



ID4削除後
![gui5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451263/6e1d7b47-a3e8-9ec2-648b-93de26d92b59.png)




ID1更新
![gui6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451263/263e7b6a-198b-96e9-4b07-346b6ded6cf2.png)

ID1更新後
![gui7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451263/56ad3097-a4e4-a5f7-3ecf-bafd7d49cbb0.png)



### リポジトリ
ソースは長くなってしまったのでこちらに置きました。

https://github.com/takeshisakuma/webapi-front










## fetch APIのheaders以外のオプション
今回は扱いませんでしたが、fetch APIにはheaders以外のオプションもあります。



### modeオプション
リクエストのモードを決めます。

|設定値|意味|
|---|---|
|same-origin|同一オリジンのみ許可 <br> クロスオリジンは接続エラー|
|cors(デフォルト)|クロスオリジンを許可 <br> レスポンスは表示できるヘッダが Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragmaに限定される|
|no-cors|同一オリジンのみ許可 <br> クロスオリジンは接続ができないがエラーにならない(空のレスポンスが返される)|



### credentialsオプション
クッキーなどのクレデンシャル(認証情報)を送る場合に使用します。


|設定値|意味|
|---|---|
|omit|リクエストにクレデンシャルを含まない|
|same-origin(デフォルト)|同一オリジンの場合だけクレデンシャルを送る|
|include|クレデンシャルの入ったリクエストを送る|


### redirectオプション




|設定値|意味|
|---|---|
|manual|HTTPリダイレクトには従わないが、res.url が新しい URL になり、res.redirected が true になる <br> 必要に応じて新しい URL へ手動でリダイレクトできる|
|follow(デフォルト)|HTTPリダイレクトに従う|
|error|HTTPリダイレクトの場合にはエラーになる|


### referrer オプション
現在のオリジン内の任意の Referer を設定するか、それを無効にすることができます。
referer を送らないようにするには、空文字をセットします。


### referrerPolicy オプション
Referer に対する一般的なルールを設定します。


|設定値|意味|
|---|---|
|no-referrer|Referer ヘッダー全体が省略される(リクエストとともにリファラー情報が送られない)|
|no-referrer-when-downgrade(デフォルト)|セキュリティ水準が低下する場合(HTTPS→HTTP) は、リファラー(URL のオリジン、パス、クエリ文字列)は送信されない|
|origin|文書のオリジンのみがリファラーとして送信される|
|origin-when-cross-origin|同一オリジン間ではオリジン、パス、クエリ文字列を送信する　クロスオリジンの場合は文書のオリジンのみ送信|
|same-origin|同一オリジンにはリファラーが送信されるが、クロスオリジンの場合はリファラー情報が送信されない|
|strict-origin|セキュリティ水準が低下する場合(HTTPS→HTTP) は、リファラー(URLのオリジン)は送信されない|
|strict-origin-when-cross-origin|同一オリジン間でリクエストを行う場合はオリジン、パス、クエリ文字列を送信 <br> クロスオリジンの場合、プロトコルのセキュリティ水準が同じ場合はオリジンを送信するが、安全性の劣る送信先 (HTTPS→HTTP) にはヘッダーを送信しない|
|unsafe-url|セキュリティに関係なく、どのリクエストを行った場合でも、オリジン、パス、クエリ文字列を送信|



### cacheオプション
HTTP キャッシュの使い方を調整できます。

|設定値|意味|
|---|---|
|default(デフォルト)|標準の HTTP キャッシュのルールとヘッダを使用|
|no-cache|キャッシュされているレスポンスがある場合は条件付きリクエストを作成 <br> 無い場合は通常のリクエストを作成(レスポンスで HTTP キャッシュを埋める)|
|reload|HTTPキャッシュから結果を取得しないが、キャッシュにレスポンスを埋める(レスポンスヘッダが許可している場合)|
|force-cache|HTTPキャッシュからのレスポンスを使用　HTTPキャッシュにレスポンスが無い場合は、通常の HTTP リクエストを行い通常通りの振る舞いをする|
|only-if-cached|HTTPキャッシュからのレスポンスを使用　HTTPキャッシュにレスポンスが無い場合は、エラー(mode が "same-origin" の場合にのみ動作)|
|no-store|HTTPキャッシュを完全に無視|


※下記のヘッダを設定している場合は、no-storeがデフォルト値になります。

- If-Modified-Since
- If-None-Match
- If-Unmodified-Since
- If-Match
If-Range



### integrity オプション
レスポンスが既知のチェックサムに一致するかを確認することができます。

サポートされているハッシュ関数

- SHA-256
- SHA-384
- SHA-512



### keepalive オプション
リクエストがページよりも長生きする可能性があること意味します。

|設定値|意味|
|---|---|
|true|有効|
|false|無効|