雖然不是重點但這篇文讓我發現Mosir的斷行是break-all XD 好在意
@leemiyinghao: 當我們在開發 Mosir 的中期,選擇了由社群發展出的一套基於 HTTP `multipart/form-data` 的方案,即 GraphQL Multipart Request Specification。 其核心概念是將傳統的 `application/json` Content Type 轉換為 `multipart/form-data`。一個典型的 `multipart/form-data` 請求會包含以下幾個部分: - **operations 部分**: 包含常規的 GraphQL Query 或 Mutation,其中檔案變數會以 null 或特定的佔位符號表示。 - **map 部分**: 定義 operations 中佔位符號與 multipart 請求中實際檔案部分的對應關係。 - **一個或多個檔案 part**: 每個 part 都包含一個實際的二進位檔案。 透過這種方式,Client 就能在單一 HTTP 請求中,同時傳送 GraphQL 操作的 JSON 資料和二進位檔案,伺服器解析後再將檔案注入到 GraphQL Mutation 的上下文中進行處理。 儘管 GraphQL Multipart Request Specification 解決了在 GraphQL 環境下上傳檔案的需求,但它也繼承了 `multipart/form-data` 的固有缺點,特別是在處理複雜情境時: 1. **缺乏斷點續傳 (Resumable Uploads)**: multipart/form-data 屬於「一次性」的傳輸模式。若檔案在上傳過程中中斷(不論是網路不穩、伺服器重啟或客戶端意外關閉),整個上傳都必須從頭開始。這對於上傳大型檔案(數十 MB 甚至 GB 等級)而言,會導致極差的使用者體驗和大量的資源浪費。 2. **Proxy 和處理的複雜性**: - **Proxy 處理壓力**:如果架構中存在反向代理 (Reverse Proxy) 或 API Gateway,當大型檔案透過 `multipart/form-data` 方式上傳時,代理層需要接收並快取整個檔案請求,然後再轉發給後端服務。這可能對代理層造成巨大的記憶體和頻寬壓力,甚至可能因為代理的快取設定或連線逾時而導致上傳失敗。 - **擴充性考量**:透過代理將大量檔案直接轉發到 GraphQL 服務,可能會使 GraphQL 服務承擔過多與其核心職責無關的 I/O 操作,進而影響其處理 GraphQL 查詢與變更的效率。這也讓檔案上傳的水平擴充變得更為困難。
在開發中後期遇到 multipart/form-data 的問題時,在評估後我們選擇改用 TUS (The Upload Server)。TUS 是一個開放的、獨立於 GraphQL 的協定,專為可靠的、可恢復的檔案上傳而設計。它的核心理念,就是將大型檔案拆分成可管理的區塊,並提供斷點續傳的能力。
TUS 在 GraphQL 架構中的角色並非直接融入 GraphQL 的請求處理,而是作為一個外部的、專門的檔案上傳服務,而 GraphQL API 則扮演協調者 (Coordinator) 的角色,尤其在檔案完成上傳後處理業務邏輯方面。
第一步:Client 與 TUS 伺服器互動 (HTTP P...
用過 Go + gqlgen 和 Node.js + TypeORM,反而覺得 GraphQL 被捧的太高了。沒有解決問題,反而製造問題,例如:N+1 需要 Dataloader 處理、無法傳遞二進制…
Facebook 當初設計 GraphQL,是為了讓 Resolver 能從不同微服務整合資料,統一回傳給前端。但很多人把它當成 HTTP API 的替代方案使用,就像 JWT 被誤用為取代 Session 一樣。
開發速度遠不如一個 POST /upload_video API。但前端生態彷彿不用上新技術就會被淘汰。似乎懂得什麼該捨棄,比學會技術還難。
但我現在用 Go + HTMX 超快樂… 🫠