Zuploについて読み、実際に何かをデプロイしてみたいと思っているなら、この記事はまさにあなた向けです。このプラットフォームは習得が早いですが、ドキュメントはポータルフロー、CLIコマンド、学習センター記事に散らばっています。このガイドは、これらの情報を1つのチュートリアルにまとめました:プロジェクトの作成、ルートの公開、APIキー認証とレート制限の追加、カスタムTypeScriptポリシーの記述、エッジへのデプロイ、そしてApidogを使った全体的なテストです。
最終的には、認証、レート制限、自動生成された開発者ポータル、CIフレンドリーなGitワークフローを備え、オリジンサーバーの前に動作するAPIゲートウェイが手に入ります。このウォークスルー全体で約30分かかります。
Zuploが適切なツールかどうかまだ迷っている場合は、関連する投稿「Zuplo APIゲートウェイとは」から始めてください。その他すべてについては、このガイドが省略するエッジケースはZuploドキュメントでカバーされています。
TL;DR
- portal.zuplo.comでサインアップするか、
npm create zuploでローカルプロジェクトを足場組み(scaffold)してください。 config/routes.oas.jsonでルートを定義し、URL Forward Handlerを使ってオリジンに転送してください。- ルートファイルを編集するか、ルートデザイナーをクリックしてインバウンドポリシー(APIキー認証、レート制限、スキーマ検証)を追加してください。
modules/にTypeScriptモジュールとしてカスタムロジックを記述してください。ランタイムは、リクエスト、コンテキスト、環境への型付きアクセスを提供します。- リンクされたGitブランチにプッシュしてプレビュー環境をデプロイしてください。マージして300以上のエッジロケーションに本番環境をデプロイしてください。
- 本番環境に昇格させる前に、Apidogで全てのルートをテストしてください。
- 料金は月間10万リクエストまで無料で、ビルダープランは月額25ドルです。
前提条件
始める前に3つのものが必要です。
- Zuploアカウント
- ゲートウェイの前に配置するオリジンAPI。もし持っていない場合は、送信したものをそのまま返す
https://echo.zuplo.ioを使用してください。 - CLIを使用する場合、Node.js 18以上。
ローカル開発にはコードエディタも必要です。TypeScript拡張機能を備えたVS Codeが最も抵抗の少ない方法であり、Apidog VS Code拡張機能と組み合わせて、エディタを離れることなくリクエストを発行できます。
ステップ1:Zuploプロジェクトを作成する
始める方法は2つあります:ウェブポータルかCLIです。ほとんどのチームはデモが速いためポータルから始め、CI/CDを望むようになったらCLIに移行します。
オプションA:ポータル優先
- portal.zuplo.comにサインインします。
- 「New Project」をクリックし、
acme-gatewayのような名前を付けます。 - 何も自動作成されないように「Empty Project」を選択します。
- コードタブにスターターファイルツリーが開きます。

