EC2上でvue+expressの環境構築からAPI通信実装まで


# バックエンド
EC2インスタンス上でexpressを動かし、アプリケーションのbackend環境を構築する。
ここでは例としてアプリケーション`app`を作る

## 環境構築
__AMI:Ubuntu18.04、IP:x.x.x.x の場合__

```
$ sudo apt-get update
$ sudo apt-get install nodejs
$ sudo apt-get install npm
$ sudo update-alternatives --install /usr/bin/node node /usr/bin/nodejs 10
$ sudo npm install -g express
$ sudo npm install -g express-generator
$ express --view=pug app
$ cd app
$ npm install
$ npm start
> app@0.0.0 start /home/ubuntu/app
> node ./bin/www

```

__AMI:AmazonLinux2、IP:x.x.x.x の場合__

```
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash
$ . ~/.nvm/nvm.sh
$ nvm install node
$ sudo npm install -g express
$ sudo npm install -g express-generator
$ express --view=pug app
$ cd app
$ npm install
$ npm start
> app@0.0.0 start /home/ubuntu/app
> node ./bin/www

```

app/bin/wwwファイルでポート番号を指定できる。デフォルトではポート3000なので、ブラウザからアクセスして確認できる。

## APIの作成
app下に`index.js`を作成し、以下を記述。

```js:GET
// expressモジュールを読み込む
const express = require('express');

// expressアプリを生成する
const app = express();

// ルート（http://x.x.x.x:3000/）にアクセスしてきたときに「Hello」を返す
app.get('/', (req, res) => res.send('Hello'));

// ポート3000でサーバを立てる
app.listen(3000, () => console.log('Listening on port 3000'));
```

```js:POST
const express = require('express');
const bodyParser = require('body-parser');

const app = express();

// urlencodedとjsonは別々に初期化する
app.use(bodyParser.urlencoded({
    extended: true
}));
app.use(bodyParser.json());

app.post('/', function(req, res) {
    console.log(req.body);
    res.send('POST request to the homepage');
})

// ポート3000でサーバを立てる
app.listen(3000, () => console.log('Listening on port 3000'));
```

```
$ node index.js # 実行
Listening on port 3000

```

POSTメソッドでは、`body-parser`モジュールを用いてJSONで取得する。

## テスト
APIを叩く。

__ブラウザから(GET)__

`x.x.x.x:3000/`にアクセス。

__コンソールから(POST)__

```:POST
$ curl -X POST http://x.x.x.x:3000/ -H "Accept: application/json" -H "Content-type: application/json" -d '{ "name" : "tanaka" }'
```

# フロントエンド
## 環境構築
vueの環境構築は[こちら](https://qiita.com/Kept1994/items/3e0c6dcbb81c268f2f0b)の記事で書いた通りなので省略。

## フロントエンドから外部APIを叩く
デフォルトのAboutページにボタンをつくり、押したら非同期で外部API(httpbin)を叩くようにしてみる。

必要なものをいれる。

```
$ npm install axios vue-axios
```

```ts:main.ts
import Vue from 'vue'
import App from './App.vue'
import './registerServiceWorker'
import router from './router'
import store from './store'
import axios from 'axios' //追記
import VueAxios from 'vue-axios' //追記

Vue.config.productionTip = false
Vue.use(VueAxios, axios) //追記

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')
```

```vue:About.vue
<template>
  <div class="about">
    <h1>This is an about page</h1>
    <button @click="clicked" >Call API</button>
  </div>
</template>

<script lang="ts">
import { Component, Prop, Vue, Emit } from 'vue-property-decorator';

@Component
export default class About extends Vue {
  private clicked():void {
      this.axios.get('https://httpbin.org/get')
        .then((response) => {
          console.log(response);
        })
        .catch((e) => {
          console.log(e);
        });
  }
}
</script>
```

consoleにデータが来てれば成功。

## フロントエンドからexpressの内部APIを叩く
上のコードのURLをそのまま`http://x.x.x.x:3000`とすると以下のCORSのエラーが出る。

> Access to XMLHttpRequest at `http://x.x.x.x:3000/` from origin `http://x.x.x.x:8080` has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

### CORS(Cross-Origin Resource Sharing)とは何か？

> あるオリジンで動いている Web アプリケーションに対して、別のオリジンのサーバーへのアクセスをオリジン間 HTTP リクエストによって許可できる仕組み

引用) https://qiita.com/att55/items/2154a8aad8bf1409db2b

### どうしたらいいか

```js:index.js(CORS設定版)
const express = require('express');
const bodyParser = require('body-parser');
const app = express();

// CORSを許可する
app.use(function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    
    // OPTIONSでメソッドがバレないようにする。 (?)
    if ('OPTIONS' === req.method) {
        res.send(200)
    } else {
        next()
    }
});

app.use(bodyParser.urlencoded({
    extended: true
}));
app.use(bodyParser.json());

// POST
app.post('/', function(req, res) {
    console.log(req.body);
    res.send('Hello');
})
// GET
app.get('/', (req, res) => res.send('Hello'));

app.listen(3000, () => console.log('Listening on port 3000'));
```

上記のコードにしてフロントとバックを起動後、GETでコールしてみる。

```
$ cd app
$ node index.js #バックエンド起動
$ cd front
$ npm run serve #フロントエンド起動
```

コンソールに以下が得られたら成功。

```js
{data: "Hello", status: 200, statusText: "OK", headers: {…}, config: {…}, …}
```

# Appendix
EC2インスタンスに置いたvueファイルを更新するのにいちいち`scp`とか`vi`とかしてるのはだるすぎるのでVSCodeから直接SSHでいじるための方法。

VSCode拡張機能である`remote development`を入れるて以下に設定ファイルを書いておく。

```:.ssh/config
Host 任意の接続名   //configには何個も接続先とキーとかの情報を書けるのでそれらを区別する名前。
  HostName x.x.x.x
  User ec2-user
  Port 22
  IdentityFile /Users/xxx/.ssh/hoge.pem
```

___

参考

* Amazon Linux2 への nvm と npm のインストール  
https://qrunch.net/@dr3mms/entries/66FDqVNl3CoNvvCH
* Node.jsとExpressでWeb APIを作ってみよう  
https://sbfl.net/blog/2018/08/25/nodejs-express-webapi/
* Ubuntu でapt を使用してNode.js をインストールする3 つの方法(Ubuntu 15.04, Ubuntu 14.04.2 LTS)  
https://qiita.com/TsutomuNakamura/items/7a8362efefde6bc3c68b
* NodeJSでWebアプリケーション開発 – Express編  
https://avinton.com/en/academy-2/webapp-with-node-express/
* node.js + expressでPOSTを受け取る & POSTパラメータをJSONで取得する  
https://qiita.com/ktanaka117/items/596febd96a63ae1431f8
* vue-cliでaxiosを使用する(設定から使用方法まで)  
https://qiita.com/right1121/items/092ac7ff747e1c47b2b1
* なんとなく CORS がわかる...はもう終わりにする。  
https://qiita.com/att55/items/2154a8aad8bf1409db2b
* express.jsのcors対応  
https://qiita.com/chenglin/items/5e563e50d1c32dadf4c3
* expressにてCORSを許可する  
https://qiita.com/tomoya_ozawa/items/feca4ffc6217d585b037