Vue(Vuex, axios) + ExpressでRESTful APIに超入門する

株式会社diffeasyでフロントエンドエンジニアをやっているカオルコです！
普段、バックエンドの方が用意してくれたデータをひたすら表示するお仕事をしていますが、APIリクエスト投げた向こう側の世界、のぞいてみたくなりませんか・・・！？

というわけで、超シンプルなRESTful APIをJavaScriptで実装して、データの流れを追っかけてみました😎
言葉遣い、用語の使い間違いがあればそっと教えて下さいmm

この記事は [diffeasy Advent Calendar 2018](https://qiita.com/advent-calendar/2018/diffeasy) 15日目の記事です。

[リポジトリ](https://github.com/s-kaoruko/practice-express)

- [前提](#前提)
- [フロントエンドの構築](#フロントエンドの構築)
  - [インストール](#インストール)
  - [Storeを構成する](#storeを構成する)
  - [リクエスト投げ投げモジュールを作成](#リクエスト投げ投げモジュールを作成)
- [バックエンドの構築](#バックエンドの構築)
  - [インストール](#インストール)
  - [自動生成されたファイルを眺めてみる](#自動生成されたファイルを眺めてみる)
  - [APIを実装してみる](#apiを実装してみる)
- [VueからExpressにリクエストを投げ、レスポンスデータを表示する](#vueからexpressにリクエストを投げレスポンスデータを表示する)
  - [CORS（オリジン間リソース共有）](#corsオリジン間リソース共有)
- [最後に](#最後に)

# 前提
- npm(6.4.1)
- vue/cli(3.1.1)
- Vue(2.5.17)
- Vuex
- axios
- Express(4.16.0)

▼フォルダ構成

```
practice-express/
   ├ client/
   │      ├ package.json
   │      └ ...
   └ server/
          ├ package.json
          └ ...
```
client/配下にVueのソースを、server/配下にExpressのソースを置いていきます。

# フロントエンドの構築

## インストール

```bash
# vue/cliをインストールしていない場合
npm install -g @vue/cli

cd ~/workspace/practice-express
vue create client

# vue createが終了したら
cd client
npm insall
npm install axios --save

# 起動後、`http://localhost:8080/`にアクセス
npm run serve

```

Hello, Vue🎉

## Storeを構成する

```
practice-express/
   ├ client/
   │      ├ package.json
   │      ├ ...
   │      ├ store/
   │      │     ├ modules/
   │      │     │       ├ test.js
   │      │     │       └ ...
   │      │     ├ index.js
   │      │     └ mutation.js 
   │      └ ...

```

今回はstore/index.jsにVuexのインスタンス定義をし、store/modules/配下にapiを投げるモジュールを書いていきます。
store/mutation.jsには定数を定義しています。（→[ミューテーション・タイプに定数を使用する](https://vuex.vuejs.org/ja/guide/mutations.html#%E3%83%9F%E3%83%A5%E3%83%BC%E3%83%86%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%BB%E3%82%BF%E3%82%A4%E3%83%97%E3%81%AB%E5%AE%9A%E6%95%B0%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B)）

## リクエスト投げ投げモジュールを作成
```javascript:client/store/modules/test.js
import axios from 'axios'
const API_URI = 'http://localhost:3000'

import {
  TEST, TEST_FAILURE, TEST_SUCCESS
} from '../mutation'

export default {
  state: {
    test: null
  },
  getters: {
    test: state => {
      return state.test
    }
  },
  actions: {
    [TEST] (params = null) {
      let data = {}
      if (params) data = params.data
      axios.get(API_URI + '/test', {data: data})
      .then(res => {
        if (res.status === 200 || res.status === 304) {
          if (res.data.error) {
            this.commit(TEST_FAILURE, res.data)
          } else {
            this.commit(TEST_SUCCESS, {data: res.data, callback: params.callback})
          }
        }
      })
      .catch(error => {
        throw error
      })
    }
  },
  mutations: {
    [TEST_FAILURE] (state, data) {
      console.log('TEST_FAILURE')
    },
    [TEST_SUCCESS] (state, data) {
      console.log('TEST_SUCCESS')
      state.test = data.data.text
      if (data.callback) {
        data.callback(data.data)
      }
    }
  }
}
```

これで`this.$store.dispatch('TEST')`とコンポーネント内で呼ぶと、`http://localhost:3000/test`へgetリクエストが発生するようになりました！

# バックエンドの構築

## インストール
```bash
cd ~/workspace/practice-express

mkdir app
cd app

npm init
npm install express-generator -g

# expressをviewエンジンなしでインストール
express --no-view

# 起動後、`http://localhost:3000/`にアクセス
DEBUG=myapp:* npm start
```

Hello, Express🎉

## 自動生成されたファイルを眺めてみる

```
practice-express/
   └ server/
          ├ ...
          ├ package.json
          ├ app.js
          ├ bin/
          │   └ www
          ├ public/
          └ routes/
              └ index.js
```

**bin**
環境立ち上げ時に実行される処理を置くところ

**public/**
静的ファイルを置くところ
`http://localhost:3000/`はここのindexを表示するみたい

**routes/**
ルーティング処理を置くところ
クライアントからの要求に対しての処理群

**app.js**
メインファイル
共通する設定や処理はここに書く

## APIを実装してみる
`http://localhost:3000/test`へGETリクエストが来たら、`{"text":"success!"}`を返すAPIを作ります。

まずは、ルーティングを作成。

```javascript:app.js
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var testRouter = require('./routes/test'); // ←追加

var app = express();

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/test', testRouter); // ←追加

module.exports = app;

```

実際に処理をするファイルを作成

```javascript:/routes/test.js
var express = require('express')
var router = express.Router()

router.get('/', function(req, res, next) {
  const data = {
    text: 'success!'
  }
  res.send(data)
})

module.exports = router

```

expressを再起動し、`http://localhost:3000/test`へアクセスしてみます。
`{"text":"success!"}`と表示されたら大成功🎉

# VueからExpressにリクエストを投げ、レスポンスデータを表示する
フロントエンドとバックエンド、それぞれのピースはできたので、組み合わせてみます。
buttonをクリックするとtextareaにレスポンスデータが表示されるコンポーネントを作成しました。

```vuejs

<template>
  <div>
    <button type="button" @click="click">get</button>
    <textarea v-model="test"></textarea>
  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  computed: {
    test () {
      return this.$store.getters.test
    }
  },
  methods: {
    click () {
      this.$store.dispatch('TEST')
    }
  }
};
</script>
```

![スクリーンショット 2018-12-15 17.37.14.png](https://qiita-image-store.s3.amazonaws.com/0/278202/5fd636d9-fb09-0c71-1e9b-5cfab4902479.png)

![スクリーンショット 2018-12-15 17.36.56.png](https://qiita-image-store.s3.amazonaws.com/0/278202/4a1a4285-18d8-43d2-3f78-1040efd166f8.png)

🙃

## CORS（オリジン間リソース共有）

異なるドメインに対してアクセスを行ったとき、許可なくレスポンスデータを読み込むことができない仕組みです。
スキーム・ホスト・ポート番号で評価されます。
（→ https://developer.mozilla.org/ja/docs/Web/HTTP/CORS）

今回は`http://localhost:8080`と`http://localhost:3000`でポートを分けて実装したので怒られたようです。

Express側で、`http://localhost:8080`からのレスポンスデータ読み込みを許可します。

```javascript:/server/app.js
// CORSを許可する処理を追加
app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "http://localhost:8080");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next()
})

```

![スクリーンショット 2018-12-15 17.47.15.png](https://qiita-image-store.s3.amazonaws.com/0/278202/556b5acd-6a0d-722a-e817-8205584b2ff5.png)

🎉🎉🎉

# 最後に
マークアップエンジニア出身なこともあり、JavaScript以外の言語に馴染みがないので、APIを作って学んでみるのにExpressはとてもいいなと思いました！（何をやってるか読解するのにストレスがあまりない）
今回はGETのみでしたが、次回はCRUD制覇したり、認証まわりの実装にチャレンジしたりしたいと思います！

Have a nice RESTful！