技術的な話

Vue.js + Fast APIでアプリを構築してみる

大規模なアプリケーションでなければなるべくコンパクトで軽いものを選びたい私です。

今回はVue.jsにaxiosを導入してHTTPアクセス出来るようにし、FastAPIで手軽にバックエンドを実装します。簡単・シンプルなフロント+バックエンドアプリケーションを構築する方法を紹介します。

ちなみに本記事は前回の記事のソースをベースに変更を加えていきます。
気になる方は下記を参照してみてください。

作成する環境

  • Windows10
  • WSL2(Ubuntu) + Docker
  • フロントエンド:Vue.js 3 + axios
  • バックエンド:Fast API(Python)

Fast APIとは

Python製のWebフレームワークです。
同じPythonで他のWebフレームワーク(FlaskやDjangoなど)と比較するとこれが軽くて使いやすいと思っています。大規模システムになるとまた違ってくるとは思いますが。
以下にFast APIの特徴を記載します。

  • 高速(Node.js / Go並みの高パフォーマンス)
  • コードが短く記載でき、バグも少なくなる
  • 自動でドキュメントが出来る(swagger)

個人的には自動でswaggerが作成されるのが有難いです。
ドキュメントを作成しなくても良いので(違うか…)。

公式ドキュメントは以下を参照してください。

https://fastapi.tiangolo.com/ja/

Fast APIを活用した他記事

APIを叩いてバッチ処理みたいなのを動かしたい場合はこちら。


FastAPIのPydanticを使って基底クラスを継承させたい場合はこちら。

今回のゴール

  • Vue.jsからFast APIの呼び出し

環境作成方法

今回はフロントエンド、バックエンドそれぞれでDockerを作成し相互に連携させます。

フロントエンド用Dockerファイルの作成

フロントエンドはVue.jsなので前回とほとんど変わりません。

FROM node:14.16.0-stretch-slim

ENV LANG C.UTF-8
ENV TZ Asia/Tokyo

RUN yarn global add @vue/cli @vue/cli-init

WORKDIR /app

バックエンド用Dockerファイルの作成

バックエンドはPythonイメージを使用して必要なソフトウェアをインストールします。
必要ソフトウェアはrequirements.txtに記載します。

FROM python:3.9.2-buster

ENV LANG C.UTF-8
ENV TZ Asia/Tokyo

# pip installs
COPY ./docker/back/requirements.txt requirements.txt
RUN pip install -r requirements.txt

WORKDIR /app

fastapi
uvicorn[standard]

composeファイルの用意

不要な人もいるかもしれませんが、私はいつもdocker-composeを使用しているので準備します。

version: '3.7'

services:
  front:
    container_name: front
    build:
      context: .
      dockerfile: ./docker/front/Dockerfile
    ports:
      - 80:8080
    restart: always
    tty: true
    volumes:
      - ./app/my-vue-app:/app
    
  back:
    container_name: back
    build:
      context: .
      dockerfile: ./docker/back/Dockerfile
    ports:
      - 3000:3000
    restart: always
    tty: true
    volumes:
      - ./app/python:/app

ビルドと起動、フロントエンド側のDockerにログイン

イメージを作成して起動します。
その後フロントエンド側のDockerへログインを行い、Vueアプリケーションを起動します。

# docker build and launch
$ sudo docker-compose up -d --build
# docker login
$ sudo docker-compose exec front bash

# vue app launch
$ yarn serve

フロントエンド側にaxiosを導入

ajax通信を行いたいのでaxiosをインストールします。

$ yarn add axios vue-axios

とりあえず通信させたいので今回は赤枠で囲んだ部分をバックエンド側から取得して表示してみようと思います。

axiosのベース処理の作成と実装

baseURLの設定やアクセスの際のinterceptorをここに書いています。
共通でさせたい処理(例えばリクエスト送信時のヘッダー設定など)があればここに書いていきます。

export let axios;

