Service Bindings: Workers Calling Workers, Billed as One

Cloudflare has made Worker-to-Worker communication generally available through Service Bindings. Instead of routing requests between Workers over the public Internet, Service Bindings let one Worker invoke another as a bound service, enabling developers to decompose applications into independently deployable pieces while keeping the execution path inside the Workers runtime.

The feature is aimed at teams that want granular control over deployment. Rather than redeploying a monolithic Worker for every change, a team can split logic across multiple Services, each owned and updated by a different group. That separation reduces the blast radius of a bad release and lets developers test and debug smaller units of code in isolation.

How It Works: A Gateway Example

A typical use case is an API gateway Worker that routes requests and handles authentication while delegating business logic to other Workers. One Cloudflare customer used this model to offload services from legacy infrastructure: the gateway Worker inspects each request, decides whether the user is authorized, and forwards the request either to a legacy application server or to a newly created Worker, depending on the route.

The routing and auth logic can live across multiple Workers. In the reference implementation, an api-gateway Worker is bound to four other Services: auth, get-user, login, and logout. The gateway handles endpoints like /login, /logout, and /getuser; for protected routes, it invokes the auth Worker to validate credentials before forwarding the request to the appropriate service. The get-user Worker then performs the outbound network request to retrieve user data and passes it back through the gateway to the client. Requests to any other endpoint receive a 404.

The api-gateway Worker handles routing and authentication checks for all the available endpoints

The code required in the gateway Worker is minimal:

export default {
 async fetch(request, environment) {
   const url = new URL(request.url);
   switch (url.pathname) {
     case '/login':
       return await environment.login.fetch(request);

     case '/logout':
       return await environment.logout.fetch(request);

     case '/getuser': {
       // Check that the "Authorization" header is sent when authenticated.
       const authCheck = await environment.auth.fetch(request.clone());
       if (authCheck.status != 200) { return authCheck }
       // If the auth check passes, send the request to the /admin endpoint
       return await environment.getuser.fetch(request);
     }
   }
   return new Response('Not Found.', { status: 404 });
 }
}

The actual fetch() call to a bound service looks like any other fetch, but the request stays within the Worker runtime rather than going out over the network.

The api-gateway Worker is bound to auth, get-user, login, and logout Workers via Service Bindings.
Developers can now create a pipeline of Workers that call one another and create a complex series of compute blocks.

Pricing: One Duration, Not Several

Service Bindings carry the same per-invocation cost as any other Worker request, but billing works differently from the traditional per-instance serverless model. Under the classic model—used by AWS Lambda, for instance—each function instance is allocated resources, and the customer pays for those resources for the entire time the instance is alive, even when it is idle waiting on other services.

Cloudflare instead bills a single duration across all Workers triggered by one incoming request. When a Worker is waiting on a bound Service to perform work—the gray portions of a request timeline—that idle time is not charged to the calling Worker, and Cloudflare does not double charge when multiple Workers share compute resources. The billable time is the amount of time a shared compute thread is actually allocated to your code, plus time spent awaiting external dependencies. Overlapping executions within a single request are not charged multiple times.

A request lifetime graphic representing a sample application with multiple subroutines and a network request

This contrasts directly with a Lambda-style model, where the magenta segments in the lifecycle below represent paid time during which a function is simply waiting. Cloudflare can avoid that by sharing compute capacity across the chained Services and passing the savings along.

with the Workers model, resources are shared and you only pay a flattened duration bill
A request lifetime graphic representing a sample application with multiple subroutines and a network request

Getting Started with Service Bindings

To add a binding, navigate to Settings → Variables on your Worker and select Edit Variables under Service Bindings. Once the binding is defined it can be referenced in code like any other variable, and you can call fetch() on it to invoke the linked Service.

Service Bindings are generally available, with efficient pricing

For more details and community support, Cloudflare points developers to its Discord channel at discord.gg/cloudflaredev.