iT邦幫忙

2026 iThome 鐵人賽

DAY 8
0
JavaScript

從看不懂到做出來,用 PawPal 走過前端新手村系列 第 8

Day 8|從「新增」到「顯示」,我的第一個 CRUD 完成了!

  • 分享至 

  • xImage
  •  

今天的故事

上一篇,我處理了前端表單、API 與資料庫欄位名稱不同的問題。

當欄位終於能正確對應後,新增寵物的資料也可以順利送到後端,並寫入資料庫。

但資料成功存進去之後,我很快就遇到下一個問題:

資料已經新增成功了,為什麼首頁還看不到?

當時的我原本以為,只要新增了什麼,畫面就會自然顯示什麼。

實際做過之後才發現,新增資料和顯示資料是兩段不同的流程。

資料成功進入資料庫,只代表新增完成;前端還需要主動更新畫面使用的資料,才能把內容顯示在畫面上。


我原本以為,新增什麼就會出現什麼

第一次製作新增寵物功能時,我的想法很單純。

使用者填完表單,按下送出,資料成功寫進資料庫後,首頁應該就會直接出現新的寵物卡片。

在我的想像中,流程大概是:

填寫寵物資料
↓
按下新增
↓
資料進入資料庫
↓
首頁自動出現寵物

但真正實作後,我才發現中間少了一段重要的流程。

資料庫裡已經有資料,不代表前端畫面會自己知道。

前端仍然要主動更新目前使用的寵物清單,才能把資料顯示成登入後首頁 Dashboard 上的寵物卡片。

PawPal 的完整流程比較接近:

使用者填寫表單
↓
前端送出新增請求
↓
後端接收並處理資料
↓
資料寫入 PostgreSQL 的 pets 資料表
↓
後端回傳新增的寵物資料
↓
前端更新寵物清單
↓
把資料顯示成寵物卡片

Dashboard 第一次載入時會先取得寵物列表;新增成功後,則使用 POST 回傳的那筆資料更新前端清單,而不是再送一次 GET。

這也是我第一次比較明確地理解,Create 和 Read 雖然都在處理同一筆資料,但負責的是不同階段。


第一次使用 Postman,我幾乎看不懂

在學習 API 的過程中,我也開始接觸 Postman。

Postman 不是課堂上直接教我使用的工具,而是我在遇到 API 問題後,自己上網搜尋資料、跟著教學慢慢嘗試。

第一次打開 Postman 時,我其實幾乎看不懂。

畫面上有請求方法、網址、Headers、Body 和 Response,但我不知道每個區塊實際負責什麼,也還沒了解資料到底要放在哪裡。

一開始,我只知道把 API 網址貼進去,再按下送出。

後來使用幾次後,我才慢慢發現:

原來不一定要透過前端畫面,才能呼叫後端 API。

即使不透過前端表單,我仍然可以使用 Postman 直接送出請求,觀察後端回傳的結果。

這讓我第一次比較清楚地看見,前端與後端之間傳遞的資料實際長什麼樣子。


POST:把寵物資料送出去

新增寵物時,PawPal 前端會把使用者填寫的資料送到 POST /api/v1/pets

src/api/pet.js 使用 Axios。以下為依照實作整理的簡化概念,省略欄位轉換函式的細節:

const request = createPetRequestData(petData)

const response = await axios.post(
  `${API_BASE_URL}/api/v1/pets`,
  request.data,
  { headers: request.headers },
)

前端表單送出的資料包含 namespeciesbreedgenderbirthdayweightmicrochipNumberneuteredbloodTypefurColornote 與選取的 avatarFile

送出前,src/api/pet.js 會把前端欄位轉成 API 使用的 snake_case,例如:

{
  name,
  species,
  breed,
  gender,
  birthday,
  weight,
  microchip_number: microchipNumber,
  neutered,
  blood_type: bloodType,
  fur_color: furColor,
  notes: note,
}

如果使用者選了寵物照片,前端會改用 FormData,並把檔案放在 avatar 欄位;沒有檔案時才以 JSON 傳送。欄位轉換函式也支援將 photoUrl 對應為 avatar_url

本文提到的新增與取得寵物 API,都需要登入狀態。前端會從 localStorage 讀取 pawpal_token,並在請求的 Authorization header 帶上 Bearer Token。

以前看到這段程式碼時,我只會把它理解成:

按下新增後,把資料送出去。

但透過 Postman 和實際測試後,我開始知道還要觀察更多事情。

例如:

  • 請求有沒有真的送出。
  • 後端有沒有收到資料。
  • 回傳的 HTTP status code 是什麼。
  • Response 裡有哪些內容。
  • 資料庫是否真的新增了一筆資料。

只有看到畫面沒有報錯,還不能直接代表整段流程都正確。


