0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

大急ぎでAPI+SPA構成のアプリを立ち上げる(Nuxt.js&簡易認証編)

Posted at

前回の記事

大急ぎでAPI+SPA構成のアプリを立ち上げる(Spring Boot&Heroku編)

あらすじ

こんなの(下図)を明日までに準備することになり、バックエンドとDBの構築までを終わらせました。
今回はフロントエンドを何とかします。

Picture2.png

Nuxt.jsについて

Nuxt.jsはフロントエンドアプリを構築するためのフレームワークです。
Vue.jsの知識は必要になりますが、最初からガッツリ出来上がったものが手に入るので、急いでいる時にはもってこいです。

環境

OS

  • macOS Catalina

ツール・ソフトウェア

  • Node.js v10.15.3
  • npm v6.14.8
  • WebStorm 2020.2.4

(※バックエンド・Heroku関連は前回記事に記載)

雛形を作る

Get Startedに従い、雛形を作ります。
このコマンドを実行するとカレントディレクトリにソースが生成されるため、あらかじめ開発用のディレクトリに移動しておきます。

npx create-nuxt-app my-rapid-app-front

生成中にいろいろ聞かれますが、落ち着いて答えていきましょう。

■Project name, Project description, Author name

何でもOKです。適当に入れます。

■package manager

yarn か npm の2択です。
長い目で見ればyarnがオススメですが、未インストールならnpmでもOKかと。

■UI framework, custom server framework, Nuxt.js modules, linting tools, test framework

たくさんあって迷っちゃいますね。全部Noneです。
Lintツールくらいは入れておいても損はないのですが、今回の記事では取り上げません。

■rendering mode

サーバサイドレンダリング(SSR)かSPAの2択です。今回はSPA。

Screen Shot 2020-12-11 at 20.45.45.png

できました。
早速プロジェクトを開いてみましょう。

Screen Shot 2020-12-11 at 20.50.29.png

何から何までご用意されています。最高ですね。

動作確認

「nuxt dev」コマンドを実行して、ローカルで開発用サーバを起動します。
このときホットリロードが有効になるため、コードを修正すると即時反映されます。

npm run dev

起動したら、ブラウザでhttp://localhost:3000を開きます。

Screen Shot 2020-12-12 at 16.38.36.png

適当に入れたメッセージが表示されてて恥ずかしいので、さっさと直していきましょう。

APIサーバと接続する(バックエンド側)

早速APIを叩いていきたいところですが、先にバックエンド側にCORSの設定を入れます。
どこから飛んできたリクエストを許容するか、あらかじめ決めておく必要があるわけです。

▼設定しないとこんなエラーが出ます
Screen Shot 2020-12-11 at 22.47.10.png

DemoApplication.javaに設定を追記したら、Herokuにpush。

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    // 以下を追記
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                // localhostとフロントエンドのドメイン(予定)に対し、"/api/"で始まるURIへのリクエストを許可
                // 末尾に/を入れると機能しないので注意
                registry.addMapping("/api/**")
                        .allowedOrigins(
                                "http://localhost:3000",
                                "https://my-rapid-demo-front.herokuapp.com"
                        );
            }
        };
    }
}
git add .
git commit -am "cors setting"
git push heroku master

これで準備完了です。

APIサーバと接続する(フロントエンド側)

ローカルからの接続先はhttp://localhost:8080、Herokuからはhttps://***.herokuapp.comにしたいので、環境変数で制御できるように設定を入れます。
nuxt.config.jsを開き、末尾に以下を追加してください。

env: {
    baseUrl: process.env.BASE_URL || 'http://localhost:8080'
}

これにより、変数process.env.baseUrlに対し、環境変数BASE_URLが設定されていればその値が、されていなければhttp://localhost:8080が格納されるようになります。

続いてpages/index.vueを開き、<template><script>をごそっと編集。
<style>はとりあえずそのまま。

<template>
  <div class="container">
    <ul>
      <li v-for="employee in employees">
        <span>{{ employee.name }} [{{ employee.department.name }}]</span>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      employees: []
    }
  },
  async created() {
    const response = await fetch(`${process.env.baseUrl}/api/employees`)
    if (response.ok) {
      this.employees = await response.json()
    } else {
      console.error(response.statusText)
    }
  }
}
</script>

これがトップページになります。中身をざっくり解説すると、

  • ページのインスタンスができたタイミング(created)で、Employee一覧取得APIを実行
  • 変数(date)のemployeesにレスポンスボディを格納
  • v-forを使い、employeesの要素数だけ<li>タグを生成

という流れです。

画面を再度開き、以下のようになっていれば接続はOKです。

Screen Shot 2020-12-12 at 14.52.05.png

殺風景ですが、とりあえずヨシ。

デプロイする

静的ビルドしてNetlify等に置くだけでもOKなのですが、今回はフロントエンドもHeroku上で動かします。
公式のガイドを参考に進めていきましょう。

まず、Heroku用の設定ファイルを作ります。
ファイル名は「Procfile」。ここに起動時のコマンドを設定します。

echo 'web: nuxt start' > Procfile

プロジェクト直下にProcfileができたことを確認したら、Herokuアプリケーションを作成。

heroku create my-rapid-demo-front

Herokuに設定を入れます。上2つがサーバの動作に必要な設定、
下が「process.env.baseUrl」に与えるための環境変数です。

