Vue.js(Vue3)とTypescriptで日記アプリを開発する。（①バックエンド開発ver）


<br>

###### APIを開発する理由・意義
・フロントエンドとバックエンドの分離
・フロントエンドエンジニアはリッチなUI、バックエンドエンジニアはビジネスロジックやデータ管理に集中できる。
・マルチPF対応（再開発が不要）
・拡張機能などスケーラビリティに対応
・チーム開発(大規模）


#### JSON / XML / YAML
・JSONはXMLに比べ軽量
・JSONはキーペアの構成で可読性が高い
・JSONはJSとの親和性が高い

・XMLはJSONの前に広く使用されていた。

・YAMLはAPI使用記述などで使用される。設定ファイルとか。

#### READMEとYAMLの違い
→README：取扱説明書。
→YAML：プログラムやデータについて、どういう設定・構造になっているか、設定情報やデータそのものを記述するフォーマット。

・YAMLの使い方
→設定ファイル（Ruby on Rails、Django、Spring Bootなど）で、データベースの接続情報、ポート番号、外部サービスのAPIキー、ログレベル
例）config/database.yml
```
# 開発環境のデータベース設定
development:
  adapter: postgresql
  encoding: unicode
  database: my_app_development
  pool: 5
  username: my_user
  password: my_password
  host: localhost
  port: 5432

# 本番環境のデータベース設定
production:
  adapter: postgresql
  encoding: unicode
  database: my_app_production
  pool: 20
  username: prod_user
  password: <%= ENV['DATABASE_PASSWORD'] %> # 環境変数から読み込む例
  host: db.example.com
  port: 5432 
  
```

→コンテナオーケストレーション (Docker Compose, Kubernetes)、コンテナ（Webサーバー、データベース、キャッシュサーバーなど）をまとめて定義し連携。 
例）docker-compose.yml

```
version: '3.8' # YAMLファイルのバージョン指定 (オプション)

services:
  web:
    image: nginx:latest # 使用するDockerイメージ
    ports:
      - "80:80"         # ホストの80番ポートをコンテナの80番ポートにマッピング
    volumes:
      - ./html:/usr/share/nginx/html # ホストのhtmlディレクトリをコンテナのディレクトリにマウント
    depends_on:
      - app             # appサービスが起動してからwebサービスを起動

  app:
    build: .            # Dockerfileがあるディレクトリを指定してビルド
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgres://my_user:my_password@db:5432/my_app_development
    depends_on:
      - db

  db:
    image: postgres:15
    environment:
      POSTGRES_USER: my_user
      POSTGRES_PASSWORD: my_password
      POSTGRES_DB: my_app_development
    volumes:
      - db_data:/var/lib/postgresql/data

volumes: # 名前付きボリュームの定義
  db_data: 
  
```
→CI/CDパイプライン (GitHub Actions, GitLab CI/CD)、ビルド、テスト、デプロイといった一連のプロセスを自動化するための手順


#### CI/CD
・CI：継続的統合（バージョン管理、自動ビルド・テスト） 
・CD：継続的デリバリー（CIをクリアしたSWを本番リリース）
→GitHub Actions、GitLab CI/CDは幾分か無料

#### APIは何を使って作る？
Laravel, Python Fast API, Java Spring Boot, Node.js NestJS


# 今回は、Node.js NestJS、Vue + TypeScriptで日記アプリを開発、AWSにデプロイ
・NestJSはNode.js上で動作するサーバサイドアプリケーションフレームワーク。TSベースで書かれていて、API・サーバアプリを簡潔にかける。
・TSは、JS+型。
string, number, booleanなど明示できる。
コンパイル（ts→js変換）時に型エラーのチェックをするため、バグを発見しやすい。（VS Codeなら型が指定されてないよとかエラーを知らせてくれる）クラスベースのため、オブジェクト指向。（クラスは設計図、オブジェクトは実体）


## ★開発工程
### STEP 1：NestJS プロジェクト作成とAPIの構築
```
(sudo) npm i -g @nestjs/cli
nest new diary-api
```
・NestJSのCLIをインストール(nom / yarn /pnpmのうち、pnpmを選択)
（インストールで、下記などnest〜から始まるコマンドが使用可能）
```
nest new <project-name>       # 新しいNestJSプロジェクトを作る
nest g controller <name>      # コントローラーを生成
nest g service <name>         # サービスを生成
```
#### npm / yarn /pnpmの違い
| 比較項目              | npm (v9〜)                  | yarn (v1, v3)                       | pnpm                           |
|----------------------|----------------------------|------------------------------------|--------------------------------|
| 速度               | 普通（v7以降改善）         | v1：高速、v3：P'n'Pでさらに高速      | 非常に高速（最速）             |
| node_modules管理   | フラット（デフォルト）      | v1：フラット、v3：Plug’n’Play        | 厳密な構造で依存関係ミスを防ぐ |
| ディスク使用量      | 高め                        | 普通                               | 少ない（共有キャッシュ）       |
| インストール整合性  | v7〜は package-lock.json を重視 | yarn.lock                          | pnpm-lock.yaml                 |
| lockファイル        | package-lock.json           | yarn.lock                          | pnpm-lock.yaml                 |
| ワークスペース対応  | あり（v7〜）                 | v1, v3ともに強い                   | 非常に優秀（monorepo向け）     |
| セキュリティ         | npm audit                   | yarn audit                         | pnpm audit（npmベース）        |
| 公式互換性          | 100%                        | 若干独自構文あり（特にv3）         | ほぼnpm互換だが内部構造は異なる |


#### STEP２：初期化
``` 
nest new diary-api
```

#### STEP３：作業ディレクトリに移動
```
cd dairy-api
pnpm run start()
```
→http://localhost:3000/ にアクセスすると、初期設定の AppController により "Hello World!" が返る。

#### STEP４：日記用のエンティティを作成
```
pnpm nest g resource diary-entry
```
・（/dairy-entry/diary-apiにて行う。）

##### [選択]
・REST API → 今回はこれを選択
・Would you like to generate CRUD entry points? → Yes （後から変更可）

→以下が自動生成される。
Ldiary-entry.controller.ts
Ldiary-entry.service.ts
Ldiary-entry.module.ts
Lcreate-diary-entry.dto.ts など

[ファイルの役割・概要]
src/
  diary-entry/
    diary-entry.controller.ts     <b>// APIルートの定義（GET, POSTなど）</b>
    diary-entry.service.ts        <b>// ビジネスロジック</b>
    diary-entry.module.ts         <b>// モジュール定義</b>
    dto/
      create-diary-entry.dto.ts   <b>// POST用の型定義</b>
      update-diary-entry.dto.ts   <b>// PUT用の型定義</b>
    entities/
      diary-entry.entity.ts       <b>// DBエンティティ定義（TypeORM想定）</b>

##### [選択肢が、今回の用途に向いているかチェック]
✅REST API：一般的なWeb API（HTTP + JSON）を実装する標準<b>→◎今回最適</b>
・GraphQL (code/schema first)：フロントで必要なデータを柔軟に取得したいときに有用（複雑なUI向け）	<b>→△ 学習コストが高い</b>
・Microservice：KafkaやMQなど非HTTPでサービス間通信する設計<b>→✕ 今回は不要</b>
・WebSockets：チャットやリアルタイム更新など、双方向通信が必要な場合<b>→✕ 今回は不要</b>

###### REST APIとは

・アーキテクチャスタイル
・6つの原則
Lクライアントサーバ分離
Lステートレス
Lキャッシュ可能性
L統一IF
L階層化システム
Lコードオンデマンド

・クライアントは、HTTPメソッドを使用して、URIに対してリクエストを送信
→エンドポイントでこれを行う。送信と応答を返す

・クライアント
→API送信をする側。

###### URLはURIの一種
・URIは識別子（レストランの住所）そのもの
・エンドポイント（そのレストランの入り口）はURIで識別された、API機能へのアクセスポイント。
・https://api.example.com/products/123
・https://api.example.com がAPIのベースアドレス
・「/products/123」がパス。
・全体がエンドポイント
→URI≠エンドポイント

##### Yes を選んだ場合に生成
・NestJSが以下のCRUD操作を自動で作成
メソッド	エンドポイント	用途
GET	/diary-entry	全件取得
GET	/diary-entry/:id	特定ID取得
POST	/diary-entry	新規作成
PATCH	/diary-entry/:id	一部更新
DELETE	/diary-entry/:id	削除
→自動でルーティングとコントローラ、DTOが生成される

###### ・DTOとは
レスポンスデータの構造を明確化
バリデーションをかけ、保守性を高めることができる（class-validatorを使えば）
（DTOがないと型なしになる。dto.date, dto.timeとかができない。補完が効かずバグの温床になる。）

###### エンティティとは
DBと直接対応する実データ構造の定義
TypeORM（Object Relational Mapping）で必要な構造
DB１レコード、１エンティティのインスタンス

###### ・処理の流れ
DTO→Service（メモリに保存）→ControllerでAPI公開

#### STEP５：Githubと連携
・githubにリポジトリを作成（diary-api）
```
# 1. Gitリポジトリを初期化（既に init 済ならスキップ可）
$ git init
# 2. 変更を追加して初回コミット
$ git add .
$ git commit -m "Initial commit"
# 3. GitHubリポジトリをリモートに追加（URLはあなたのリポジトリに置き換えて）
$ git remote add origin https://github.com/Toma-0205/diary-api.git
# 4. ブランチ名をmainに統一
$ git branch -M main
# 5. 初回Push
$ git push -u origin main
# 6 ブランチを分ける
$ git checkout -b dev（ローカルにdevブランチ作成）
$ git push -u origin dev（リモートにもdevを作成、ローカルと紐付け）
```

#### STEP６：日記を key + value 形式で保存・取得する APIの構築、動作確認（テスト）
###### ・create-diary-entry.dto.ts
```
export class CreateDiaryEntryDto {
  key: string;    // 例: "sleepTime"
  value: string;  // 例: "23:30"
}
```
→POSTリクエストで送られてくるデータの「型」を定義
→@Body() デコレーター（注釈）は、HTTPリクエストのBodyに含まれるJSONを受け取るためのNestJSの注釈
→マッピング、JSONの中身をDTOクラスのフィールドに対応づける

###### ・diary-entry.service.ts
```
@Injectable()
export class DiaryEntryService {
  private entries: DiaryEntry[] = [];
  private idCounter = 1;

  create(entryDto: CreateDiaryEntryDto): DiaryEntry {
    const newEntry: DiaryEntry = {
      id: this.idCounter++,
      key: entryDto.key,
      value: entryDto.value,
      createdAt: new Date(),
    };
    this.entries.push(newEntry);
    return newEntry;
  }

  findAll(): DiaryEntry[] {
    return this.entries;
  }
}
```
→データの作成・取得・更新などを行う。
###### 処理の流れ（create）
1. Controllerから entryDto（key/valueのデータ）を受け取る
2. 自動的に id をインクリメント（1, 2, 3…）
3. createdAt に現在の日時を付与
4. それを entries[] 配列に保存
5. 登録されたデータを返す（findAll()）

###### ・diary-entry.controller.ts
```
@Controller('diary-entry')
export class DiaryEntryController {
  constructor(private readonly diaryEntryService: DiaryEntryService) {}

  @Post()
  create(@Body() dto: CreateDiaryEntryDto) {
    return this.diaryEntryService.create(dto);
  }

  @Get()
  findAll() {
    return this.diaryEntryService.findAll();
  }
}
```
###### 処理の流れ
@Post()
* POST /diary-entry にリクエストが来ると発火
* @Body() で送られてきたJSONをDTOに変換して受け取る
* Serviceの create() に渡す → 保存された結果を返す（Node.jsのプロセスメモリ、RAM上に一時保存、再起動すると消える）

@Get()
* GET /diary-entry にアクセスがあると findAll() を呼び出し
* Serviceの findAll() の戻り値をそのまま返す
* NestJSAPI（POST /diary-entry）にデータ送信
```
curl -X POST http://localhost:3000/diary-entry \
  -H "Content-Type: application/json" \
  -d '{"key": "sleepTime", "value": "23:45"}'
```
レスポンス
```
curl http://localhost:3000/diary-entry
[{"id":1,"key":"sleepTime","value":"23:45","createdAt":"2025-05-20T08:17:23.407Z"}]%
```
→データの保存、取得ができていることが確認できる。

###### ★処理の順番
① curlでPOST　→　クライアントがリクエストを送る
② @Post()メソッドに一致　→　NestJSが controller.ts の create() を呼ぶ
③ @Body()でDTOに変換　→　JSONが CreateDiaryEntryDto にマッピングされる
④ serviceに渡す　→　create(dto)メソッドでデータ処理（保存）
⑤ 結果を返す　→　生成されたオブジェクトが controller → curl に返る

#### STEP７：TypeORM + SQLiteでDBにデータを永久保存

###### TypeORM
・TSで使えるObject Relational Mapper、TSクラスを使用したDBテーブルとデータをやり取りするライブラリ
→TypeORMを使用すると、SQLがなくても操作可能。
→ディスク上に永久保存（DB保存）。↔︎Node.jsのRAMに保存。
→ORM＝オブジェクト（データと処理を一つの単位、再利用可能）とRDB（SQL）を繋ぐライブラリ
→オブジェクト指向で表現することで、スパゲティ化（ぐちゃる、メンテしづらい）を防止
→オブジェクト指向にすることで、constructorで管理が可能。

NestJS↔︎TypeORM(Entityクラス)↔︎SQLite

###### ①依存ライブラリのインストール
```
pnpm add @nestjs/typeorm typeorm sqlite3
```
・@nestjs/typeormで、NestJSのDI（Dependency Injection：依存を外部注入、newを描かなくていい）
・モジュール構造（@Module下のimports, controllers, providersなど。ちなみにAngular由来）に沿ったDB接続
・管理が可能になるNest公式ラッパー（専用モジュール）、NestJS視点では生のTypeORMは使いづらいため。

→typeormは、TS対応ORM(TypeORM)本体、EntityクラスでDB操作可能（CRUD操作）
→sqlite3で、TypeORMがSQLiteのDBを読み書き可能に。
→NestJSはDIをTSで実現したモダンフレームワーク。

###### ②TypeORM設定ファイルの編集
```
// src/app.module.ts

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { DiaryEntry } from './diary-entry/entities/diary-entry.entity';
import { DiaryEntryModule } from './diary-entry/diary-entry.module';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'sqlite',
      database: 'db.sqlite',
      entities: [DiaryEntry],
      synchronize: true, // 開発中のみtrue（本番はfalse推奨）
    }),
    DiaryEntryModule,
  ],
})
export class AppModule {}
```
###### ③Entityファイルの編集(DBのテーブルを定義)
```
// src/diary-entry/entities/diary-entry.entity.ts

import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity()
export class DiaryEntry {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  key: string;

  @Column()
  value: string;

  @Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' })
  createdAt: Date;
}
```
###### ④ModuleにRepository登録
```
// src/diary-entry/diary-entry.module.ts

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { DiaryEntry } from './entities/diary-entry.entity';
import { DiaryEntryService } from './diary-entry.service';
import { DiaryEntryController } from './diary-entry.controller';

@Module({
  imports: [TypeOrmModule.forFeature([DiaryEntry])],
  controllers: [DiaryEntryController],
  providers: [DiaryEntryService],
})
export class DiaryEntryModule {}
```
###### ⑤Serviceファイルの編集（DB保存へ切り替え）
```
// src/diary-entry/diary-entry.service.ts

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { DiaryEntry } from './entities/diary-entry.entity';
import { CreateDiaryEntryDto } from './dto/create-diary-entry.dto';

@Injectable()
export class DiaryEntryService {
  constructor(
    @InjectRepository(DiaryEntry)
    private readonly diaryRepo: Repository<DiaryEntry>,
  ) {}

  create(dto: CreateDiaryEntryDto) {
    const entry = this.diaryRepo.create({
      key: dto.key,
      value: dto.value,
    });
    return this.diaryRepo.save(entry);
  }

  findAll() {
    return this.diaryRepo.find({ order: { createdAt: 'DESC' } });
  }
}
```
###### ⑥動作確認
・POST
```
curl -X POST http://localhost:3000/diary-entry \
  -H "Content-Type: application/json" \
  -d '{"key": "sleepTime", "value": "23:45"}'
```
・GET
```
curl http://localhost:3000/diary-entry
```

値が取れたらOK！
バックエンドでの開発は一旦終了です。

お疲れ様でした。
次は、フロントエンドの開発に入ります。