Vue でサーバーと CRUD
テクノロジ系 / L13 app-tasks-jsonserver — Vue で json-server CRUD (モック API 体験)
Vue でサーバーと CRUD
作って・読んで・直して・消す
タスクアプリを作り、サーバーで「作る・読む・直す・消す」を実装する
作るもの:サーバーとつながるタスクアプリ
入力して「追加」→ サーバーに保存。「編集」「削除」もサーバーに反映される。
> ローカルだけのデータはリロードで消える。サーバーに保存すると、リロードしても残る。
CRUD ってなに?
アプリがデータに対してやることは、だいたいこの4つ。
| 頭文字 | 意味 | このアプリでは | HTTP |
|---|---|---|---|
| C | Create(作る) | タスクを追加 | POST |
| R | Read(読む) | 一覧を表示 | GET |
| U | Update(直す) | タスク名を編集 | PATCH |
| D | Delete(消す) | タスクを削除 | DELETE |
> 「やること4種 × HTTPメソッド4種」が対応している。今回はこれを Vue から全部やる。
環境構築:プロジェクトを用意する(最初に1回だけ)
このアプリは Vite(npm create vite@latest)で作ります。
おすすめ:配布の 作業用/ フォルダを VS Code で開く。
Vue と json-server(サーバー)が準備済みなので、すぐ次へ進めます。
> 何も作らなくてよい。一番早い。次の「自分で作る場合」は読み飛ばしてOK。
環境構築:自分で作る①(プロジェクトを作成)
🖥️ ターミナルで npm create vite@latest を実行し、対話で次を選ぶ:
| 質問 | 選ぶもの |
|---|---|
| Project name | my-app(好きな名前) |
| Select a framework | Vue |
| Select a variant | JavaScript |
続けて、作ったフォルダで必要なものを入れる:
cd my-app npm install npm install -D json-server@0.17.4
環境構築:自分で作る②(ファイルを置く)
配布の src/App.vue・src/components/TaskItem.vue・db.json を置き、
package.json の "scripts" に1行足す:
"server": "json-server --watch db.json --port 8000"
> これで「作業用」と同じ状態になる。この後の手順は共通。
進め方:あなたの机の上
作業中、画面には 2つのウィンドウを並べておく。
*(図は省略)*
> 左で書いて、右で確かめる。この2画面を行き来して進める。
3つの「画面」の役割
| 記号 | 画面 | 役割 |
|---|---|---|
| 📝 | VS Code(エディタ) | コードを書く・保存する |
| 🖥️ | ターミナル(VS Code 下部の2タブ) | サーバーを動かす・ログを見る |
| 🌐 | ブラウザ | アプリを動かして確かめる |
この資料では、操作のたびに 🛠️ どの画面で何をするか を記号で示す。
> ターミナルは VS Code の下に2タブで開く。①サーバー(json-server)と ②作業用(Vue)。
準備1:フォルダを開く
📝 VS Code で操作
VS Code で配布フォルダの中の 作業用/ を開く(メニュー: ファイル > フォルダーを開く)。
> 左にファイル一覧、右にコード、下にターミナル。この画面で作業する。
準備2:必要なものを入れる
🖥️ ターミナル①
ターミナル①で 作業用/ に移動し、必要なものを入れる(npm install は最初の1回だけ・Vue と json-server がまとめて入る)。
> Vue もサーバーも npm で入る。Python は不要。
準備3:サーバーを起動する
🖥️ ターミナル①(サーバー)
続けて、ターミナル①でサーバー(json-server)を起動する:
> http://localhost:8000 で待ち受け、データは db.json に保存される。このタブはこのままにしておく。
準備4:Vue を起動する
🖥️ ターミナル②
ターミナル②(+で新しいタブ)で、Vue の画面を起動する:
> npm run dev は止めずに置いておく。コードを保存すると自動で画面が更新される。
準備5:ブラウザで開く
🌐 ブラウザで操作
ブラウザで、ターミナル②に出た URL(例:http://localhost:5173/)を開く。
> 配布のアプリ(手書きデータ)が出れば準備OK。URLは自分の環境のものに読み替えること。
学習のループ:書く → 保存 → 自動更新 → 確かめる
*(図は省略)*
> 保存しないと画面は変わらない。「変わらない?」と思ったらまず保存(Cmd/Ctrl + S)。
進め方:作業用 と 答え
サンプルコード/ ├── 作業用/ ← ここを編集していく(install済み・起動する) ├── 答え/ ← step2〜5 の App.vue(詰まったら見る・コピーする) └── 完成品/ ← 全部できた状態(動かして確かめたい人用)
編集するのは 作業用/src/App.vue が中心。
> 詰まったら 答え/stepN/src/App.vue を見る。コピーすれば追いつける。
🎯 STEP1:手書きのタスクアプリを動かす
まずは配布の手書きデータのタスクアプリを動かして、出発点を確認する。
STEP1:出発点(手書きデータのタスクアプリ)
📄 作業用/src/App.vue(出発点のコード・抜粋)
<script setup>
import { ref } from 'vue'
import TaskItem from './components/TaskItem.vue'
const tasks = ref([
{ id: 1, name: '牛乳を買う' }, // ← 手書き
])
function addTask() { /* ローカルに追加 */ }
function removeTask(id) { /* ローカルから削除 */ }
</script>> この tasks は手書き。これをサーバーから取ってくるところから始める。
STEP1:ブラウザで確認
🌐 起動すると、ブラウザにこう表示されていればOK:
🎯 STEP2:R(読む)— 一覧をサーバーから
手書きの tasks を、サーバーから取ってきたデータに変える。
💡 STEP2 で学ぶ:fetch(GET)と onMounted
fetch(URL)= サーバーに「データちょうだい」とお願いする(GET)onMounted= 「画面が開いたとき」に1回だけ動く
onMounted(async () => {
const res = await fetch('http://localhost:8000/tasks')
tasks.value = await res.json() // 返ってきた一覧を入れる
})> await は「サーバーの返事を待つ」印。通信は時間がかかるので待つ。
STEP2-操作1:script を書き換える
📝 VS Code で操作
📄 作業用/src/App.vue の <script setup> を書き換える:
<script setup>
import { ref, onMounted } from 'vue'
import TaskItem from './components/TaskItem.vue'
const title = 'マイ・タスク'
const tasks = ref([]) // 手書きの3件をやめ、空にする
const loading = ref(true) // 「読み込み中」を出すための旗
const newName = ref('')
const API = 'http://localhost:8000/tasks'
onMounted(async () => { // 開いたとき、サーバーから取得する
const res = await fetch(API)
tasks.value = await res.json()
loading.value = false
})
// nextId, addTask, removeTask はそのまま(変更なし)
</script>> 変えたのは tasks を空に・loading を足す・onMounted で取得の3点だけ。
STEP2-操作1:template を書き換える
📄 同じ App.vue の <template> を書き換える(loading の表示を足す):
<template>
<h1>{{ title }}</h1>
<input v-model="newName" placeholder="やることを入力" />
<button @click="addTask">追加</button>
<p v-if="loading">読み込み中...</p>
<ul v-else>
<TaskItem v-for="task in tasks" :key="task.id"
:name="task.name" @remove="removeTask(task.id)" />
</ul>
</template>💾 書けたら Cmd/Ctrl + S で保存する。
STEP2-操作2:ブラウザで確認
🌐 ブラウザで操作
保存すると画面が自動で更新される。サーバーから取ってきた一覧が出ればOK:
> 手書きしていないのに「牛乳を買う」などが出る = サーバーから来た証拠。
STEP2-操作3:サーバーのログを見る
🖥️ ターミナル①(サーバー)
ターミナル①(サーバー)を見ると、ブラウザからの GET /tasks が記録されている:
> ブラウザが「ちょうだい」とお願いし、サーバーが 200 OK(成功)で返した記録。
STEP2-おまけ:サーバーを止めてみる
🖥️🌐 ターミナル+ブラウザ
サーバーが無いとどうなるか、わざと体験してみる。
- 🖥️ ターミナル①で Ctrl + C を押してサーバーを止める
- 🌐 ブラウザを再読み込みする
> 「読み込み中...」のまま止まる = データはサーバーから来ている証拠。
STEP2-おまけ:サーバーを戻す
確認できたら、ターミナル①でもう一度 npm run server で再起動しておく。
> 次の STEP でまたサーバーを使う。止めたままにしない。
✅ STEP2 の確認(R)
- [ ] ブラウザにサーバーの一覧が表示される
- [ ] ターミナル①に
GET /tasks ... 200 OKが出ている - [ ] サーバーを止めると「読み込み中...」のまま(=サーバー依存を確認)
> これが R(Read)。サーバーのデータを読んで表示した。
❓ STEP2 うまくいかないとき
| 症状 | 原因 | 対処 |
|---|---|---|
| 「読み込み中...」のまま | サーバーが起動していない | ターミナル①で npm run server |
| 画面が真っ白 | コードの保存忘れ / 書き間違い | Cmd/Ctrl+S で保存 → ターミナル②のエラーを見る |
| 何も変わらない | 保存していない | Cmd/Ctrl + S で保存する |
> どうしても進めないときは 答え/step2/src/App.vue をコピーする。
🎯 STEP3:C(作る)— 追加をサーバーに
フォームの「追加」を、ローカル追加から サーバーに POST へ変える。
💡 STEP3 で学ぶ:fetch(POST)
await fetch(API, {
method: 'POST', // 作る
headers: { 'Content-Type': 'application/json' }, // JSONで送る宣言
body: JSON.stringify({ name: '...' }), // 送る中身
})> GET は「読む」、POST は「作る」。method で何をするか決まる。
STEP3-操作1:addTask を書き換える
📝 VS Code で操作
📄 作業用/src/App.vue の addTask を、サーバー送信に書き換える:
async function addTask() {
if (newName.value === '') return
const res = await fetch(API, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: newName.value }),
})
tasks.value.push(await res.json()) // サーバーが返したタスクを足す
newName.value = ''
}> let nextId の行は、もう使わないので消してよい(サーバーが番号をつけてくれる)。
💾 Cmd/Ctrl + S で保存する。
STEP3-操作2:入力して追加する
🌐 ブラウザで操作
🌐 入力欄に「銀行に行く」と入れて「追加」を押す:
STEP3-操作2:追加されたか確認する
🌐 「追加」を押すと、一覧に「銀行に行く」が増える:
<figure class="before-after">
実行前
<img src="screenshots/browser_step3_typing.png" alt="入力中">
実行後
<img src="screenshots/browser_step3_added.png" alt="追加された">
<figcaption>「銀行に行く」と入力して「追加」ボタンを押すと、リストに追加される</figcaption>
</figure>
STEP3-操作3:本当に保存されたか確かめる
🌐 ブラウザで操作
🌐 ブラウザを再読み込みしてみる。
→ 追加した「銀行に行く」がまだある。
> ローカルだけだと再読み込みで消える。サーバーに保存されたから残る。これが本物のアプリ。
✅ STEP3 の確認(C)
- [ ] 入力して「追加」を押すと一覧に増える
- [ ] ターミナル①に
POST /tasks ... 200 OKが出る - [ ] 再読み込みしても追加したタスクが残っている
> これが C(Create)。フォームの入力がサーバーに作られた。
❓ STEP3 うまくいかないとき
| 症状 | 原因 | 対処 |
|---|---|---|
| 追加しても増えない | サーバー未起動 / 保存忘れ | サーバー起動 + Cmd/Ctrl+S |
| 再読み込みで消える | POSTになっていない(ローカルのまま) | method: 'POST' を確認 |
| エラーが出る | headers / body の書き間違い | 答え/step3/src/App.vue と見比べる |
🎯 STEP4:D(消す)— 削除をサーバーで
「削除」を、ローカル削除から サーバーに DELETE へ変える。
💡 STEP4 で学ぶ:fetch(DELETE)
削除は「どれを?」を URL で指定する。
await fetch(`${API}/${id}`, { method: 'DELETE' })> /tasks/3 のように id を URL に付ける。「3番を消して」の意味。
STEP4-操作1:removeTask を書き換える
📝 VS Code で操作
📄 作業用/src/App.vue の removeTask を、サーバー削除に書き換える:
async function removeTask(id) {
await fetch(`${API}/${id}`, { method: 'DELETE' })
tasks.value = tasks.value.filter(task => task.id !== id)
}💾 Cmd/Ctrl + S で保存する。
> まずサーバーで消し、それから画面の一覧からも消す。
STEP4-操作2:削除して確かめる
🌐 ブラウザで操作
🌐 どれかのタスクの「削除」を押す → 一覧から消える:
🌐 ブラウザを再読み込みしても、消したタスクは戻ってこない。
> ターミナル①には DELETE /tasks/2 ... 200 OK が出る。サーバーからも消えた証拠。
✅ STEP4 の確認(D)
- [ ] 「削除」を押すと一覧から消える
- [ ] ターミナル①に
DELETE /tasks/.. ... 200 OKが出る - [ ] 再読み込みしても消したタスクは戻らない
> これが D(Delete)。
🎯 STEP5:U(直す)— 名前を編集
タスク名を後から編集して、サーバーに PATCH で保存する。これで CRUD 一周。
💡 STEP5 で学ぶ:fetch(PATCH)と編集UI
- PATCH = 「一部だけ直す」。名前だけ書き換える
- 編集ボタンを押したら、画面に入力欄を出す(v-if で出し分け)
await fetch(`${API}/${id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: '新しい名前' }),
})> 「編集中かどうか」を editingId で覚えておき、編集中だけ入力欄を出す。
STEP5-操作1:App.vue に編集の仕組みを足す
📝 VS Code で操作
📄 作業用/src/App.vue の <script setup> に追記する:
const editingId = ref(null) // 編集中のタスクid(編集してないとき null)
const editName = ref('') // 編集の入力欄の値
function startEdit(task) { // 編集を始める
editingId.value = task.id
editName.value = task.name
}
function cancelEdit() { editingId.value = null }
async function saveEdit() { // サーバーに PATCH して名前を更新
const res = await fetch(`${API}/${editingId.value}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: editName.value }),
})
const updated = await res.json()
const task = tasks.value.find(t => t.id === editingId.value)
if (task) task.name = updated.name
editingId.value = null
}STEP5-操作2:template に編集欄を足す
📝 VS Code で操作
📄 作業用/src/App.vue の <template>:入力欄と、一覧に @edit を足す:
<button @click="addTask">追加</button>
<div v-if="editingId" class="edit-box">
新しい名前:
<input v-model="editName" />
<button @click="saveEdit">保存</button>
<button @click="cancelEdit">やめる</button>
</div>
<p v-if="loading">読み込み中...</p>
<ul v-else>
<TaskItem v-for="task in tasks" :key="task.id"
:name="task.name"
@edit="startEdit(task)"
@remove="removeTask(task.id)" />
</ul>STEP5-操作2のつづき:編集欄に色をつける
📝 VS Code で操作
📄 作業用/src/App.vue の <style scoped> に、編集欄を見やすくする色を足す:
<style scoped>
.edit-box {
margin: 8px 0;
padding: 8px;
background: #f0f7ff;
border-radius: 4px;
}
</style>STEP5-操作3:TaskItem に編集ボタンを足す
📝 VS Code で操作
📄 作業用/src/components/TaskItem.vue に「編集」ボタンを足す:
<script setup>
const props = defineProps({ name: { type: String, required: true } })
const emit = defineEmits(['remove', 'edit'])
</script>
<template>
<li class="task">
<span>{{ props.name }}</span>
<span>
<button @click="emit('edit')">編集</button>
<button @click="emit('remove')">削除</button>
</span>
</li>
</template>💾 2つのファイルを保存する。
STEP5-操作4:編集して確かめる
🌐 ブラウザで操作
🌐 タスクの「編集」を押すと、一覧の上に入力欄が出る:
STEP5-操作4:名前を変えて保存する
🌐 入力欄で名前を変えて「保存」を押すと、一覧の名前が変わる:
> 再読み込みしても変えた名前は残る(サーバーに PATCH で保存された)。
✅ STEP5 の確認(U・完成!)
- [ ] 「編集」を押すと入力欄が出る
- [ ] 「保存」で名前が変わる / 「やめる」で元に戻る
- [ ] ターミナル①に
PATCH /tasks/.. ... 200 OKが出る - [ ] 再読み込みしても変えた名前が残る
> これが U(Update)。CRUD(C・R・U・D)が全部そろった!
完成形チェック:CRUD ↔ コード
| CRUD | 操作 | コード |
|---|---|---|
| C | 追加 | fetch(API, { method:'POST', body:... }) |
| R | 一覧 | fetch(API)(onMounted) |
| U | 編集 | fetch(URL+id, { method:'PATCH', body:... }) |
| D | 削除 | fetch(URL+id, { method:'DELETE' }) |
振り返り:あなたは今こうできる
- [ ] 一覧をサーバーから 読める(R / GET)
- [ ] フォームの入力をサーバーに 作れる(C / POST)
- [ ] タスクをサーバーで 消せる(D / DELETE)
- [ ] タスク名をサーバーで 直せる(U / PATCH)
> 4つできれば、Web アプリの中核「サーバーとの CRUD」を一通り理解した。
まとめ
| STEP | CRUD | できるようになったこと |
|---|---|---|
| 1 | — | 手書きのタスクアプリ |
| 2 | R | サーバーから読む(GET) |
| 3 | C | サーバーに作る(POST) |
| 4 | D | サーバーで消す(DELETE) |
| 5 | U | サーバーで直す(PATCH) |
> 手書きのタスクアプリが「サーバーと CRUD するアプリ」になった。
> これで Web アプリの中核 ——「サーバーとデータをやりとりする」—— を一通り作れる。