export default {
    install(app) {
        // base url
        app.config.globalProperties.$http.defaults.baseURL = 'http://localhost:3000'
        
        // request interceptor
        app.config.globalProperties.$http.interceptors.request.use(config => {
            config.headers.Accept = 'application/json';
            return config;
        })

        // response interceptor
        app.config.globalProperties.$http.interceptors.response.use(response => {
            
            return response;
        }, function(error) {
            
            return Promise.reject(error);
        })

        axios = app.config.globalProperties.$http;
    },
    get(url) {
        return axios.get(url);
    },
    post(url) {
        return axios.post(url);
    }
}

作成したaxiosのベースをmain.jsで読み込む設定をします。

import { createApp } from 'vue'
import App from './App.vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
import AxiosPlugin from '@/plugins/axios/index'

createApp(App).use(VueAxios, axios)
              .use(AxiosPlugin)
              .mount('#app')

実際のコンポーネントにaxiosの処理を書いていきます。
先ほど作成したベース処理を読み込んで使用します。

methods部にはaxiosベース処理を使用したget_hello()を定義しています。
mounted部ではmethodsに定義した処理を書いてデータ受信時の処理を書いています。
その後、messageに値を設定して画面上に値を表示させています。
アクセスできずに値が設定されない場合は何も表示されません。

<template>
  <section class="hero is-medium">
    <div class="hero-body has-text-centered">
      <h1 class="title is-2">{{ message }}</h1>
      <div id="hero-input-group" class="field has-addons has-addons-centered">
        <div class="control">
          <input class="input is-medium" type="text" placeholder="Search..." />
        </div>
        <div class="control">
          <a class="button is-medium is-primary">
            <i class="fal fa-search"></i>
          </a>
        </div>
      </div>
    </div>
  </section>
</template>

<script>
import { axios } from '@/plugins/axios/index'

export default {
    components: {
    },
    data() {
      return {
        message : ''
      }
    },
    mounted() {
      this.get_hello().then((response) =>{
        this.message = response.data.message
      })
    },
    methods: {
      get_hello() {
        return axios.get('api/hello')
      }
    },
}
</script>
<style lang="scss" scoped>

</style>

ここまででコンパイルが成功して画面が表示されればOKです。
現時点ではバックエンド側は実装されていないので何も表示されません。
ちなみにVue.jsの起動は下記コマンドで実施します。

$ yarn serve

今回はhttp://<baseURL>/api/helloにアクセスしてデータを取得することを想定しています。
次以降でバックエンド側の処理を作成していきます。

バックエンド側処理の実装

フロントエンド側が想定しているURLになるように作成していきます。
取り急ぎHello Worldと返す簡単処理になっています。

from fastapi import FastAPI

app = FastAPI()

@app.get("/api/hello")
def index():
    return {"message": "Hello World"}

FastAPIの起動方法は下記コマンドを実行します。

$ uvicorn server:app --host 0.0.0.0 --port 3000 --reload

この状態でブラウザのURLバーにhttp://localhost:3000/api/helloを入力してアクセスすると下記のように値が返ってくることが分かります。
これでバックエンド側の処理も問題無さそうなのでVue側から動作を見てみます。

ちなみにこの状態でhttp://localhost:3000/docsを入力してアクセスするとswaggerが開きます。
先ほど作成したAPI情報が表示されます。

フロントエンド側とバックエンド側処理の動作確認

この状態だと「No 'Access-Control-Allow-Origin' header is present on the requested resource.」と表示されエラーが発生します。
Originが違うようで弾かれています。

バックエンド側でOriginが異なっても許可するように設定します。

from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=['*'],
    allow_credentials=True,
    allow_methods=['*'],
    allow_headers=['*']
)

@app.get("/api/hello")
def index():
    return {"message": "Hello World"}

この状態で再度画面を表示してみます。
以下のように「Hello World」が表示されました。

まとめ

フロントエンド(Vue.js + axios)とバックエンド(FastAPI※Python)の簡単なアプリケーションを実装しました。
axiosは一癖ありますがVueでHTTPアクセスをする際によく使用されます。
またFastAPIは物凄く簡単に実装でき、コードを書くと自動的にSwaggerも作成されるのでドキュメントとはもうおさらば!と言っても過言ではないかもしれません。
これを応用してバックエンドでデータベースアクセスをしたり、色々な処理が実装出来るかと思います。

-技術的な話
-, , , , ,