採点されません。匿名です。 間違えることは学習の一部です。読むだけでもかまいません。

Vue でサーバーと CRUD

テクノロジ系 / L13 app-tasks-jsonserver — Vue で json-server CRUD (モック API 体験)

Vue でサーバーと CRUD

作って・読んで・直して・消す

タスクアプリを作り、サーバーで「作る・読む・直す・消す」を実装する

作るもの:サーバーとつながるタスクアプリ

入力して「追加」→ サーバーに保存。「編集」「削除」もサーバーに反映される。

!h:300px

> ローカルだけのデータはリロードで消える。サーバーに保存すると、リロードしても残る。

CRUD ってなに?

アプリがデータに対してやることは、だいたいこの4つ

頭文字意味このアプリではHTTP
CCreate(作る)タスクを追加POST
RRead(読む)一覧を表示GET
UUpdate(直す)タスク名を編集PATCH
DDelete(消す)タスクを削除DELETE

> 「やること4種 × HTTPメソッド4種」が対応している。今回はこれを Vue から全部やる。

環境構築:プロジェクトを用意する(最初に1回だけ)

このアプリは Vitenpm create vite@latest)で作ります。

おすすめ:配布の 作業用/ フォルダを VS Code で開く。

Vue と json-server(サーバー)が準備済みなので、すぐ次へ進めます。

> 何も作らなくてよい。一番早い。次の「自分で作る場合」は読み飛ばしてOK。

環境構築:自分で作る①(プロジェクトを作成)

🖥️ ターミナルで npm create vite@latest を実行し、対話で次を選ぶ:

質問選ぶもの
Project namemy-app(好きな名前)
Select a frameworkVue
Select a variantJavaScript

続けて、作ったフォルダで必要なものを入れる:

cd my-app
npm install
npm install -D json-server@0.17.4

環境構築:自分で作る②(ファイルを置く)

配布の src/App.vuesrc/components/TaskItem.vuedb.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 で配布フォルダの中の 作業用/ を開く(メニュー: ファイル > フォルダーを開く)。

!h:330px

> 左にファイル一覧、右にコード、下にターミナル。この画面で作業する。

準備2:必要なものを入れる

🖥️ ターミナル①

ターミナル①で 作業用/ に移動し、必要なものを入れる(npm install は最初の1回だけ・Vue と json-server がまとめて入る)。

!w:520px

> Vue もサーバーも npm で入る。Python は不要

準備3:サーバーを起動する

🖥️ ターミナル①(サーバー)

続けて、ターミナル①でサーバー(json-server)を起動する:

!w:520px

> http://localhost:8000 で待ち受け、データは db.json に保存される。このタブはこのままにしておく。

準備4:Vue を起動する

🖥️ ターミナル②

ターミナル②(+で新しいタブ)で、Vue の画面を起動する:

!w:520px

> npm run dev は止めずに置いておく。コードを保存すると自動で画面が更新される。

準備5:ブラウザで開く

🌐 ブラウザで操作

ブラウザで、ターミナル②に出た URL(例:http://localhost:5173/)を開く。

!h:300px

> 配布のアプリ(手書きデータ)が出れば準備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:

!h:330px

🎯 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:

!h:300px

> 手書きしていないのに「牛乳を買う」などが出る = サーバーから来た証拠。

STEP2-操作3:サーバーのログを見る

🖥️ ターミナル①(サーバー)

ターミナル①(サーバー)を見ると、ブラウザからの GET /tasks が記録されている:

!w:560px

> ブラウザが「ちょうだい」とお願いし、サーバーが 200 OK(成功)で返した記録。

STEP2-おまけ:サーバーを止めてみる

🖥️🌐 ターミナル+ブラウザ

サーバーが無いとどうなるか、わざと体験してみる。

  1. 🖥️ ターミナル①で Ctrl + C を押してサーバーを止める
  2. 🌐 ブラウザを再読み込みする

!h:300px

> 「読み込み中...」のまま止まる = データはサーバーから来ている証拠。

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.vueaddTask を、サーバー送信に書き換える:

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:入力して追加する

🌐 ブラウザで操作

🌐 入力欄に「銀行に行く」と入れて「追加」を押す:

!h:330px

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>

!h:330px

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.vueremoveTask を、サーバー削除に書き換える:

async function removeTask(id) {
  await fetch(`${API}/${id}`, { method: 'DELETE' })
  tasks.value = tasks.value.filter(task => task.id !== id)
}

💾 Cmd/Ctrl + S で保存する。

> まずサーバーで消し、それから画面の一覧からも消す。

STEP4-操作2:削除して確かめる

🌐 ブラウザで操作

🌐 どれかのタスクの「削除」を押す → 一覧から消える:

!h:280px

🌐 ブラウザを再読み込みしても、消したタスクは戻ってこない

> ターミナル①には 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:編集して確かめる

🌐 ブラウザで操作

🌐 タスクの「編集」を押すと、一覧の上に入力欄が出る:

!h:330px

STEP5-操作4:名前を変えて保存する

🌐 入力欄で名前を変えて「保存」を押すと、一覧の名前が変わる:

!h:330px

> 再読み込みしても変えた名前は残る(サーバーに 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」を一通り理解した

まとめ

STEPCRUDできるようになったこと
1手書きのタスクアプリ
2Rサーバーから読む(GET)
3Cサーバーに作る(POST)
4Dサーバーで消す(DELETE)
5Uサーバーで直す(PATCH)

> 手書きのタスクアプリが「サーバーと CRUD するアプリ」になった。

> これで Web アプリの中核 ——「サーバーとデータをやりとりする」—— を一通り作れる。