我會從三個地方確認新增是否成功

測試新增寵物功能時,我不會只看前端畫面。

我通常會從三個地方確認。

1. HTTP status code

第一個是查看 HTTP status code。

PawPal 成功建立寵物時,後端會回傳 201 Created

它可以幫助我初步判斷,這次請求是否有成功被後端處理。

當時的我還記不住每一種狀態碼代表什麼,但至少開始知道:

  • 成功和失敗會有不同的狀態碼。
  • 不能只看有沒有出現畫面。
  • 狀態碼可以協助判斷問題在哪個階段。

PawPal 的驗證失敗會回傳 400,未登入或 Token 無效會回傳 401,晶片號碼重複則回傳 409;其他未預期的錯誤會以 500 回應。

2. JSON Response

第二個是查看後端回傳的 JSON。

建立成功時,後端回傳的結構是 { pet: ... },其中的寵物欄位仍使用資料庫端的 snake_case。以下為簡化概念

{
  "pet": {
    "id": 42,
    "user_id": 7,
    "name": "小花",
    "species": "dog",
    "microchip_number": "123456789012345",
    "neutered": false,
    "avatar_url": "https://..."
  }
}

Response 讓我可以看見後端實際回傳了哪些資料。

透過 Response,我才開始理解,API 不是只有把資料送出去,也會把處理結果傳回來。

前端的 src/api/pet.js 會把這個 pet 轉回前端使用的欄位名稱;若請求失敗,則統一回傳 success: false、訊息與後端 Response 資料,讓新增視窗與提示訊息可以顯示錯誤。

3. 資料庫

第三個是直接查看資料庫。

PawPal 使用 PostgreSQL 的 pets 資料表保存寵物資料。新增時,後端會以登入會員的 user_id 寫入 namespeciesbreedgenderbirthdayweightmicrochip_numberneuteredblood_typefur_colornotesavatar_url

pets.user_id 是外鍵,對應 users.id。因此,新增與取得寵物的 API 都會依登入者的 user ID 處理資料:新增時建立該會員的寵物,讀取時也只取得該會員的寵物。

如果 API 顯示成功,我還是會確認資料庫裡是否真的新增了一筆寵物資料。

這樣拆開檢查,也能幫助我判斷問題可能出在哪一段。

例如:

  • API 失敗時,先檢查請求與後端處理。
  • API 成功但資料庫沒有資料時,檢查資料寫入流程。
  • 資料庫已經有資料但 Dashboard 沒有顯示時,檢查讀取與前端清單更新。

透過這些確認,我才不會只靠「看起來好像成功」判斷功能是否完成。


GET:把已經存在的資料取回來

當需要取得已有的寵物資料時,PawPal 使用 GET /api/v1/pets

src/api/pet.js 會從 Response 的 pets 陣列取出資料,再透過欄位轉換函式提供給前端。以下為簡化概念

const response = await axios.get(`${API_BASE_URL}/api/v1/pets`, {
  headers: getAuthHeaders(),
})

const pets = response.data.pets.map(mapPetFromApi)

GET 成功時會回傳 200 OK,Response 的結構是 { pets: [...] },不是直接回傳陣列。

GET 負責的不是建立新資料,而是取得已經存在的資料。

後端會用登入會員的 user ID 查詢 pets 資料表,再把資料透過 Response 回傳給前端。

前端取得資料後,才能把每一筆寵物資訊顯示成 Dashboard 上的卡片。

流程可以拆成:

Dashboard 載入時送出 GET 請求
↓
後端依登入會員查詢資料庫
↓
後端回傳 pets 陣列
↓
前端把 snake_case 欄位轉回前端格式
↓
畫面顯示寵物卡片

在這之前,我一直把「資料存在」和「畫面出現」當成同一件事。

真正做過後才發現,資料庫負責保存資料,前端畫面則需要透過 API 取得資料後才能顯示。


API 成功,不代表畫面會自己更新

這次實作讓我印象最深的一件事,就是:

API 成功,不代表前端畫面會自己更新。

即使新增請求已經成功,資料也確實寫入資料庫,Dashboard 仍然可能維持原本的內容。

因為前端目前顯示的,可能還是新增前取得的那一份資料。

如果前端沒有重新取得最新資料,或沒有把新增結果加入畫面使用的資料清單,新的寵物卡片就不會自動出現。

PawPal 的實作採用第二種做法:Dashboard 透過 Pinia 的 petStore 建立寵物後,將 POST 回傳的資料正規化,再直接放進 pets 清單最前面。

以下同樣為依照實作整理的簡化概念

const result = await createPetApi(payload)
const createdPet = normalizePet(result.data.pet)

pets.value = [createdPet, ...pets.value]

Vue 會依這個清單重新渲染寵物卡片,所以新增成功後不需要手動重新整理頁面。