heroku config:set HOST=0.0.0.0 -a 【アプリ名】
heroku config:set NODE_ENV=production -a 【アプリ名】
heroku config:set BASE_URL=https://【バックエンドのアプリ名】.herokuapp.com -a 【アプリ名】

ここで注意なのですが、npm/yarn使用時に生成されるpackage-lock.jsonyarn.lockをコミットすると、ビルドに失敗してしまいます。
.gitignoreに追記して、コミット対象外になるようにしておきます。

echo "package-lock.json" >> .gitignore
echo "yarn.lock" >> .gitignore

コードをpush。

git add .
git commit -am "initial commit"
git push heroku master

pushが完了したら、https://【アプリ名】.herokuapp.com/を開いてみましょう。
起動まで少し時間がかかるかもしれません。

Screen Shot 2020-12-12 at 14.56.14.png

無事に成功しました。
しばらく待っても何も出てこない場合、git push時の出力、Herokuのログ、デベロッパーツールのコンソールを確認し、エラーが起きていないか確認してください。

Done!

これで最低限の構築は完了です。
あとは必要に応じてAPIを増やしたり、テンプレートやUIフレームワークを見繕ってフロントエンドの見栄えを調整したりしていきましょう。
下のスクリーンショットはVuetifyで体裁を整えた画面です。

Screen Shot 2020-12-12 at 16.34.06.png

簡易認証を入れる(おまけ)

さて、開発用とはいえAPIと画面が公開されているのは気になる…
ということで、最低限の認証機構を入れてみます。

  1. フロントエンドとバックエンドで共通のAPIキーを持たせてチェック
  2. 画面を開くときにユーザ名・パスワードの入力を求める

なお、これを導入したとてAPIキーが流出すればアクセスし放題なので、言うまでもなく気休めです。くれぐれも本番運用にはお使いになりませんよう…

1. APIキーのチェック

まずはバックエンド側から。
application.propertiesAPI_KEY=dummyという行を追加します。
この値は環境変数で上書きされるため、環境変数があればその値、なければ「dummy」が適用されます。

続いて、パッケージinterceptorを新しく切り、中に以下のクラスを作成します。

package com.example.demo.interceptor;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class ApiInterceptor implements HandlerInterceptor {

    @Value("${API_KEY}")
    private String API_KEY;

    @Override
    public boolean preHandle(
            HttpServletRequest request,
            HttpServletResponse response,
            Object handler) throws IOException {

        // preflightの場合はスキップ
        if (request.getMethod().equals(HttpMethod.OPTIONS.name())) return true;

        String authorizationHeader = request.getHeader("Authorization");

        if (authorizationHeader == null || !authorizationHeader.equals("Bearer " + API_KEY)) {
            response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
            return false;
        }

        return true;
    }
}

このpreHandleというメソッドは、リクエストがControllerに渡される前に実行されます。
Headerに設定された文字列を読み取り、キーが一致しなかったら401エラーを返して後続処理が行われないようにしています。

続いて、DemoApplication.javaを開き、先ほど作ったcorsConfigurerをまるっと書き換えます。

    @Bean
    public WebMvcConfigurer webMvcConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/api/**")
                        .allowedOrigins(
                                "http://localhost:3000",
                                "https://my-rapid-demo-front.herokuapp.com"
                        );
            }

            @Override
            public void addInterceptors(InterceptorRegistry registry) {
                registry.addInterceptor(apiInterceptor)
                        .addPathPatterns("/api/**");
            }
        };
    }

addInterceptorsメソッドを実装することで、先ほどのInterceptorが/api/以下のリクエストに適用されるようになります。

続いてフロントエンド側。
先ほどのbaseUrlと同様に、nuxt.config.jsに変数を追加します。

env: {
    baseUrl: process.env.BASE_URL || 'http://localhost:8080',
    apiKey: process.env.API_KEY || 'dummy'
}

そして、リクエスト送信時にヘッダを追加します。

    const response = await fetch(`${process.env.baseUrl}/api/employees`, {
      headers: {
        Authorization: `Bearer ${process.env.apiKey}`
      }
    })

これで実装は完了です。
試しに"dummy"を別の文字列に変えてみると、401エラーが返ってくるかと思います。

Heroku上にはもう少し長めの文字列を付与してあげます。
なお、本格的に認証を行うのであればただの文字列ではなく、JWTトークンなどを検討するのが良いかと思います。

heroku config:set API_KEY=【キー文字列】 -a 【フロントエンドアプリ名】
heroku config:set API_KEY=【キー文字列】 -a 【バックエンドアプリ名】

2. 画面に認証ダイアログ

こちらはNuxt.js用の神モジュールを使わせて頂きます。

nuxt-basic-auth-module

npm install nuxt-basic-auth-module
  modules: [
    'nuxt-basic-auth-module'
  ],
  basic: {
    name: process.env.BASIC_AUTH_USER || 'user',
    pass: process.env.BASIC_AUTH_PASSWORD || 'password',
    enabled: true
  },

Screen Shot 2020-12-12 at 21.03.38.png

デプロイ後に画面を開き直すと、ダイアログが出てきました。
これですべて完了です。

おわりに

ここまで読んでいただきありがとうございました。
リポジトリのリンクを置いておきますので、お急ぎの際はご自由にお使いください。

GitHub

my-rapid-demo
my-rapid-demo-front

参考ページ

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?