---
author: OOMOL
author_url: https://oomol.com/zh-tw/about/
title: 透過 SaaS Proxy 呼叫供應商 API
description: 使用 Project API key 與終端使用者已連接的帳號，透過 SaaS Proxy 呼叫供應商 API。
lang: zh-TW
canonical_url: https://oomol.com/zh-tw/docs/saas-proxy/
markdown_url: https://oomol.com/zh-tw/docs/saas-proxy.md
---

# 透過 SaaS Proxy 呼叫供應商 API

需要呼叫尚未封裝為 Connector 操作的供應商介面時，可以使用 SaaS Proxy。後端指定介面位址、方法與參數，Connector 選擇終端使用者已連接的帳號並注入供應商憑證。供應商必須支援 proxy，呼叫仍受帳號既有權限限制。

## 準備已連接的帳號

先完成 [Connector for SaaS 接入指南](/zh-tw/docs/connector-saas/)，將 Project API key 儲存在後端，並保存經過驗證的使用者與帳號綁定關係。使用 oo_proj_… 形式的 Project key，不要使用 OOMOL 個人 key 或供應商 token。根據產品已登入使用者決定 userId 與帳號，不要直接信任瀏覽器提交的任意帳號 ID。

## 透過 HTTP 呼叫

```http
POST https://connector.oomol.com/v1/saas/proxy/{service}
Authorization: Bearer <project-api-key>
Content-Type: application/json
```

以下 Gmail 唯讀範例取得已連接帳號的個人資料。將服務設定與連接帳號 ID 替換為後端保存的值，帳號需已授權讀取資料。外層 HTTP 方法一律為 POST，供應商請求的方法寫在 request 內。

```bash
export PROJECT_API_KEY='oo_proj_...'
export PROVIDER_CONFIG_ID='<gmail-provider-config-id>'
export CONNECTED_ACCOUNT_ID='<connected-account-id>'

curl --silent --show-error \
  'https://connector.oomol.com/v1/saas/proxy/gmail' \
  -H "Authorization: Bearer $PROJECT_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'x-request-id: req-saas-proxy-1' \
  --data @- <<JSON
{
  "providerConfigId": "$PROVIDER_CONFIG_ID",
  "userId": "customer-1",
  "connectedAccountId": "$CONNECTED_ACCOUNT_ID",
  "request": {
    "endpoint": "/users/me/profile",
    "method": "GET"
  }
}
JSON
```

## 請求欄位

| 欄位 | 用法 |
| --- | --- |
| `userId` | 必填：產品內的終端使用者 ID，必須與連接帳號對應。 |
| `providerConfigId` | 選填，建議明確填入服務設定 ID，其服務必須與 URL 一致。省略時，專案中該服務必須只有一個有效設定。 |
| `connectedAccountId` / `alias` | 選填，最多填入一個。都省略時，選擇該使用者在此服務設定下最新的 active 帳號。 |
| `request.endpoint` | 必填：以 / 開頭、附加至供應商 Base URL 的路徑，或位於允許來源的完整 HTTPS URL。Gmail 的 Base URL 為 https://gmail.googleapis.com/gmail/v1。 |
| `request.method` | 必填：GET、POST、PUT、PATCH 或 DELETE。 |
| `request.query` | 選填：值為字串、數字、布林值或 null 的物件。null 項目會被略過，不接受陣列或巢狀物件。 |
| `request.headers` | 選填：非驗證用途的標頭。Connector 會注入憑證，不要填入 Authorization、Cookie、Host 或 X-API-Key。 |
| `request.body` | 選填：傳送至供應商寫入介面的 JSON 或文字。GET 不傳送請求本文。 |

## 透過 ProjectConnector 呼叫

使用包含 ProjectConnector.proxy 的 @oomol-lab/connector 版本。如果已安裝版本沒有此方法，可先使用 HTTP 範例。SDK 使用 externalUserId 與 connectionName，傳送時轉換為 userId 與 alias；connectedAccountId 優先於 connectionName。泛型用於描述預期回應型別，不會在執行階段驗證供應商資料。

```sh
npm install @oomol-lab/connector
```

```ts
import { ProjectConnector } from "@oomol-lab/connector";

const project = new ProjectConnector({
  apiKey: process.env.PROJECT_API_KEY!,
});

const result = await project.proxy<{ emailAddress: string }>(
  "customer-1",
  "gmail",
  { endpoint: "/users/me/profile", method: "GET" },
  {
    providerConfigId: process.env.PROVIDER_CONFIG_ID!,
    connectedAccountId: process.env.CONNECTED_ACCOUNT_ID!,
    retries: 0,
  },
);

if (result.status < 200 || result.status >= 300) {
  throw new Error(`Provider returned HTTP ${result.status}`);
}
console.log(result.data.emailAddress);
```

同一使用者需要多次呼叫時，可以先綁定使用者：

```ts
const user = project.forUser("customer-1");
const profile = await user.proxy<{ emailAddress: string }>(
  "gmail",
  { endpoint: "/users/me/profile", method: "GET" },
  {
    providerConfigId: process.env.PROVIDER_CONFIG_ID!,
    connectionName: "work",
    retries: 0,
  },
);
if (profile.status < 200 || profile.status >= 300) {
  throw new Error(`Provider returned HTTP ${profile.status}`);
}
console.log(profile.data.emailAddress);
```

## 讀取回應與處理錯誤

以下為 HTTP 回應結構範例。SDK 直接回傳其中的 data 物件，即 status、headers 與 data。透過 HTTP 呼叫時，可保存 meta.executionId 供排查使用。

```json
{
  "success": true,
  "message": "OK",
  "data": {
    "status": 200,
    "headers": { "content-type": "application/json" },
    "data": {
      "emailAddress": "user@example.com",
      "messagesTotal": 120,
      "threadsTotal": 80,
      "historyId": "12345"
    }
  },
  "meta": {
    "executionId": "exec-example",
    "service": "gmail",
    "appId": "app-example"
  }
}
```

需要檢查兩層結果。Connector 成功回傳時，data.status 仍可能是供應商的非 2xx 狀態碼；SDK 對應檢查 result.status，同時檢查供應商回應本文。Connector 本身的失敗會在 SDK 中擲出 ConnectorError。Project API key 無效時，HTTP 可能回傳 401 與純文字 Unauthorized，因此不要假定所有錯誤回應都是 JSON。

## 限制與疑難排解

- Proxy 回傳解析後的 JSON 或文字，不是串流或二進位傳輸介面。預設上游逾時為 30 秒，回應大小限制為 4 MiB；個別供應商可以覆寫這些限制。

- Proxy 不會增加 OAuth 權限。供應商因 scope 不足拒絕請求時，檢查服務設定，並讓使用者重新授權所需權限。僅接受供應商允許的來源，不能代理任意 URL。

- provider_config_not_found 與 multiple_provider_configs_found 用於排查設定選擇；proxy_not_supported 表示供應商不支援 proxy。遇到 proxy_upstream_timeout 或 proxy_response_too_large 時，盡可能縮小請求或回應範圍。

- 逾時不代表供應商寫入失敗。可能產生重複效果的操作應關閉自動重試，SDK 範例已將 retries 設為零。記錄請求 ID 與 HTTP 回應中存在的 meta.executionId，並到 OOMOL Console 的專案執行記錄中排查。

繼續閱讀 [ProjectConnector 帳號接入](/zh-tw/docs/project-connector/)。
