Vue.js(vue-cli)とFlaskを使って簡易アプリを作成する【後半 - サーバーサイド編】


![vueflask.png](https://qiita-image-store.s3.amazonaws.com/0/209160/4840dec5-8671-ab14-a6e8-8798a2a1f8a3.png)

どうもmiyachi（[@_38ch](https://twitter.com/_38ch)）です。
前回の記事
[Vue.js(vue-cli)とFlaskを使って簡易アプリを作成する【前半 - フロントエンド編】](https://qiita.com/mitch0807/items/2a93d93adbf6b5fc445c)
で、フロントエンドVue.js、サーバーサイドFlaskで作る簡易SPAのフロントエンドの紹介をしました。

今回はサーバーサイド（Flask側）の作り方を書いていきます。
なお、サーバーサイドにはPython3.6を使っています。


## backendディレクトリを作成

ディレクトリを切って、virtualenvで仮想環境を作ります。

```
$ mkdir backend
$ cd backend
$ virtualenv -p python3 venv
```

アクティベートします。

```
$ source venv/bin/activate
```

アクティベートが完了したら、Flaskをインストールします。

```
(venv) pip install Flask
```


ルートディレクトリ（frontend, backendと同じディレクトリ）に戻って、`run.py`を作成。

```
(venv) cd ..
(venv) touch run.py
```

中身はこんな感じ

```python
from flask import Flask, render_template
app = Flask(__name__,
            static_folder = "./dist/static",
            template_folder = "./dist")
@app.route('/')
def index():
    return render_template("index.html")
```

この`run.py`は一般的なFlask starterとは少し異なります。
どこが違うかというと、static_folderとtemplate_folderの部分。
templateファイルはfrontendから生成されたものを読み込む必要があるので、ここでパスを指定しています。

## Flaskサーバーを起動
以下を実行してあげると、Flaskサーバーが立ち上がります。

```
(venv) FLASK_APP=run.py FLASK_DEBUG=1 flask run
```
`localhost:5000`で確認できます。
最初のページはうまく表示されますが、`/about`に行くとNOT FOUNDになってしまいました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/209160/b6b8f6f6-d12f-4055-3489-41e73156eada.png)

`run.py`の設定を少し変えてあげましょう。

```python
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def catch_all(path):
    return render_template("index.html")
```
これで正常にaboutページが表示されるはず。

## APIエンドポイントを追加
1-100でランダムな数値を返すシンプルなAPIを作成します。
run.pyを以下のように修正します。

```python
from flask import Flask, render_template, jsonify
from random import *
app = Flask(__name__,
            static_folder = "./dist/static",
            template_folder = "./dist")

@app.route('/api/random')
def random_number():
    response = {
        'randomNumber': randint(1, 100)
    }
    return jsonify(response)

@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def catch_all(path):
    return render_template("index.html")
```

`'/api/random'`にリクエストを投げるとランダムな数値を以下のようなJSON形式で返してくれる。

```JSON
{
  "randomNumber": 36
}
```

ブラウザで [localhost:5000/api/random](http://localhost:5000/api/random) にアクセスしてテストできます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/209160/b5fb35b8-444d-10a2-62cb-5a10a9a98629.png)

サーバーサイドで動作が確認できたら、それをフロントから呼べるようにしましょう。
Home.vueを以下のように修正します。

```html:Home.vue
<template>
  <div>
    <p>Home page</p>
    <p>Random number from backend: {{ randomNumber }}</p>
    <button @click="getRandom">New random number</button>
  </div>
</template>

<script>
import axios from 'axios'

export default {
  data () {
    return {
      randomNumber: 0
    }
  },
  methods: {
    getRandom () {
      this.randomNumber = this.getRandomFromBackend()
    },
    getRandomFromBackend () {
      const path = `http://localhost:5000/api/random`
      axios.get(path)
      .then(response => {
        this.randomNumber = response.data.randomNumber
      })
      .catch(error => {
        console.log(error)
      })
  }
},
  created () {
    this.getRandom()
  }
}
</script>
```
大まかに説明すると、

* randomNumberは0で初期化されている。
* ボタンをクリックすると、`getRandom`が走る。
* getRandomが走ると、サーバーサイドにHTTPリクエストが送られ、ランダムな数値が返ってくる。
* randomNumberが更新されて、画面の値が変わる

という感じです。

backendにHTTPリクエストをなげるためには、[axios](https://github.com/axios/axios)というライブラリを使用します。

```
(venv) cd frontend
(venv) npm install --save axios
```

フロントを更新したので、再度 `npm run build` を実行して、
[localhost:5000](http://localhost:5000)から確認します。

![Jun-03-2018 1-02-26 PM.gif](https://qiita-image-store.s3.amazonaws.com/0/209160/9c8b8297-50cf-a343-23a2-67b79a9a36f4.gif)

正常にランダムな数値を取得するAPIを実装できました。
間違え、ご指摘などあれば、コメント欄にいただけると助かります。

## ソースコード
今回使用したコードはこちらにあげておきます。
もしお役にたったらStarいただけると嬉しいです。
https://github.com/miyachin/vue-flask-sample