※ 当ブログ「日々是事始め(コレコト)」はプロモーションを含みます。
このシリーズの総まとめです。これまで学んだ「リアクティブ・コンポーネント・REST API・OpenAPI・Vuetify」を1つにつなげ、小さなタスク管理アプリ(CRUD)を組み立てます。完成済みの巨大なコードを貼るより、どの層に何を置くかという地図を持って帰ってもらうことを目的にします。
※ 学習用に調べた内容を個人的にまとめたものです。文章は AI の補助を受けて作成しています。
作るもの(CRUD)
- Create:タスクを登録する
- Read:一覧・詳細を表示する
- Update:タスクを編集する
- Delete:タスクを削除する
フォルダ構成(layerを意識する)
src/
├─ app/
│ ├─ router/index.ts … 画面とURLの対応
│ └─ plugins/vuetify.ts … Vuetify登録
├─ generated/api/ … OpenAPIから自動生成(手で触らない)
├─ shared/
│ └─ api/httpClient.ts … axios共通設定
├─ features/
│ └─ tasks/
│ ├─ api/taskRepository.ts … 生成APIを包む層
│ ├─ composables/useTasks.ts … 画面が使うロジック
│ ├─ components/TaskForm.vue … 入力フォーム部品
│ └─ pages/TaskListPage.vue … 一覧ページ
└─ main.ts
キモは 「画面 → composable → repository → 生成API」 という一方向の流れです。生成 API を .vue から直接呼ばないことで、API 仕様が変わっても影響を repository に閉じ込められます。
① repository層:生成APIを包む
// features/tasks/api/taskRepository.ts
import { TasksApi, Configuration } from '@/generated/api'
import type { Task } from '@/generated/api'
const api = new TasksApi(
new Configuration({ basePath: import.meta.env.VITE_API_BASE_URL })
)
export const taskRepository = {
list: async (): Promise<Task[]> => (await api.listTasks()).data,
create: async (title: string): Promise<Task> =>
(await api.createTask({ title, done: false })).data,
remove: async (id: number): Promise<void> => {
await api.deleteTask(id)
},
}
② composable層:画面が使うロジック
// features/tasks/composables/useTasks.ts
import { ref } from 'vue'
import type { Task } from '@/generated/api'
import { taskRepository } from '../api/taskRepository'
export function useTasks() {
const tasks = ref<Task[]>([])
const loading = ref(false)
const error = ref<string | null>(null)
async function load() {
loading.value = true
error.value = null
try {
tasks.value = await taskRepository.list()
} catch {
error.value = '一覧の取得に失敗しました'
} finally {
loading.value = false
}
}
async function add(title: string) {
await taskRepository.create(title)
await load()
}
return { tasks, loading, error, load, add }
}
③ 画面:Vuetifyで一覧と登録
<!-- features/tasks/pages/TaskListPage.vue -->
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useTasks } from '../composables/useTasks'
const { tasks, loading, error, load, add } = useTasks()
const title = ref('')
onMounted(load)
async function onAdd() {
if (!title.value) return
await add(title.value)
title.value = ''
}
</script>
<template>
<v-container>
<v-text-field v-model="title" label="タスク名" />
<v-btn color="primary" @click="onAdd">追加</v-btn>
<v-alert v-if="error" type="error">{{ error }}</v-alert>
<v-progress-circular v-if="loading" indeterminate />
<v-list>
<v-list-item v-for="t in tasks" :key="t.id" :title="t.title" />
</v-list>
</v-container>
</template>
画面コンポーネントには、API の詳細も fetch も出てきません。useTasks() を呼んで状態とアクションを受け取るだけ。これが「読みやすくテストしやすい」状態です。残りの Update / Delete も同じ型(repository に足す → composable に足す → 画面で呼ぶ)で増やせます。
ここまでで身についたこと
- 状態(リアクティブ)を起点に画面を組み立てる
- 役割ごとにファイルを分け、一方向の流れを作る
- OpenAPI から型安全な API クライアントを生成する
- Vuetify で最後に見た目を整える
ロードマップに戻ると、まだ読んでいないクラスタ(Pinia・composables・API設計・Vuetifyフォーム)が残っています。気になるところから埋めていけば、知識が一本につながります。
もっと体系的に学ぶなら(書籍)
この記事はシリーズの一部です。TypeScript前提で Composition API・Pinia・Vue Router・テストまで一冊で体系的に学びたい方には、内容がそのまま重なる次の本がおすすめです。
PR
📕 紙の本
Vue 3 フロントエンド開発の教科書 [ WINGSプロジェクト 齊藤 新三 ] 価格:3960円 |
📱 電子書籍版
Vue 3 フロントエンド開発の教科書 【電子書籍】[ WINGSプロジェクト 齊藤新三【著】 ] 価格:3960円 |
関連記事



