ORM 在騙你:當 populate / include 悄悄失效

症狀:資料明明在,前端就是拿不到 一個 CMS 系統的文件列印預覽,每份文件 header 都固定顯示「上傳者:—」。DB 裡明明有資料,API 回 HTTP 200 OK,回應裡的其他欄位(日期、文件類型、附件)都對——唯獨 uploadedBy 是 null。 沒錯誤、沒警告,伺服器 log 也乾淨。前端碰到 uploadedBy?.realName || '-' 就乖乖畫 dash。使用者以為這份文件沒指派擁有者,作者本人以為畫面壞了。大家都錯,但系統看起來好好的。 這是最難纏的 bug——靜默失敗。 先看 ORM 層在整個架構哪裡 要理解為什麼這類 bug 發生、也為什麼解法是「繞過 ORM」,先把架構分層看清楚: 為什麼 bug 只會發生在 ORM 層? 上面的業務邏輯只負責說「給我文件,順便帶上傳者」——它不知道要怎麼 JOIN。下面的 DB 只回應被問到的 SQL——你沒 SELECT 的欄位它不會主動給你。只有 ORM 層有權力決定「要翻譯成什麼 SQL」,也只有它遇到不會處理的組合時,會選擇「悄悄回你 null」而不是「拋錯給你看」。 這就是為什麼靜默失敗只會出現在這一層。 用 curl 逼它露餡 把 query 簡化到只剩 filter + populate 兩件事,一個一個比對: # A: 用 id filter → 關聯欄位變 null ❌ curl "$URL?filters[owner][id][\$eq]=3807&populate[uploadedBy]=true" # { "uploadedBy": null } # B: 用 documentId filter → 關聯正常回來 ✅ curl "$URL?filters[owner][documentId][\$eq]=abc123&populate[uploadedBy]=true" # { "uploadedBy": { "id": 85, "realName": "J. Chen" } } 同樣的 populate、同樣的文件集合,差別只在「怎麼辨認 owner」。這種事應該是 ORM 內部細節,對使用者不該有差。但就是有。 ...

April 24, 2026 · 2 分鐘 · Peter