這也是我第一次開始理解「畫面狀態」的重要性。

前端不會因為資料庫改變,就自動知道要重新顯示什麼。

必須由程式主動更新資料,畫面才會跟著改變。


從資料庫回到首頁寵物卡片

當寵物資料從後端回到前端後,還需要把資料顯示在對應的位置。

在 PawPal 裡,使用者新增的寵物會出現在登入後首頁 Dashboard 的寵物卡片中。

Dashboard 從 petStorepets 資料取得卡片內容,再交給 PetCard 元件顯示寵物名字和照片。

後端回傳的照片欄位是 avatar_url;前端的欄位轉換會把它轉成 photoUrl 與卡片使用的 image,因此照片可以正確顯示在卡片上。

以前我看到一張卡片,只會注意它的版面、文字和圖片。

完成這次功能後,我才知道一張卡片背後其實經過了很多步驟:

表單輸入
↓
POST 新增資料
↓
後端處理
↓
資料庫儲存
↓
POST 回傳新增資料並更新前端清單
↓
卡片渲染

畫面上只是一張寵物卡片,但背後連接了前端、API、後端和資料庫。


當自己的寵物出現在首頁

第一次看到自己的寵物真的出現在首頁卡片上時,我非常有成就感。

那不是隨便輸入的一筆測試資料,而是我自己養、也很喜歡的寵物。

從一開始建立表單、處理欄位,到後來串接 API、確認資料庫,再把資料顯示在首頁,整個過程花了我不少時間。

所以當那張卡片真的出現在畫面上時,我感受到的不是只有:

程式終於沒有報錯。

而是:

我真的從零做出了一個可以使用的功能。

原本只是一張空白的畫面,經過一步一步處理後,開始出現真實的資料。

這也是我第一次感受到,前端開發不只是把畫面切出來,而是讓使用者輸入的內容,真的能被保存、取得並顯示。


這裡說的 CRUD「完成」是什麼?

CRUD 通常代表四種資料操作:

  • Create:新增資料。
  • Read:讀取資料。
  • Update:修改資料。
  • Delete:刪除資料。

但這篇所說的「完成」,不是四個功能都做完了,而是我第一次把新增與讀取真正串成一段完整流程。

也就是:

Create
新增寵物資料
↓
Read
取得並顯示寵物資料

在這個階段,我還沒有把 Update 和 Delete 全部完成。

但當 Create 和 Read 接起來後,這個功能才真正從「資料送得出去」,變成「使用者可以在畫面上看到結果」。

對當時的我來說,這已經是第一次完成一段完整的資料流程。


如果現在重新做一次

如果現在重新做一次,我會從一開始就把新增與顯示一起思考。

不會只問:

資料有沒有成功新增?

還會同時確認:

  • 新增成功後,畫面要如何更新寵物清單。
  • 前端使用的資料格式是否和 Response 一致。
  • 卡片需要的名字和圖片是否能正確顯示。
  • 發生錯誤時,新增視窗是否顯示 API 回傳的訊息。
  • 重新整理後,資料是否仍然存在。

我也會更早使用 Postman 分開測試 API。

先確認後端可以正確處理資料,再處理前端畫面,可以讓我更容易知道問題是在前端、後端,還是資料庫。


這次我學到的事

完成新增與顯示流程後,我學到:

  • 新增資料和顯示資料是兩段不同的流程。
  • POST 負責建立資料,GET 負責取得資料。
  • API 成功不代表前端畫面會自動更新。
  • HTTP status code、JSON Response 和資料庫都能幫助確認結果。
  • 前端不是唯一能呼叫 API 的地方。
  • Postman 可以幫助我分開測試後端功能。
  • 資料庫裡有資料,不代表首頁一定會顯示。
  • Create 和 Read 接起來後,功能才真正從零變成可以使用。

以前我把 CRUD 當成四個需要背起來的英文單字。

真正做過後才發現,它們代表的是使用者操作資料時,一段一段連接起來的流程。

而當自己的寵物第一次出現在首頁時,我也第一次真正感受到:

原來我真的可以把一個想法,做成畫面上可以使用的功能。


下一篇預告

從新增到顯示,我第一次把 Create 和 Read 接成一段完整的資料流程。

但資料建立之後,使用者還會需要修改已經存在的內容。

下一篇,我會從第一次修改寵物資料開始,聊聊我為什麼做到這一步,才真正發現 PUT 和 PATCH 的差別。

Day 9|第一次修改資料,才知道 PUT 和 PATCH 差很多。


上一篇
Day 7|表單、API 與資料庫:欄位沒有統一,資料就對不上
下一篇
Day 9|第一次修改資料,才知道 PUT 和 PATCH 差很多
系列文
從看不懂到做出來,用 PawPal 走過前端新手村12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言