前言
上一篇 把同一個 Service Center 掛到 Order Index、Order Status 與 Profile。這個案例的特殊之處是沒有獨立 web server:Customer Account block、Admin App Home 與 Admin App Tools 都是 extension bundles。
「沒有後端」很容易被誤解成「不能讀資料」,也容易反過來被誇大成「Shopify 會幫忙代管所有資料與商業流程」。真正的邊界要看資料在哪裡、surface 是 customer 還是 admin、需不需要 secret、長時間工作與外部系統。
原本的做法與問題症狀
第一個方向是為每個功能先架一台 server、database 與 authentication callback。若需求只是顯示 target context、連到內建訂單頁或讀 Shopify 已提供的資料,這會增加:
- hosting 與 secret management。
- session token validation。
- CORS、timeout、retry 與 observability。
- database retention 與 privacy inventory。
另一個極端是「既然 extension 能直接 query Shopify,就永遠不需要 backend」。只要需求包含第三方保固系統、客服工單、私有計算、secret、queue、scheduled job 或 cross-shop data,這個結論就不成立。
案例還有一個容易混淆的地方:Admin App Home 能用 direct Admin API 查 app-owned metaobjects,不代表 Customer Account block 也自動擁有 Admin API。兩個 surface 的 runtime、identity 與 API contract 不同。
問題是怎麼推敲出來的?
Root package 只有 extension workspaces 與 Shopify CLI scripts,沒有 web framework、server runtime 或 database client。App configuration 使用 Shopify-hosted App Home,Customer Account extension 則由 Shopify 代管 compiled bundle。
Source data flow 分成兩條:
customer surface
→ Customer Account block
→ target context + links
→ api_access = false
→ network_access disabled
admin surface
→ App Home
→ embedded app direct Admin API
→ app-owned metaobjects
→ legacy lookup / FAQ management
Admin lookup code會查 customer summary 與 legacy order metaobjects;但 Customer Account Service Center 沒有 import 這段 model,也沒有 query 或 fetch。這證明「資料模型已存在」和「顧客端已接線」是兩件事。
最後採用的架構
先用能力梯子判斷是否需要 backend:
| 需求 | 優先能力 | 是否需要自家 backend |
|---|---|---|
| 顯示 target 提供的 order/account context | Target APIs | 否 |
| 查 profile、orders、addresses | Customer Account API | 通常否 |
| 查 products、collections、公開 storefront data | Storefront API+api_access |
通常否 |
| 讀預先寫入的 customer/order metafield | Customer Account API/metafield API | 通常否 |
| 管理員操作 app-owned data | Admin extension direct API | 視 surface 與操作而定 |
| 呼叫第三方保固、ERP、CRM | network_access+session token |
是 |
| 保存 secret、跑 queue/cron、跨商店彙整 | App backend | 是 |
Extension-only 不是架構目標,而是最小責任的結果。只要 Shopify 平台資料與 extension runtime 已能安全完成需求,就不額外引入 backend;一旦越過能力邊界,就明確新增 server,而不是把 secret 或 privileged logic 塞進 bundle。
關鍵實作
Customer Account API 可直接由 extension 發 request,平台處理 authentication:
const response = await fetch(
'shopify://customer-account/api/2026-07/graphql.json',
{
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
query: `query {
customer {
id
firstName
}
}`,
}),
},
);
是否能讀特定欄位仍受 protected customer data access 約束。
若是 storefront products/collections,啟用:
[extensions.capabilities]
api_access = true
再用 shopify.query()。這個 capability 不是 Admin API scope,也不是任意 external fetch。
只有要呼叫自家 server 或第三方服務時才開:
[extensions.capabilities]
network_access = true
Server 端要驗證 extension 取得的 session token,並處理 sandboxed Web Worker 的 CORS。不能把 API secret、Admin access token 或 third-party credential 編進 extension JavaScript。
實際驗證
2026-07-30 的 source inspection 證明:
- App 沒有獨立 server runtime dependency。
- Customer Account Service Center 沒有 API query 或 external fetch。
api_access關閉,network_access沒有啟用。- Admin App Home 使用 direct Admin API 查 app-owned metaobjects。
- Legacy lookup 位於 admin surface,不是 customer surface。
- 三個既有 compiled bundles 分別約 23 KB、26 KB 與 2 KB。
官方 64 KB 限制是每個 compiled UI extension bundle;不能把三個 bundle 相加後當成同一個 deploy limit。
官方文件查核也確認:Customer Account API direct access 不需額外 capability;Storefront API 需要 api_access;external services 需要 network_access,而 network capability 要先在 Partner Dashboard 申請發布存取。
本次沒有從 clean install 重跑 source project build,沒有 deploy 新 App Version,也沒有用測試 customer 驗證 API fields。既有 bundles 只能當 artifact evidence,不是當日 production proof。
仍然存在的限制
第一,source 的 API versions 不一致:Customer Account block、Admin App Home、Admin tools 與 app config 使用不同季度版本。它們可能都仍在支援窗口,但 release checklist 應統一記錄每個 surface 的 version、breaking change 與 upgrade owner。
第二,沒有 backend 就沒有自家的 durable job、retry queue、server log 與 secret store。若操作需要長時間執行或可追溯重試,不能依賴顧客開著頁面直到完成。
第三,metafield/metaobject 在 Shopify 裡不等於自動適合 customer exposure。要區分 app-owned、merchant-owned、Storefront visibility、Customer Account API 可讀範圍與 protected data。
第四,Admin App Home 的 direct API 執行在管理員權限脈絡;Customer Account extension 執行在登入顧客脈絡。共享 TypeScript model 不會自動共享 authorization。
第五,開啟 network access 後,不只多一個 fetch()。還要有 session token validation、CORS、rate limit、timeout、retry、data minimization、privacy deletion 與 incident response。
最後,Shopify 代管 extension code,不代管外部服務 SLA。售後、保固或 legacy system 只要有一段離開 Shopify,就需要明確 owner 與 fallback。
可以延伸到哪些情境?
可以用一句判斷:
平台已提供、短時間、無 secret、單一登入脈絡
→ 先做 extension-only
外部資料、privileged logic、長時間工作、跨脈絡
→ 加 backend
這比「所有 App 都要有 server」或「UI Extension 完全不需要 server」更實際。架構不是按技術偏好選,而是按資料信任邊界與失敗復原需求選。
下一篇把這條界線放進遷移計畫:從舊會員中心搬到 Shopify New Customer Accounts,功能如何分階段遷移?
參考資料
- Shopify:Customer account UI extensions
- Shopify:Enable extension capabilities
- Shopify:Customer Account target APIs
- Shopify:Storefront API in Customer Account extensions
- Shopify:Authenticated Account API
- Shopify:Protected customer data
以上平台文件查核日期:2026-07-30。
