This example demonstrates how to route requests to different backend environments based on API key metadata. It implements the same pattern used by Stripe, where developers get separate test and live API keys that hit the same API endpoint but route to different backends.
This pattern is useful for:
- Environment separation: Route users to sandbox or production backends based on their API key
- Customer isolation: Route each customer to their own isolated backend for compliance or data residency
- Hybrid multi-tenant: Route premium customers to dedicated backends while others use shared infrastructure
Prerequisites
- A Zuplo account. You can sign up for free.
- A Zuplo project (you'll need this to use the API Key Service locally)
Working with this Example
Locally
Working locally is the best way to explore and understand the code for this example. You can get a local version by using the Zuplo CLI:
Then install dependencies:
Before running the dev server, you need to complete the setup steps below.
Deploy this example to Zuplo
It is also possible to deploy this example directly to your Zuplo account and work with it via the Zuplo Portal. You can do this by clicking the Deploy to Zuplo button anywhere on this page.
Setup
1. Set Up Environment Variables
This example requires two environment variables pointing to your backend URLs.
For local development:
Copy the example environment file and add your backend URLs:
We have included two mock APIs (powered by Mockbin) that already work with this example.
For deployed projects:
You must also set these environment variables in the Zuplo Portal:
- Go to your project in the Zuplo Portal
- Navigate to Settings → Environment Variables
- Add
SANDBOX_BACKEND_URLandPRODUCTION_BACKEND_URLwith the same backend URLs
2. Set Up the API Key Service
To use API key authentication locally, you need to connect to Zuplo's API Key Service.
- In the Zuplo Portal, open your project
- Navigate to Services → API Key Service
- Click Configure to create an API key bucket (if you haven't already)
3. Link Your Local Environment
To connect your local development environment to Zuplo services (API keys, rate limiting, etc.), run:
Follow the prompts to select your account, project, and environment. This creates a .env.zuplo file that connects your local gateway to Zuplo's services.
Note: Don't commit
.env.zuploor.envto version control. These files contain environment-specific configuration.
For more details, see Connecting to Zuplo Services Locally.
4. Create API Keys
Create two API keys with different environment metadata:
- In the Zuplo Portal, go to Services → API Key Service
- Click Create Consumer
- Enter a name (e.g., "Sandbox User")
- In the Metadata field, enter:
json
- Click Create and copy the generated API key
Repeat the process to create a production key with this metadata:
You should now have two API keys, each configured to route to a different backend.
How It Works
When a request is authenticated using Zuplo's API Key Authentication, user information becomes available on request.user. This includes any metadata you've attached to the API key.
The custom routing policy:
- Reads the
environmentfield from the API key's metadata (request.user.data.environment) - Sets
context.custom.backendUrlto the appropriate backend URL based on that value - The URL Rewrite handler uses this value to forward the request to the correct backend
This means a single API endpoint can transparently route to completely different backends depending on which API key the caller uses.
Project Structure
Key files to explore:
modules/environment-routing.ts: The custom inbound policy that determines which backend to route to based on the API key's metadata.config/routes.oas.json: Defines the API routes using OpenAPI format. Each route uses the URL Rewrite handler with${context.custom.backendUrl}to dynamically select the backend.config/policies.json: Configures the API key authentication and environment routing policies.
API Endpoints
This example exposes a simple payments API to demonstrate the routing:
| Method | Path | Description |
|---|---|---|
GET | /v1/balance | Retrieve account balance |
POST | /v1/charges | Create a new charge |
GET | /v1/charges/{chargeId} | Retrieve a charge by ID |
Running the Example
Start the API Gateway:
The server will start on http://localhost:9000.
Testing the Routing
Use curl to test with different API keys. The response data will differ based on which environment the request routes to.
Request with Sandbox Key
Expected response (note livemode: false and test amounts):
Request with Production Key
Expected response (note livemode: true and production amounts):
Creating a Charge
The response will include test_ prefixed IDs for sandbox keys or live_ prefixed IDs for production keys.
Extending This Example
- Add more environments: Extend the policy to support additional environments like
stagingordevelopment - Customer-specific routing: Route to customer-specific backends using a
customerIdfield in the metadata - Dynamic configuration: Use
BackgroundLoaderto fetch routing rules from an external service - JWT-based routing: The same pattern works with JWT claims instead of API key metadata
Troubleshooting
If requests aren't routing correctly or you're getting errors, work through this checklist:
- Created a Zuplo project in the portal
- Copied
.env.exampleto.envand added your backend URLs - Configured the API Key Service in Services → API Key Service
- Ran
npx zuplo linkand selected your project/environment - Created a sandbox API key with metadata
{"environment": "sandbox"} - Created a production API key with metadata
{"environment": "production"} - Started the dev server with
npm run dev
Common errors:
| Error | Cause | Fix |
|---|---|---|
401 Unauthorized | Missing or invalid API key | Check you're passing the Authorization: Bearer header with a valid key |
403 Forbidden with "API key is not configured for a valid environment" | API key metadata missing or incorrect | Verify the key has {"environment": "sandbox"} or {"environment": "production"} in its metadata |
SANDBOX_BACKEND_URL is not defined | Environment variable not set | Check your .env file exists and contains the variable |