AWS API Gateway

For AWS, the canonical pattern is API Gateway with a Lambda authorizer that calls Fairvisor POST /v1/decision.

Architecture

Client -> API Gateway -> Lambda authorizer -> Fairvisor /v1/decision
                                  | allow
                                  v
                               Backend integration
                                  | reject
                                  v
                              429 to client

Authorizer request mapping

Your authorizer should pass to Fairvisor:

  • X-Original-Method from incoming method
  • X-Original-URI from path + query
  • Authorization from request headers (if present)
  • X-Forwarded-For from request context source IP

Lambda authorizer sketch (Node.js)

export const handler = async (event) => {
  const method = event.requestContext?.http?.method || event.httpMethod || "GET";
  const path = event.rawPath || event.path || "/";
  const query = event.rawQueryString ? `?${event.rawQueryString}` : "";
  const originalUri = `${path}${query}`;

  const headers = {
    "X-Original-Method": method,
    "X-Original-URI": originalUri,
  };

  const auth = event.headers?.authorization || event.headers?.Authorization;
  if (auth) headers.Authorization = auth;

  const ip = event.requestContext?.http?.sourceIp || event.requestContext?.identity?.sourceIp;
  if (ip) headers["X-Forwarded-For"] = ip;

  const res = await fetch(`${process.env.FAIRVISOR_EDGE_URL}/v1/decision`, {
    method: "POST",
    headers,
  });

  if (res.status === 429) {
    return {
      isAuthorized: false,
      context: {
        fairvisor_reason: res.headers.get("x-fairvisor-reason") || "rate_limited",
        retry_after: res.headers.get("retry-after") || "1",
      },
    };
  }

  if (!res.ok) {
    // choose route policy: fail-open or fail-closed
    return { isAuthorized: false };
  }

  return { isAuthorized: true };
};

Gateway response mapping

Configure API Gateway deny responses to preserve 429 semantics and surface:

  • Retry-After
  • X-Fairvisor-Reason

If your authorizer path can only return generic deny, add integration response mapping so client still sees meaningful rate-limit metadata.

Failure policy

Decide explicitly per route class:

  • write/admin routes: fail-closed
  • public/read routes: fail-open only with alerting

Reference: Gateway Failure Policy.

Timeout guidance

  • authorizer->Fairvisor timeout: 200-500 ms target
  • avoid retries in authorizer hot path
  • monitor p95/p99 authorizer latency

Security notes

  • Do not expose Fairvisor publicly; keep it in private network path
  • Trust X-Original-* only from the authorizer boundary
  • Keep FAIRVISOR_EDGE_URL in secure runtime config

Verification checklist

  1. Confirm allow path succeeds through API Gateway
  2. Trigger limit and verify client receives 429
  3. Confirm Retry-After and X-Fairvisor-Reason propagation
  4. Simulate Fairvisor outage and validate fail-open/closed behavior

See Decision API for contract details.