Cloud · Gateway & Routing · DWG NO. 034

Ambassador

A sidecar specialized for outbound calls — handling retries, TLS, and monitoring for network traffic the service sends out.

TypeGateway/Routing
ScopeOutbound calls from one service
ComplexityMedium
Common inService meshes, legacy client wrapping, multi-cloud routing

Retry, timeout, and TLS logic gets duplicated in every outbound call site

A service calling three downstream APIs wants retries, circuit breaking, and TLS on every one of those calls — implementing that inside the service's own code, at every call site, means the same plumbing is copy-pasted (or slightly reinvented) each time.

A local proxy standing in for the network

An ambassador is a sidecar the service's code talks to as if it were the real remote endpoint — usually over localhost. The ambassador actually makes the outbound network call, applying retries, timeouts, and TLS on the service's behalf, so the service's own code stays a simple local call.

Servicelocalhost callAmbassadorretries · TLS · timeoutreal network callRemote API

An ambassador wrapping outbound calls with retry logic

The service calls `ambassador.get(...)` as if talking directly to the API. Retry logic lives entirely in the ambassador, reusable across every call.

ambassador-proxy.js
class Ambassador {
  async get(path, { retries = 2 } = {}) {
    for (let attempt = 0; attempt <= retries; attempt++) {
      try {
        return await this.#realNetworkCall(path);
      } catch (err) {
        if (attempt === retries) throw err;
        console.log(`ambassador: retrying ${path} (attempt ${attempt + 1})`);
      }
    }
  }

  async #realNetworkCall(path) {
    console.log(`ambassador: calling remote ${path} over TLS`);
    if (path === "/flaky" && Math.random() < 0.5) throw new Error("timeout");
    return { path, status: 200 };
  }
}

// Service code stays simple — it just calls the ambassador locally.
const ambassador = new Ambassador();
const result = await ambassador.get("/flaky");
console.log(result);

Trade-offs