iT邦幫忙

2026 iThome 鐵人賽

DAY 11
0

今日目標說明

今天我們來開發這套系統最常被呼叫的 API:取得持有紀錄列表
還記得 DAY 7 我們把資料正規化成 pokemon_base (基礎圖鑑) 與 user_pokemon_holdings (個人持有紀錄) 兩張表嗎?現在,我們必須在後端用 SQL 的 JOIN 語法,把分離的資料縫合起來,變成一張完整的卡片資訊丟給前端。

核心程式碼片段

src/routes/holdings.ts 中,我們撰寫了這段 JOIN 查詢:

router.get('/', async (req, res) => {
  // 從昨天的 Middleware 取得登入者的資訊
  const userId = req.user.userId; 

  try {
    // 使用 JOIN 把兩張表的資料拼起來
    const result = await db.execute({
      sql: `
        SELECT 
          h.holding_id,
          h.player_id,
          h.gender,
          h.is_shiny,
          b.pokemon_id,
          b.name_zh,
          b.generation
        FROM user_pokemon_holdings h
        -- 核心:透過 pokemon_id 把圖鑑表的資料拉過來
        JOIN pokemon_base b ON h.pokemon_id = b.pokemon_id
        WHERE h.player_id = ?
        ORDER BY b.pokemon_id ASC, h.created_at DESC
      `,
      args: [userId]
    });
    
    res.json(result.rows);
  } catch (error) {
    res.status(500).json({ error: '讀取紀錄失敗' });
  }
});

【技術原理解析】SQL JOIN 語法的效能考量

許多新手一聽到 JOIN 就會擔心:「把兩張表綁在一起搜,效能會不會很差?」
其實,關聯式資料庫對 JOIN 的優化已經非常成熟了,但前提是**「您有沒有建對 Index (索引)」**!

  • 如果沒有索引:資料庫會進行「全表掃描 (Full Table Scan)」,它會把 10 萬筆持有紀錄跟 1000 隻寶可夢圖鑑進行地毯式比對,伺服器絕對會當機。
  • 有建置索引 (Primary Key & Foreign Key):在我們的設計中,pokemon_base.pokemon_id 是 Primary Key,預設就有索引。當資料庫在執行 JOIN ... ON h.pokemon_id = b.pokemon_id 時,它能像查字典一樣,用極快的速度瞬間找到對應的圖鑑資料,即使有幾百萬筆紀錄也能在幾毫秒內回傳!

所以,大膽且正確地使用 JOIN 吧!

實際畫面截圖

撈取持有列表

在 Postman 中帶上 Bearer Token,發送 GET 請求到 /api/holdings,成功撈出包含 name_zhis_shiny 等關聯資料的 JSON 陣列。

小結

JOIN 成功把我們的資料立體化了!能夠「讀」出資料後,明天我們要來實作「寫入」的功能,並且探討一個極為致命的資料庫安全問題:SQL Injection!


上一篇
DAY 10:打造 API 防護罩 (Express Middleware 實作)
系列文
就決定是你了!打造寶可夢持有追蹤系統 (React x Express)11
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言