嗨,大家今天過得好嗎?歡迎來到鐵人賽 Day 22。
昨天我們成功在 n8n 中把 AI 產出的菜單「回寫」到 PostgreSQL 資料庫中。現在你的資料庫裡,應該已經躺著幾筆帶有 session_id 的歷史配單紀錄了。
一個完整的 App 不可能只有單向的輸入。今天,我們要在 n8n 建立我們的第二支 API(讀取歷史紀錄),並再次請出 Vibe Coding 幫我們在 SwiftUI 快速刻出一個「歷史訂單列表」,完成 App 的雙向資料流!
我們要開一支全新的 API 給前端拉取歷史資料。請在 n8n 開啟一個新的 Workflow(或者在原本的畫布空白處拉新的節點)。
新增 Webhook 節點:
GET(因為我們只是要「獲取」資料,不包含複雜的 JSON Body)。history(網址會變成 /webhook-test/history)。When Last Node Finishes 即可。新增 PostgreSQL 節點:
Execute Query。SELECT cpu, gpu, total_price, purpose
FROM hardware_history
WHERE session_id = $1
ORDER BY id DESC;
$1 來防止 SQL Injection。這裡有一個全端新手超容易卡關的雷點。
在 Day 11 我們做 POST 請求時,前端傳來的變數藏在 HTTP Body 裡,所以我們在 n8n 是用 {{ $json.body.budget }} 來抓取。
但是,GET 請求是沒有 Body 的! 它的變數會跟在網址後面(例如 ?sessionId=12345)。
所以在設定 PostgreSQL 的參數時,請你務必要改成從 query 中抓取:{{ $json.query.sessionId }}
如果這裡寫錯,你的 SQL 就會永遠撈不到東西,回傳一片空白。
後端的 API 準備好了,現在把視角切回 Xcode。
我們要在 APIService.swift 裡面,加入第二個發送 GET 請求的 Function。你可以手動加,或是用這個 Prompt 請 AI 幫你寫:
請幫我在 APIService 中新增一個 `fetchHistory(sessionId: String)` 的非同步函數。
1. 目標 URL 為:`[http://192.168.](http://192.168.)X.X:5678/webhook-test/history?sessionId=\(sessionId)`
2. 使用 GET 請求。
3. 預期回傳的是一個陣列 (Array),請將它解析為 `[HardwareMenu]` 並回傳。
AI 會產出類似這樣的 Code:
func fetchHistory(sessionId: String) async throws -> [HardwareMenu] {
// 注意這裡的參數是直接拼在網址後面的 (Query String)
let urlString = "[http://192.168.](http://192.168.)X.X:5678/webhook-test/history?sessionId=\(sessionId)"
guard let url = URL(string: urlString) else { throw URLError(.badURL) }
var request = URLRequest(url: url)
request.httpMethod = "GET"
let (data, response) = try await session.data(for: request)
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
throw URLError(.badServerResponse)
}
// 注意!因為回傳的是多筆紀錄,這裡的型別必須加中括號 [HardwareMenu].self
return try JSONDecoder().decode([HardwareMenu].self, from: data)
}
資料層搞定了,最後就是畫面的呈現。為了維持 MVC 架構,我們同樣需要一個 Controller 跟一個 View。
請對 AI 下這段結構化 Prompt:
我需要一個用來顯示歷史紀錄的 SwiftUI 頁面。
請嚴格遵守 MVC 架構,幫我生成 `HistoryController` 與 `HistoryListView`。
【任務要求】
1. HistoryController:包含一個 `[HardwareMenu]` 陣列來存放資料,一個 `isLoading` 狀態,以及一個呼叫 `APIService.shared.fetchHistory` 的函數。
2. HistoryListView:使用 `List` 或 `ScrollView` 來展示陣列資料。每一筆資料請設計成一個精美的卡片 (顯示 CPU、GPU 與總價)。
3. 當畫面出現 (onAppear) 時,自動觸發 Controller 去拉取資料。
幾秒鐘後,你就會得到一個乾淨的 HistoryListView 和對應的 Controller。
為了讓這個頁面能被看見,你可以回到最外層的 App 進入點,或是做一個 TabView 把「首頁」跟「歷史紀錄」兩個 View 包起來。這部分因為是純 SwiftUI 的基礎導覽機制,我就不佔用篇幅了,你可以直接叫 AI 幫你加上底部的 Tab 導航列。
history Webhook 正在監聽中(按下 Execute Workflow)。今天我們完成了全端開發的另一個重要里程碑:打通了 GET 請求的資料流,並透過 Vibe Coding 毫不費力地完成了列表畫面的刻版與資料綁定。
到這裡,我們的「AI 硬體推薦 App」已經具備了完整的 CRUD(讀寫)能力,在商業場景上已經是個合格的 MVP(最小可行性產品)了。
但,軟體開發不可能永遠一帆風順。如果今天 n8n 的某個節點當機了,或是外部的匯率 API 突然掛掉,整個工作流就會中斷,前端也會永遠等不到回應。
明天,我們要把視角切回 n8n,實作後端極度重要的**「Error Trigger (錯誤捕捉) 與 Fallback 機制」**,打造一個不會輕易當機的強韌工作流!
我們 Day 23 見!