バックエンド開発verに続いて、フロントエンドを開発
バックエンドでの開発が一旦終了したので、今度はフロントエンドの開発を進めていきます。
構成としてはこのような感じです。
mori_pro/(プロジェクトのディレクトリ)
├── diary-api/ ← NestJS(APIサーバー)
└── diary-frontend/ ← Vue.js(フロント側)★今回作成
前回の記事までは、dairy-apiで作業をしていたので、今回から新しくdairy-frontendというプロジェクトを作成し、そこで作業をしていきます。
Vueアプリ開発
#### STEP1:環境設定とプロジェクト作成 今回、Vue3 + TypeScript + Viteを使用して、フロントエンドを構築していきます。 以下の手順で、環境構築を進めました。
①プロジェクトの作成
$ pnpm create vue@latest
・Vue CLI(Command Line Interface) は、Vue.jsの公式ツールで、Vueアプリの作成や管理をコマンドで行うツール
・Vue3以降では Vite が推奨される。(Vue CLIはvue2時代の開発環境、Vue CLIでコマンドを書く場合は$ vue create my-project
)
・Viteは「ビルドツール(Rollupでdistを作成)兼開発サーバー」(Vue CLIはwebpack)→どちらもモジュールバンドラ
・Webpackも、Webアプリをバンドル(ビルド)するためのツール(JSファイルをまとめたり)
・Rollupは、ESMに最適化されたバンドラー(ツリーシェイキング(不要なコード除去)で軽量)
→作成する中で、下記のような選択肢が出てきますので、それぞれ選択していきます。
選択1: プロジェクト名(ディレクトリ名)
◇ Project name (target directory):
→ 「diary-frontend」 と入力。
選択2: 使用する機能の選択
◇ Select features to include in your project: (↑/↓で選択、スペースで選択)
下記を選択。
- TypeScript:型安全な開発のため
- Router (SPA development):ルーティングを利用するため(/homeとか/aboutとかのURL分け)
- Pinia (state management):状態管理(ログインユーザ情報やカート情報の一元管理)
- ESLint (error prevention):リンター。コード品質維持(let x == 1みたいな書き間違えを検出)
- Prettier (code formatting):コードフォーマット整形(インデント、改行を統一)
・この選択はプラグイン。
・ちなみにLintという概念自体は、1978年にC言語向けに作られたツールが由来。
選択3: Oxlintの導入(実験的)
◆ Install Oxlint for faster linting? (experimental)
→ No を選択。
・Rust製の次世代高速Lintツール。
・ESLintよりも数倍速くLintをかけられるのがメリット
・ただしまだ実験的。
・安定性を優先し、標準のESLintで進める。
②開発サーバの起動
ここまででプロジェクトが作成できたので、サーバの立ち上げに移っていきます。
githubと連携、ブランチの作成
その前に、githubと連携してpushできるように設定します。ブランチの設定もここで行います。
$ git init
$ git add -A
$ git commit -m "initial commit"
$ git checkout -b dev
$ git push -u origin dev
依存パッケージのインストール〜サーバ起動
次に、先ほどのvueプロジェクトで作成した際に生成結果で出てきた指示通りに、コマンドを進めます。
$ cd diary-frontend
$ pnpm install
・package.json に書かれた依存パッケージをすべてインストール。
例)vue, vue-router, pinia, vite(開発サーバ), eslint, prettier, typescript など
・これにより、実際にコードを書いて動かすのに必要な「部品一式」 が node_modules に整います。
$ pnpm format
$ pnpm dev
・Prettierを使って、プロジェクト全体のコードを自動整形するコマンド
$ pnpm dev
・Viteの開発サーバーを起動するコマンド
・devはホットリロード用。
・本番環境の場合は$ pnpm build
。生成されたdist/(distribution(配布物))をデプロイ。
ブラウザで http://localhost:5173
にアクセスすると、「You did it!」画面が表示され、無事開発環境が整ったことが確認できます。
Axiosの導入
APIとの通信に使用するため、Axiosを追加します。
$ pnpm add axios
・バックエンドAPI(今回はNestJS)との通信を行うため
・JSには標準APIで、Fetch APIが標準装備されているが、エラーハンドリングやリクエストキャンセルなどが弱め。
これでVue3 + Vite + TypeScript + Piniaによるフロントエンドの土台が完成しました!
次は、Axiosを使ってバックエンドAPI(NestJS)との通信処理を進めていきます🚀
STEP2:NestJS APIとの接続
①APIエンドポイントの設定
プロジェクト直下に、.envをファイルを作成し、下記記述。
diary-frontend/
├── .env
├── package.json
├── src/
├── vite.config.ts
└── ...
VITE_API_URL=http://localhost:3000
・VITE_API_URLは、Vueアプリ側から見たNestJSのAPIサーバーのURL
・「VITE_ 」から始まる名前にすることで、Viteが自動的にビルド時に注入可能。
②axios.tsを作成し、APIを呼び出す。
[axios.tsの作成]
src/配下に、utilsディレクトリを作成し、その直下にaxios.tsファイルを作成する。
diary-frontend/
├── src/
│ ├── assets/
│ ├── components/
│ ├── router/
│ ├── stores/
│ ├── views/
│ ├── utils/
│ │ └── axios.ts
│ ├── main.ts
│ └── ...
└── .env
import axios from 'axios';
const apiClient = axios.create({
baseURL: import.meta.env.VITE_API_URL,
headers: {
'Content-Type': 'application/json',
},
});
export default apiClient;
[コンポーネントから使う]
import apiClient from '@/utils/axios';
// API呼び出し例
const response = await apiClient.get('/diary-entry');
③画面開発
・APIへ送信する処理を、フロント側で作っていきます。
<template>
<div>
<h2>日記の登録</h2>
<input v-model="key" placeholder="Key" />
<input v-model="value" placeholder="Value" />
<button @click="submitEntry">送信</button>
<h2>日記一覧</h2>
<ul>
<li v-for="entry in entries" :key="entry.id">
{{ entry.key }}: {{ entry.value }}
</li>
</ul>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import apiClient from '@/utils/axios';
const key = ref('');
const value = ref('');
const entries = ref([]);
// 送信(POST)
const submitEntry = async () => {
try {
await apiClient.post('/diary-entry', {
key: key.value,
value: value.value,
});
console.log('送信成功');
await fetchEntries(); // 再取得
} catch (error) {
console.error('送信エラー:', error);
}
};
// 取得(GET)
const fetchEntries = async () => {
try {
const response = await apiClient.get('/diary-entry');
entries.value = response.data;
} catch (error) {
console.error('取得エラー:', error);
}
};
// 初期表示時に取得
onMounted(fetchEntries);
</script>
それとフロントの邪魔な部分を削除(非表示)にします。
<template>
<!-- <header>
<img alt="Vue logo" class="logo" src="@/assets/logo.svg" width="125" height="125" />
<div class="wrapper">
<HelloWorld msg="You did it!" />
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
</nav>
</div>
</header> -->
<RouterView />
</template>
・このままkey(就寝時間), value(23:00)などを入力して送信すると、CORS関連のエラーが出るため、API側の修正をしていきます。
例)こんな感じのエラーが出ます。
Access to XMLHttpRequest at 'http://localhost:3000/diary-entry' from origin 'http://localhost:5173' has been blocked by CORS policy...
・NestJS側でCROS許可の対応
(dairy-frontendではなく、dairy-apiで作業する)
import { NestFactory } from '@nestjs/core'; // NestJSアプリを生成するための関数
import { AppModule } from './app.module'; // ルートモジュールがAppModuleにある
// bootstrap関数
async function bootstrap() {
// AppModuleでアプリを作成
const app = await NestFactory.create(AppModule);
// ここで、CORSを有効化(フロント5173→APIサーバ3000、別オリジンでの通信を許可)
app.enableCors();
// PORT設定があればそれを使用、なければ3000を使用
await app.listen(process.env.PORT ?? 3000);
}
// アプリ立ち上げ
bootstrap();
STEP3:画面で送信し、データが受け取れているか確認
①下記のような見た目になっているので、keyとvalueに任意の値(就寝時間, 23:00)を入力し、送信btnを押下する。
②送信が成功していると、日記一覧にデータが表示され、Consoleでも送信成功が表示される。
STEP4:SQLiteにデータがある確認
①VS Codeを開き、拡張機能からSQLiteをインストール。
②APIサーバを再起動
$ pnpm run start
③VS CodeでDBを表示
・command+shift+pでコマンドパレットを起動させ、SQLite: Open Databaseを検索し押下。db.sqliteを選択。
・エクスプローラの最下部に「SQLITE EXPLORER」が表示されるので、クリック。db.sqliteもクリックすると、dairy_entryテーブルが表示されるので、二本指でクリックしshow tableを選択。
・すると、VS Code内でtableを確認することができます。
STEP5:データの流れのまとめ
ーーーーーーーーーーーーーーーーーーーーーーー
①フロントエンド:Vueアプリ
(src/views/HomeView.vue)
→ 画面表示・送信ボタンでAPIを呼ぶ(axios)
②フロント → APIサーバーへリクエスト
→ 送信先は .env の VITE_API_URL → http://localhost:3000
③NestJSサーバー:main.ts
→ bootstrap() でアプリ起動
→ AppModule が「どの機能(モジュール)を使うか」決める
④具体的なAPI処理
DiaryEntryController が受け取る
↓
DiaryEntryService でDB保存
↓
SQLite(db.sqlite)にデータ保存
⑤APIのレスポンスをVueが受け取る
→ entries に反映(画面に表示!)
ーーーーーーーーーーーーーーーーーーーーーーー
※AppModule の役割
DB接続設定(TypeOrmModule)
→ DiaryEntryModule(日記管理API)を読み込む
STEP6:CSSでスタイリング+機能追加(必須項目、バリデーションなど)
色々細々実装をしました。
詳細については、githubをご確認ください。
[フロント]
https://github.com/Toma-0205/diary-frontend.git
[バック]
https://github.com/Toma-0205/diary-api.git
[参考]
https://gyazo.com/3eadd71a7393df34e8c27e52e3861323
ローカル環境での主な作業は、ここまでです。
次はDBを本番環境用に移行し、本番サーバにWEBアプリを公開します。