Zuplo logo

Environment-Based Routing

Route requests to sandbox or production backends based on API key metadata, like Stripe.

Prerequisite: You need a Zuplo account to run this example. Sign up for free

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:

Terminalbash
npx create-zuplo-api@latest --example environment-routing

Then install dependencies:

Terminalbash
cd environment-routing
npm install

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:

Terminalbash
cp env.example .env

We have included two mock APIs (powered by Mockbin) that already work with this example.

text
SANDBOX_BACKEND_URL=https://3e99109e442f4df598c518114b250e0d_oas.api.mockbin.io
PRODUCTION_BACKEND_URL=https://9b720748f6564204b6e2e0baa095d779_oas.api.mockbin.io

For deployed projects:

You must also set these environment variables in the Zuplo Portal:

  1. Go to your project in the Zuplo Portal
  2. Navigate to Settings → Environment Variables
  3. Add SANDBOX_BACKEND_URL and PRODUCTION_BACKEND_URL with 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.

  1. In the Zuplo Portal, open your project
  2. Navigate to Services → API Key Service
  3. Click Configure to create an API key bucket (if you haven't already)

To connect your local development environment to Zuplo services (API keys, rate limiting, etc.), run:

Terminalbash
npx zuplo link

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.zuplo or .env to 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:

  1. In the Zuplo Portal, go to Services → API Key Service
  2. Click Create Consumer
  3. Enter a name (e.g., "Sandbox User")
  4. In the Metadata field, enter:
    JSONjson
    {
      "environment": "sandbox"
    }
  5. Click Create and copy the generated API key

Repeat the process to create a production key with this metadata:

JSONjson
{
  "environment": "production"
}

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:

  1. Reads the environment field from the API key's metadata (request.user.data.environment)
  2. Sets context.custom.backendUrl to the appropriate backend URL based on that value
  3. 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

text
├── config/
│   ├── routes.oas.json    # OpenAPI spec with route definitions
│   └── policies.json      # Policy configuration
├── modules/
│   └── environment-routing.ts  # Custom policy that reads API key metadata
├── .env.example           # Example environment variables
└── .env.zuplo             # Generated by `zuplo link` (do not commit)

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:

MethodPathDescription
GET/v1/balanceRetrieve account balance
POST/v1/chargesCreate a new charge
GET/v1/charges/{chargeId}Retrieve a charge by ID

Running the Example

Start the API Gateway:

Terminalbash
npm run dev

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

Terminalbash
curl http://localhost:9000/v1/balance \
  -H "Authorization: Bearer YOUR_SANDBOX_API_KEY"

Expected response (note livemode: false and test amounts):

JSONjson
{
  "object": "balance",
  "available": [
    {
      "amount": 50000,
      "currency": "usd",
      "source_types": {
        "card": 45000,
        "bank_account": 5000
      }
    }
  ],
  "pending": [
    {
      "amount": 1500,
      "currency": "usd",
      "source_types": {
        "card": 1500
      }
    }
  ],
  "livemode": false
}

Request with Production Key

Terminalbash
curl http://localhost:9000/v1/balance \
  -H "Authorization: Bearer YOUR_PRODUCTION_API_KEY"

Expected response (note livemode: true and production amounts):

JSONjson
{
  "object": "balance",
  "available": [
    {
      "amount": 2847593,
      "currency": "usd",
      "source_types": {
        "card": 2134850,
        "bank_account": 712743
      }
    }
  ],
  "pending": [
    {
      "amount": 284650,
      "currency": "usd",
      "source_types": {
        "card": 284650
      }
    }
  ],
  "livemode": true
}

Creating a Charge

Terminalbash
curl -X POST http://localhost:9000/v1/charges \
  -H "Authorization: Bearer YOUR_SANDBOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 2000,
    "currency": "usd",
    "description": "Order #1234",
    "metadata": {
      "order_id": "1234"
    }
  }'

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 staging or development
  • Customer-specific routing: Route to customer-specific backends using a customerId field in the metadata
  • Dynamic configuration: Use BackgroundLoader to 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.example to .env and added your backend URLs
  • Configured the API Key Service in Services → API Key Service
  • Ran npx zuplo link and 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:

ErrorCauseFix
401 UnauthorizedMissing or invalid API keyCheck 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 incorrectVerify the key has {"environment": "sandbox"} or {"environment": "production"} in its metadata
SANDBOX_BACKEND_URL is not definedEnvironment variable not setCheck your .env file exists and contains the variable

Learn More

Related Examples

Explore more examples in this category

Starter

Basic API Gateway

Authentication

Protect your backend API with authentication, rate limiting, and request validation.

Starter

Dev Portal with API Keys

Authentication

Let developers sign up and create their own API keys through a self-serve portal.

Backend for Frontend (BFF) Auth

Authentication

Secure your web app by handling authentication server-side without exposing tokens to the browser.