1. Examples
  2. Basic API Gateway

Basic API Gateway

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

Deploy to Zuplo
Deploy to Zuplo

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

This example demonstrates how to build a production-ready API gateway with Zuplo. It showcases the core features you need to protect and manage an API: authentication, rate limiting, request validation, and request size limits, all configured declaratively without writing code.

The gateway proxies a sample Todo API, demonstrating how Zuplo sits in front of your backend to add security and control.

What You'll Learn

By working through this example, you'll understand how to:

  • Set up API key authentication to control access to your API
  • Configure rate limiting to protect your backend from abuse
  • Validate incoming requests against a JSON schema
  • Limit request body size to prevent oversized payloads
  • Forward requests to an upstream backend API

Prerequisites

  • A Zuplo account. You can sign up for free.

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 basic-api-gateway

Then install dependencies and start the development server:

Terminalbash
cd basic-api-gateway
npm install
npm run dev

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.

Setting Up API Keys

This example uses API key authentication, for this you'll need to create a real API key:

  1. Create an API Key Bucket (if you don't have one already)

    • In the Zuplo Portal, navigate to Services → API Key Service
    • Click Configure to create a new API key bucket for your project
  2. Create a Consumer

    • In the API Key Service, click Create Consumer
    • Enter a name for the consumer (e.g., "Test User")
    • Optionally add metadata like an email address
    • Click Create
  3. Get Your API Key

    • After creating the consumer, click on them to view their details
    • Copy the API key value (it will look something like zpka_d67b7e241bb948758f415b79aa8exxxx_2efbxxxx)
    • Use this key in the Authorization: Bearer YOUR_API_KEY header when making requests

Tip: You can also manage API keys programmatically using the Zuplo API.

How This Example Works

This example creates an API gateway that proxies requests to a backend Todo API (https://todo.zuplo.io) while applying several policies to each request:

  1. API Key Authentication: Validates the Authorization header contains a valid API key
  2. Rate Limiting: Limits requests to 2 per minute per authenticated user
  3. Request Validation: Validates request bodies against JSON schemas defined in the OpenAPI spec
  4. Request Size Limit: Rejects requests with bodies larger than 1000 bytes

All requests pass through these policies before being forwarded to the upstream backend.

Why These Policies?

These four policies represent the foundation of a secure, production-ready API. Here's why each one matters:

API Key Authentication

API keys are the most common way to authenticate API consumers. They allow you to:

  • Identify who is calling your API so you can track usage, apply rate limits per consumer, and revoke access when needed
  • Monetize your API by tying keys to billing plans or usage tiers
  • Control access by enabling or disabling keys without changing your backend

In this example, every request must include a valid API key. Unauthenticated requests are rejected before they ever reach your backend.

Rate Limiting

Rate limiting protects your API from abuse and ensures fair usage across all consumers. Without it:

  • A single consumer could overwhelm your backend with requests
  • Malicious actors could launch denial-of-service attacks
  • Costs could spiral out of control from unexpected traffic spikes

This example limits each authenticated user to 2 requests per minute (intentionally low for easy testing). In production, you'd typically set higher limits based on your API's capacity and pricing tiers.

Request Validation

Validating requests at the gateway catches malformed data before it reaches your backend. This:

  • Reduces backend load by rejecting invalid requests early
  • Improves security by ensuring only well-formed data gets through
  • Provides better error messages to API consumers about what's wrong with their request

This example validates request bodies against the JSON schemas defined in the OpenAPI spec, rejecting requests that don't match the expected structure.

Request Size Limit

Limiting request body size prevents large payloads from consuming excessive resources. This protects against:

  • Memory exhaustion from processing massive request bodies
  • Denial-of-service attacks using oversized payloads
  • Unexpected costs from processing and storing large amounts of data

This example limits request bodies to 1000 bytes. Adjust this based on your API's legitimate use cases.

Project Structure

text
├── config/
│   ├── routes.oas.json    # OpenAPI spec with route definitions and policies
│   └── policies.json      # Policy configuration (rate limits, auth settings, etc.)
├── docs/                  # Developer portal configuration (Zudoku)
│   ├── zudoku.config.tsx  # Portal configuration
│   └── pages/             # Documentation pages
├── modules/
│   └── hello-world.ts     # Example custom handler (not used in this example)
├── env.example            # Example environment variables
└── zuplo.jsonc            # Zuplo project configuration

Key files to explore:

  • config/routes.oas.json - This is where your API routes are defined using OpenAPI format. Each route specifies which policies to apply and how to handle requests.
  • config/policies.json - Contains the configuration for all policies (authentication, rate limiting, etc.). Modify this file to adjust policy behavior.
  • modules/hello-world.ts - An example of a custom request handler. This example doesn't use it (routes forward to an external API instead), but it shows how you can write custom TypeScript logic.

API Endpoints

The gateway exposes the following endpoints:

MethodPathDescription
GET/todosRetrieve all todo items
POST/todosCreate a new todo item
PUT/todos/{id}Update an existing todo
DELETE/todos/{id}Delete a todo

Key Policy Options to Experiment With

PolicyOptionDescription
API KeyallowUnauthenticatedRequestsWhen false, rejects requests without valid API keys
API KeycacheTtlSecondsHow long to cache API key validation results
Rate LimitrateLimitByRate limit per user, ip, or function
Rate LimitrequestsAllowedMaximum requests allowed in the time window
Rate LimittimeWindowMinutesTime window for rate limiting
Request ValidationvalidateBodyHow to handle invalid bodies: reject-and-log, log-only, or none
Request Size LimitmaxSizeInBytesMaximum allowed request body size

Running the Example

If you haven't already, start the API Gateway:

Terminalbash
npm run dev

The server will start on http://localhost:9000. You should see output indicating the server is ready.

Deployed projects: If you deployed to Zuplo, your API will be available at a URL like https://your-project-name.zuplo.dev. You can find this URL in the Zuplo Portal.

Testing the API

Use curl to test the API. Both locally and when deployed, you need a real API key from your API Key Service (see Setting Up API Keys above).

Terminalbash
curl http://localhost:9000/todos \
  -H "Authorization: Bearer YOUR_API_KEY"

Expected response (200 OK):

JSONjson
[
  {"id": 1, "title": "Buy groceries", "completed": false, "userId": 123},
  {"id": 2, "title": "Write documentation", "completed": true, "userId": 456}
]

Testing the Policies

Authentication

Try making a request without an API key to see authentication in action:

Terminalbash
curl http://localhost:9000/todos

Expected response (401 Unauthorized):

JSONjson
{"type":"https://httpproblems.com/http-status/401","title":"Unauthorized","status":401,"detail":"No authorization header was found"}

Rate Limiting

Send more than 2 requests within a minute to see rate limiting in action:

Terminalbash
# These should succeed (requests 1 and 2)
curl http://localhost:9000/todos -H "Authorization: Bearer YOUR_API_KEY"
curl http://localhost:9000/todos -H "Authorization: Bearer YOUR_API_KEY"

# This should return 429 Too Many Requests (request 3)
curl http://localhost:9000/todos -H "Authorization: Bearer YOUR_API_KEY"

Expected response on the third request (429 Too Many Requests):

JSONjson
{"type":"https://httpproblems.com/http-status/429","title":"Too Many Requests","status":429,"detail":"Rate limit exceeded"}

Tip: Wait 60 seconds for the rate limit to reset, or create a second API key consumer to test again.

Request Validation

Send an invalid request body to see validation errors:

Terminalbash
# Missing required field "userId"
curl -X POST http://localhost:9000/todos \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title": "Invalid todo"}'

Expected response (400 Bad Request):

JSONjson
{"type":"https://httpproblems.com/http-status/400","title":"Bad Request","status":400,"detail":"Body schema validation failed","errors":[{"path":"userId","message":"Required"}]}

Request Size Limit

Send a request body larger than 1000 bytes to see the size limit in action:

Terminalbash
# This creates a request body larger than the 1000 byte limit
curl -X POST http://localhost:9000/todos \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title": "This is a very long title that we are using to test the request size limit policy. We need to make this string long enough to exceed 1000 bytes which is the configured limit for this example. Adding more text here to ensure we go over the limit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris. More text to push us over the edge of the limit. This should definitely be enough now to trigger the policy and return an error response to the client.", "userId": 1}'

Expected response (413 Payload Too Large):

JSONjson
{"type":"https://httpproblems.com/http-status/413","title":"Payload Too Large","status":413,"detail":"Request body is too large"}

Extending This Example

Here are some ways to extend this basic gateway:

  • Connect your own backend: Replace https://todo.zuplo.io with your API URL in routes.oas.json
  • Customize rate limits: Adjust limits per endpoint or use dynamic rate limiting
  • Add custom logic: Create custom handlers or custom policies

Learn More

  • Zuplo Documentation
  • API Key Authentication
  • Rate Limiting
  • Request Validation
  • Request Size Limit
  • URL Forward Handler

Quick Links

View on GitHubDocumentation

Run Locally

Clone and run this example:

npx create-zuplo-api --example basic-api-gateway

On This Page

Related Examples

Explore more examples in this category

Canary Routing

Routing

Gradually roll out new backend versions by routing a percentage of traffic to canary before full deployment.

View Example

Environment-Based Routing

Routing

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

View Example
Starter

Dev Portal with API Keys

Authentication

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

View Example

Backend for Frontend (BFF) Auth

Authentication

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

View Example
Check all of our Examples

Scale your APIs with
confidence.

Start for free or book a demo with our team.
Book a demoStart for Free
SOC 2 TYPE 2High Performer Spring 2025Momentum Leader Spring 2025Best Estimated ROI Spring 2025Easiest To Use Spring 2025Fastest Implementation Spring 2025

Get Updates From Zuplo

Zuplo logo
© 2026 zuplo. All rights reserved.
Products & Features
API ManagementAI GatewayMCP ServersMCP GatewayDeveloper PortalRate LimitingOpenAPI NativeGitOpsProgrammableAPI Key ManagementMulti-cloudAPI GovernanceMonetizationSelf-Serve DevX
Developers
DocumentationBlogLearning CenterCommunityChangelogIntegrations
Product
PricingSupportSign InCustomer Stories
Company
About UsMedia KitCareersStatusTrust & Compliance
Privacy PolicySecurity PoliciesTerms of ServiceTrust & Compliance
Docs
Pricing
Sign Up
Login
ContactBook a demoFAQ
Zuplo logo
DocsPricingSign Up
Login