Nuxt 3+Spring BootでREST API #12 フロントのインフラを構築する(手動編)
イントロダクション
この回からはデプロイ周りを整備していく。
最終的にはAWS CDKで自動化するが、何をやっているかの理解を深めるために手動→CloudFormation→CDKと段階を踏みながら同じ作業をしていく予定。
今回はマネジメントコンソールをいじりながら手動でデプロイしていく。
目次
- S3バケットを作成する
- Nuxtのビルド & デプロイ
- CloudFrontディストリビューションを作成する
- S3バケットポリシーの更新
S3バケットを作成する
Nuxtのファイルを放り込むためのS3バケットを作成する。
一般的な設定
任意のバケット名を付ける。グローバルに一意な必要がある。
このバケットのブロックパブリックアクセス設定
「パブリックアクセスをすべてブロック」のチェックを外す。
「現在の設定により、このバケットとバケット内のオブジェクトが公開される可能性があることを承認します。」にチェックを入れる。
これ以外はデフォルトのままバケットを作成。
Nuxtのビルド & デプロイ
npm run generate
を実行。
.output/public
にファイルが吐き出されるので、この中身を全てS3にアップロードする。
プロパティ > 静的ウェブサイトホスティングの編集を押す。
- 静的ウェブサイトホスティング
- 「有効にする」を選択。
- インデックスドキュメント
index.html
を指定。
「変更の保存」を押す。
CloudFrontディストリビューションを作成する
CloudFrontに移動し、「ディストリビューションを作成」を押す。
オリジン
- オリジンドメイン
- 先ほど公開したエンドポイントを登録。
- 名前
- オリジンドメイン設定時に自動で反映される。
- S3 バケットアクセス
- Origin access control settings (recommended) を選択。
- Origin access control
- 「コントロール設定を作成」から作成し設定。
デフォルトのキャッシュビヘイビア
設定
- デフォルトルートオブジェクト - オプション
- index.html を設定。
上記以外はデフォルトのままで「ディストリビューションを作成」をクリック。
S3バケットポリシーの更新
ディストリビューション作成後、下記のポップアップが出るので、ポリシーをコピーした後、S3のバケットポリシー編集画面に移動する。
ポリシーを貼り付け。
CloudFrontに戻り、一般 > 詳細 > ディストリビューションドメイン名 のURIにアクセスしてみる。
画面が表示されればOK。
参考
Nuxt3 (SSG) を S3 & CloudFront に CloudFormation でデプロイする | mirumi.tech
Nuxt 3+Spring BootでREST API #11 フロントのテストを自動化する
目次
- フロント用のワークフローを定義する
- ワークフローを分離する
- (補足)pathsとpaths-ignoreの挙動について
- フロント側の試運転
- API側の試運転
フロント用のワークフローを定義する
.github\workflows
にフロント用のワークフローであるui-test.yml
を作成する。
name: pr_check_ui on: pull_request: types: [opened, synchronize] paths: - "ui/**" jobs: comment: runs-on: ubuntu-latest steps: - name: checkout source uses: actions/checkout@v3 - name: Cache for node_modules uses: actions/cache@v2 with: path: "**/node_modules" key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} - name: install package run: cd ui && npm install - name: run test run: cd ui && npm run test
依存パッケージをインストールして、前回追加したテストコマンドを叩いている。
プロジェクトのルートパスで動作するのでコマンド実行前にフロントのディレクトリに移動する必要がある。
また、on
にpaths
を指定してui以下に変更があった場合だけワークフローが動作するようにしている。
ワークフローを分離する
以前作成したAPI用のワークフロー(api-test.yml
)にはpaths
の指定がないため、フロントの変更のみを含むプルリクにも反応してしまう。
無駄なので、フロントのみの変更をAPI用のワークフローが無視するように修正していく。
name: pr_check on: pull_request: types: [opened, synchronize] paths-ignore: - "ui/**" jobs: comment: runs-on: ubuntu-latest steps: - name: checkout source uses: actions/checkout@v3 - name: Set up JDK 17 uses: actions/setup-java@v3 with: java-version: 17 distribution: "temurin" - uses: actions/setup-node@v1 with: node-version: "14" - name: Cache for node_modules uses: actions/cache@v2 with: path: "**/node_modules" key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} - name: build java project by gradle run: sh ./gradlew clean build
on
にpaths-ignore
を指定して、ui
以下の変更しか含まれないプルリクの場合は無視するように設定した。
フロントとは異なり、paths-ignore
を用いて指定しているところに注意。
今回の構成では、api
は他の外部のプロジェクト(common
, repository
など)にも依存する場合があり、フロント以外の変更がある場合には基本的に常に動かしておきたいためこのような指定にした。
このあたりはプロジェクトの設計や都合に応じて適宜考慮すること。
(補足)pathsとpaths-ignoreの挙動について
paths
- マッチするファイルの変更が1つでもあれば起動。
paths-ignore
- マッチするファイルの変更しかなければ起動しない。
- マッチしないファイルの変更が1つでもあれば起動。
フロント側の試運転
フロント側のみに変更を入れて、プルリクを出してみる。
フロントのワークフローのみが動いている。
API側の試運転
API側のみに変更を入れて、プルリクを出してみる。
APIのワークフローのみが動いている。
参考
Nuxt 3+Spring BootでREST API #10 Jestでテストを書く
目次
- Jestのインストール・設定
- package.jsonの編集
- テストコードを書く
- テストを走らせる
Jestのインストール
TypeScriptで記述する前提で必要なライブラリをインストールしていく。
npm install --save-dev jest typescript ts-jest @types/jest
設定ファイルの作成。
npx ts-jest config:init
package.jsonの編集
npm run test
でテスト実行できるようにする。
"scripts": { "test": "jest" },
テストコードを書く
まずはテスト対象になるコード。
./logic/sum.ts
に、単純な足し算を行う関数を作成。
export const sum = (a: number, b: number) => a + b;
テストコードは以下。
./test/sum.test.ts
にテストを記述。
1と2を与えて3が返ってくればPASS。
import { test, expect } from "@jest/globals"; import { sum } from "../logic/sum"; test("adds 1 + 2 to equal 3", () => { expect(sum(1, 2)).toBe(3); });
テストを走らせる
npm run test
を叩いてみる。
補足
折角のNuxt3なのでVitestを使ってみたかった……が、今回は全体的な構築を主眼に置いているので導入が簡単なJestにした。
Nuxt3 + Vitestでのテストはまた別途やりたい。
Nuxt 3 × Vitest で単体テストの実行環境を作る
参考
Nuxt 3+Spring BootでREST API #9 NuxtからHTTPリクエストを送る
目次
- フロントの実装
- APIサーバの実装
- 試運転
フロントの実装
<script setup lang="ts"> const onClick = async () => { const uri = "http://localhost:8080/hello"; fetch(uri, { method: "GET", redirect: "follow", }) .then((response) => { return response.json(); }) .then((json) => { console.log(json.message); }) .catch((err) => { console.error(err); }); }; </script>
<template> <div> <button @click="onClick()">Hello</button> </div> </template>
ボタンを配置し、クリック時にAPIリクエストが飛ぶようにする。
後々環境変数から読み込むようにしたいが、ひとまずlocalhostをハードコード。
APIサーバの実装
package com.sample.api.controller; import com.sample.api.controller.out.GetOut; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("hello") @RequiredArgsConstructor @CrossOrigin public class HelloController { @RequestMapping(method = RequestMethod.GET) public GetOut sayHello() { return new GetOut("Hello, client"); } }
試運転
ボタンを押下するとAPIサーバからレスポンスが返却された。
Nuxt 3+Spring BootでREST API #8 フロントプロジェクトを作成する
目次
今回は短め。
- プロジェクトの新規作成
- 試運転
コマンドのインストール
npx nuxi init (プロジェクト名)
でNuxtプロジェクトを作成する。
試運転
ui
ディレクトリに移動し、依存パッケージのインストール。
npm run dev
を実行しhttp://localhost:3000/
にアクセスして、Nuxtデフォルトの画面が表示されていればOK。
Nuxt 3+Spring BootでREST API #7 GitHub ActionsでCI環境を構築する
目次
リポジトリを作成する
前回作成したJUnitをCI環境に組み込み、プルリクエストを作成、更新した時点で自動でテストが走るようにしたい。
今回はGitHub Actionsを使用するが、まずはリポジトリを作らないと始まらないのでrest-api-sample
という名前で作成する。
GitHub Actionsワークフローの定義を行う
次にワークフローの定義。
リポジトリのルートに.github\workflows
ディレクトリを作成。
workflowsの中にyaml形式でファイルを作成することでワークフローとして認識される。
今回はapi-test.yml
という名前で作成した。
name: api-test on: pull_request: types: [ opened, synchronize ] jobs: api-test: runs-on: ubuntu-latest steps: - name: checkout source uses: actions/checkout@v3 - name: Set up JDK 17 uses: actions/setup-java@v3 with: java-version: 17 distribution: "temurin" - uses: actions/setup-node@v1 with: node-version: "14" - name: Cache for node_modules uses: actions/cache@v2 with: path: "**/node_modules" key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} - name: build java project by gradle run: sh ./gradlew clean build
on
でワークフローをトリガーするイベントを定義する。
今回はプルリクエストが作成されたり、更新されたりしたタイミングでワークフローを実行したいので、typesにopened
, synchronize
を指定した。
jobs
で実行させたいジョブを定義する。
steps
の具体的な内容に関しては今回は詳述しないが、ざっくり言うと
- リポジトリからソースをチェックアウトする
- JDK 17のセットアップを行う
- Node.jsのセットアップを行う
- ライブラリのインストールをキャッシングする
- APIプロジェクトをビルドする (ここでテストが走る)
のような流れになっている。
ひとまず上記のyamlファイルをプッシュして準備完了。
試運転
適当なブランチを切ってプルリクを出し、CIが動くか試してみる。
まずtest_gha
という名前のブランチを切る。
適当にロジックを更新して
コミットして、リモートにプッシュしたらプルリクの作成を行う。
定義したジョブが走っているのがわかる。
テストにパスし、ビルド完了。
あえてテストコードを間違った内容に変更し、テストが失敗したときの動作を見てみる。
テストコードを変更し、再度コミット、プッシュ。
(先ほどtypesにsynchronize
を設定したので) プルリクが更新された場合もワークフローが実行されることがわかる。
テスト失敗。
というわけで、簡易的なAPIプロジェクトのCI環境が完成した。
参考
Nuxt 3+Spring BootでREST API #6 JUnitでテストを書く
目次
- Gradleでテストコマンドを実行できるようにする
- テスト対象のロジッククラスを記述する
- テストコードを書く
- テストを実行する
Gradleでテストコマンドを実行できるようにする
apiプロジェクトのbuild.gradleに以下の記述を追加する。
testImplementation('org.springframework.boot:spring-boot-starter-test') { exclude group: 'junit', module: 'junit' exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' } testImplementation 'org.mockito:mockito-inline:3.6.28' testImplementation 'net.java.dev.jna:jna-platform:5.8.0' testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
全体はこんな感じ。
plugins { id 'org.springframework.boot' version '2.7.5' id 'io.spring.dependency-management' version '1.0.15.RELEASE' id 'java' } group = 'com.sample' version = '0.0.1-SNAPSHOT' sourceCompatibility = '17' configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' testImplementation('org.springframework.boot:spring-boot-starter-test') { exclude group: 'junit', module: 'junit' exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' } testImplementation 'org.mockito:mockito-inline:3.6.28' testImplementation 'net.java.dev.jna:jna-platform:5.8.0' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } tasks.named('test') { useJUnitPlatform() }
rootのbuild.gradleのsubprojects
に以下の記述を追加する。
test { useJUnitPlatform() }
全体はこんな感じ。
/* * This file was generated by the Gradle 'init' task. * * This is a general purpose Gradle build. * Learn more about Gradle by exploring our samples at https://docs.gradle.org/6.9.3/samples */ allprojects { group = 'com.sample' } subprojects { apply plugin: 'java' sourceCompatibility = '17' test { useJUnitPlatform() } }
IntelliJ画面右のGradleタブよりbuildを実行。 Gradleタブよりtestコマンドが実行できるようになっていればOK。
テスト対象のロジッククラスを記述する
logic
パッケージを作成し、名前を引数で渡したら挨拶を返すシンプルなロジッククラスを作成。
テストコードを書く
最低限だけどこんな感じ。
package com.sample.api.logic; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; public class HelloLogicTest { HelloLogic logic = new HelloLogic(); @Test public void test_sayHello() throws Exception { try { String param = "Taro"; String expected = "Hello, Taro"; String result = logic.sayHello(param); assertEquals(expected, result, "sayHello()"); } catch (Exception e) { fail(e.getMessage()); } } }
テストを実行する
先ほどのGradleタブからtest
を選んで実行。
テスト成功。