ポータルはデフォルトでプロジェクトを管理されたGitリポジトリにリンクします。後で設定から独自のGitHub、GitLab、Bitbucket、またはAzure DevOpsリポジリトリを接続できます。
オプションB:CLI優先
CLIは同じプロジェクトレイアウトをローカルに足場組み(scaffold)するため、IDEで編集し、初日からGitを使用できます。
npm create zuplo@latest -- --name acme-gateway
cd acme-gateway
npm install
npm run dev
開発サーバーはポート9000で起動し、ローカルのルートデザイナーへのリンク(http://localhost:9100)を出力します。エディタまたはデザイナーで行った変更は、すぐにホットリロードされます。
デプロイ準備ができたら、ローカルプロジェクトをZuploアカウントにリンクするには:
npx zuplo link
プロンプトが表示されたらアカウントと環境を選択します。ここから、npx zuplo deployが現在のGitブランチをデプロイします。
ステップ2:最初のルートを定義する
config/routes.oas.jsonを開きます。これは、ハンドラーとポリシー用のZuplo拡張機能を持つOpenAPI 3ドキュメントです。GET /v1/productsをオリジンに転送するルートを追加します。
{
"openapi": "3.1.0",
"info": { "title": "Acme Gateway", "version": "1.0.0" },
"paths": {
"/v1/products": {
"get": {
"summary": "List products",
"operationId": "list-products",
"x-zuplo-route": {
"corsPolicy": "anything-goes",
"handler": {
"export": "urlForwardHandler",
"module": "$import(@zuplo/runtime)",
"options": {
"baseUrl": "${env.ORIGIN_URL}"
}
},
"policies": { "inbound": [] }
},
"responses": {
"200": { "description": "Success" }
}
}
}
}
}
いくつか注目すべき詳細があります。x-zuplo-route拡張機能は、それ以外の標準的なOpenAPIファイル内でZuploが機能する場所です。handlerはルートが一致したときに何が起こるかを記述します。urlForwardHandlerは組み込みのプロキシです。${env.ORIGIN_URL}参照は環境変数から取得されるため、環境ごとに異なるバックエンドをターゲットにできます。
ORIGIN_URLは、ポータルの「Settings > Environment Variables」から、またはローカルでconfig/.envを編集して設定します。まだ実際のオリジンがない場合は、https://echo.zuplo.ioを使用してください。
保存すると、ローカル開発サーバーがリロードされます。http://localhost:9000/v1/productsにアクセスすると、エコーされたリクエストが表示されるはずです。デプロイされたゲートウェイは、代わりに最も近いエッジデータセンターから応答します。
ステップ3:APIキー認証を追加する
パブリックAPIには認証情報が必要です。ZuploはマネージドAPIキーサービスを提供しているため、自分でキーストアを構築する必要はありません。
ルートを編集してインバウンドポリシーを追加します。
"policies": {
"inbound": ["api-key-auth"]
}
次に、ポリシー定義をconfig/policies.jsonに追加します(Zuploはポリシーを初めて追加したときにこのファイルを作成します)。
{
"name": "api-key-auth",
"policyType": "api-key-inbound",
"handler": {
"export": "ApiKeyInboundPolicy",
"module": "$import(@zuplo/runtime)",
"options": {
"allowUnauthenticatedRequests": false
}
}
}
次に、コンシューマー(1つ以上のAPIキーを所有するエンティティ)を作成します。
- ポータルの「Services > API Key Service」に移動します。
- 「Create Consumer」をクリックします。
- サブジェクトを
acme-customer-1のような安定した識別子に設定します。 - キーを管理する人のメールアドレスを追加します。
- 生成されたAPIキーをコピーします。
curlでテストします。ヘッダーがない場合、401が表示されるはずです。
curl -i https://YOUR-PROJECT.zuplo.app/v1/products
# HTTP/2 401
ヘッダーがあると、元の200応答が表示されるはずです。
curl -i https://YOUR-PROJECT.zuplo.app/v1/products \
-H "Authorization: Bearer YOUR_API_KEY"
# HTTP/2 200
実際のクライアントからこれを実行したい場合は、ゲートウェイのOpenAPIスペックをApidogにインポートし、Authorization: Bearer {{api_key}}のグローバルヘッダーを設定し、api_keyを環境変数にバインドします。これにより、すべてのルートに対してクリーンなテストインターフェースが数秒で得られます。
ステップ4:ルートのレート制限
レート制限なしでパブリックAPIを公開しないでください。Zuploのデフォルトのレート制限ポリシーは、IPごと、キーごと、またはカスタム属性ごとのスロットリングを提供します。
認証後、インバウンドリストに追加します。
"policies": {
"inbound": ["api-key-auth", "rate-limit-by-key"]
}
config/policies.jsonで定義します。
{
"name": "rate-limit-by-key",
"policyType": "rate-limit-inbound",
"handler": {
"export": "RateLimitInboundPolicy",
"module": "$import(@zuplo/runtime)",
"options": {
"rateLimitBy": "sub",
"requestsAllowed": 60,
"timeWindowMinutes": 1
}
}
}
rateLimitBy: "sub"は、APIキーポリシーからの認証されたサブジェクトに基づいてバケットをキー設定するため、各顧客は独自の1分あたり60リクエストの予算を得ます。匿名トラフィックを制限したい場合は、"ip"に置き換えてください。
60秒以内の61番目のリクエストは、リトライヘッダーを付けて429を返します。70個のリクエストをループで送信し、応答コードの変化を観察してテストします。
for i in {1..70}; do
curl -s -o /dev/null -w "%{http_code}\n" \
https://YOUR-PROJECT.zuplo.app/v1/products \
-H "Authorization: Bearer YOUR_API_KEY"
done | sort | uniq -c
200と表示される行が60行、429と表示される行が10行表示されるはずです。
ステップ5:リクエストペイロードを検証する
JSONボディを受け取るPOSTルートがある場合、リクエスト検証ポリシーは、オリジンではなくゲートウェイで不正な形式のペイロードを捕捉します。これはOpenAPI操作に埋め込まれたJSON Schemaを使用するため、仕様が正確であればこれを無料で利用できます。
リクエストボディを持つルートを追加します。
"/v1/products": {
"post": {
"summary": "Create product",
"operationId": "create-product",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": ["name", "priceCents"],
"properties": {
"name": { "type": "string", "minLength": 1 },
"priceCents": { "type": "integer", "minimum": 1 },
"category": { "type": "string", "enum": ["food", "drink"] }
}
}
}
}
},
"x-zuplo-route": {
"handler": { /* same as above */ },
"policies": {
"inbound": [
"api-key-auth",
"rate-limit-by-key",
"validate-request"
]
}
}
}
}
ポリシーを追加します。
{
"name": "validate-request",
"policyType": "open-api-request-validation-inbound",
"handler": {
"export": "OpenApiRequestValidationInboundPolicy",
"module": "$import(@zuplo/runtime)",
"options": {
"validateBody": "reject"
}
}
}
これで、フィールドが欠落しているPOSTリクエストは、オリジンに到達する前に400で拒否されます。「正常系」リクエスト、「必須フィールド欠落」リクエスト、「不正な列挙値」リクエストを同じリクエストグループの個別の例として保存し、Apidogでテストしてください。これら3つすべてをワンクリックで実行できます。
ステップ6:カスタムTypeScriptポリシーを記述する
事前構築されたポリシーは、ほとんどのチームが必要とするものをカバーしています。しかし、Zuploのポイントは、カスタムなものが必要になった瞬間です。ここでは、有料顧客にはCache-Controlヘッダーを、無料顧客にはno-storeを追加するアウトバウンドポリシーを示します。
modules/tiered-cache.tsを作成します。
import { ZuploRequest, ZuploContext, HttpProblems } from "@zuplo/runtime";
interface PolicyOptions {
paidPlanHeader: string;
paidMaxAge: number;
}
export default async function (
response: Response,
request: ZuploRequest,
context: ZuploContext,
options: PolicyOptions,
): Promise<Response> {
const plan = request.user?.data?.plan ?? "free";
if (plan === "free") {
response.headers.set("Cache-Control", "no-store");
} else {
response.headers.set(
"Cache-Control",
`public, max-age=${options.paidMaxAge}`,
);
}
context.log.info(`Cache header set for plan=${plan}`);
return response;
}
config/policies.jsonに組み込みます。
{
"name": "tiered-cache",
"policyType": "custom-code-outbound",
"handler": {
"export": "default",
"module": "$import(./modules/tiered-cache)",
"options": {
"paidPlanHeader": "x-plan",
"paidMaxAge": 300
}
}
}
そしてルートから参照します。
"policies": {
"inbound": ["api-key-auth", "rate-limit-by-key"],
"outbound": ["tiered-cache"]
}
カスタムポリシーは単なる関数です。統合ハーネスは必要なく、合成のResponseとZuploRequestを渡してヘッダーをアサートすることで、VitestまたはJestで単体テストできます。
ステップ7:エッジにデプロイする
デプロイはGitプッシュです。
git add .
git commit -m "Add products gateway with auth, rate limit, and tiered cache"
git push origin feature/products-gateway
Zuploはすべてのブランチに対してプレビュー環境を構築し、ビルドログにURLを出力します。プレビューは、https://acme-gateway-feature-products-gateway-abc123.zuplo.appのような独自のサブドメインを取得し、すべてのポリシーが有効になり、その環境に設定されたORIGIN_URLを指します。
プロジェクトで新しい環境として設定し、ApidogでプレビューURLをテストします。それに対して完全なテストスイートを実行します。すべてがパスしたら、ブランチをマージします。
git checkout main
git merge feature/products-gateway
git push origin main
マージにより本番デプロイがトリガーされます。60秒以内に新しいバージョンは300以上のエッジロケーションでライブになります。昇格とロールバックはどちらもgit push操作であり、個別のUIはありません。
ステップ8:開発者ポータルを生成する
ポータルはhttps://YOUR-PROJECT.developers.zuplo.comでホストされており、デプロイごとに再構築されます。これには以下が含まれます。
- ルートごとに1つのページがあり、スキーマ、説明、および試用コンソールがあります。
- cURL、JavaScript、Python、Go、その他いくつかの言語のコードサンプル。
- サインアップした訪問者向けのセルフサービスAPIキー発行。
- ポータルの「Developer Portal > Settings」にあるブランディング制御。
OpenAPI仕様に適切な説明と例があれば、ポータルは追加作業なしで完成したように見えます。仕様が薄い場合は、ここでそのことが明らかになります。
カスタマイズするには、ポータルソースは別のNext.jsアプリとして提供されており、GitHubのZuplo開発者ポータルリポジトリからフォークできます。ほとんどのチームはホストされたバージョンを使用します。
ステップ9:Apidogですべてをテストする
ゲートウェイが稼働したら、本番環境でのインシデントを防ぐ規律は、すべてのルート、すべてのポリシー、すべてのエラーパスをテストすることです。Apidogはこれを迅速に行います。

うまく機能するワークフロー:
https://YOUR-PROJECT.zuplo.app/openapiからゲートウェイのOpenAPI仕様をインポートします。Apidogは各操作を発行できるリクエストに変換します。local、preview、productionの環境を作成し、それぞれ独自のbase_urlとapi_keyを設定します。- ルートごとに最低3つのリクエストを保存します:正常系、認証失敗、レート制限トリガー。デプロイごとにこれらをグループとして実行します。
- Apidogの自動テストシナリオを使用して、呼び出しを連結(製品の作成、リスト表示、削除)し、応答の形状をアサートします。
- チームの主要言語でコードサンプルを生成し、ランブックに貼り付けます。
Postmanから移行する場合は、PostmanなしのAPIテストガイドでインポートについて説明されています。Apidogをダウンロードしていない場合は、ダウンロードしてください。
Zuploの使用に関するよくある質問
仕様を変更せずに、環境間でルートを切り替えるにはどうすればよいですか?
環境変数を使用します。ポータルの設定またはローカルのconfig/.envで環境ごとにORIGIN_URLを定義し、ハンドラーオプション内で${env.ORIGIN_URL}として参照します。ルートは同じままで、変数のみが変更されます。
Zuploをオフラインで実行できますか?
はい。npm run devはポート9000でローカルゲートウェイを起動し、ポート9100でローカルルートデザイナーを起動します。カスタムポリシー、検証、レート制限はすべてローカルで機能します。インターネット接続を必要とする唯一のものはマネージドAPIキーサービスであり、npx zuplo linkを実行することでローカルインスタンスからクラウドサービスを使用できます。
不正なデプロイをロールバックするにはどうすればよいですか?
マージコミットをgit revertしてプッシュします。Zuploは以前の状態を再デプロイします。Git履歴が真実のソースであるため、個別の「ロールバック」ボタンはありません。
デプロイ中に進行中のリクエストはどうなりますか?
デプロイはエッジでアトミックに行われます。進行中のリクエストは古いバージョンで完了し、新しいリクエストは新しいバージョンに到達します。ダウンタイム期間はありません。
ZuploをgRPCまたはWebSocketsで使用できますか?
はい。urlForwardHandlerはWebSocketのアップグレードを透過的にプロキシし、gRPCはgRPCハンドラーを介してサポートされます。RESTとGraphQLはファーストクラスであり、最も一般的なケースです。
Zuplo APIをAIエージェントに公開するにはどうすればよいですか?
ルートにMCPサーバーハンドラーを追加し、OpenAPI仕様を指し、公開する操作を選択します。MCPリクエストにも同じ認証およびレート制限ポリシーが適用されます。Zuplo MCPサーバーのドキュメントで設定について説明されています。
本番環境でのゲートウェイの費用はいくらですか?
無料ティアは月間10万リクエストをカバーします。ビルダープランは月額25ドルで100万リクエストを追加し、追加リクエストは10万リクエストごとに100ドルかかります。エンタープライズ料金は年間契約で月額1,000ドルから始まります。詳細はZuplo料金ページで確認できます。
結論
これで、APIキー認証、キーごとのレート制限、リクエスト検証、カスタムTypeScriptアウトバウンドポリシー、および開発者ポータルを備え、Gitを介してグローバルエッジにデプロイされるZuploゲートウェイが機能するようになりました。同じプロジェクトで、プレビュー環境、本番ロールアウト、MCPを介したAIエージェントアクセスを処理します。
それを安定させる要素はテストループです。マージする前にすべてのプレビューに対してApidogを使用することで、破損した認証ヘッダー、欠落したスキーマフィールド、誤って寛大すぎたレート制限が出荷される前に発見できます。Apidogをダウンロードして、今すぐゲートウェイに組み込みましょう。
