Cloudflare Docs
Workers
Edit this page
Give us feedback
Set theme to dark (⇧+D)

Service Bindings — RPC

Service bindings allow one Worker to call into another, without going through a publicly-accessible URL.

You can use Service bindings to create your own internal APIs that your Worker makes available to other Workers. This can be done by extending the built-in WorkerEntrypoint class, and adding your own public methods. These public methods can then be directly called by other Workers on your Cloudflare account that declare a binding to this Worker.

The RPC system in Workers is designed feel as similar as possible to calling a JavaScript function in the same Worker. In most cases, you should be able to write code in the same way you would if everything was in a single Worker.

​​ Example

For example, the following Worker implements the public method add(a, b):

For example, if Worker B implements the public method add(a, b):

wrangler.toml
name = "worker_b"
main = "./src/workerB.js"
src/workerB.js
import { WorkerEntrypoint } from "cloudflare:workers";
export default class extends WorkerEntrypoint {
async fetch() { return new Response("Hello from Worker B"); }
add(a, b) { return a + b; }
}

Worker A can declare a binding to Worker B:

wrangler.toml
name = "worker_a"
main = "./src/workerA.js"
services = [
{ binding = "WORKER_B", service = "worker_b" }
]

Making it possible for Worker A to call the add() method from Worker B:

src/workerA.js
export default {
async fetch(request, env) {
const result = await env.WORKER_B.add(1, 2);
return new Response(result);
}
}

You do not need to learn, implement, or think about special protocols to use the RPC system. The client, in this case Worker A, calls Worker B and tells it to execute a specific procedure using specific arguments that the client provides. This is accomplished with standard JavaScript classes.

​​ The WorkerEntrypoint Class

To provide RPC methods from your Worker, you must extend the WorkerEntrypoint class, as shown in the example below:

index.js
import { WorkerEntrypoint } from "cloudflare:workers";
export default class extends WorkerEntrypoint {
async add(a, b) { return a + b; }
}

A new instance of the class is created every time the Worker is called. Note that even though the Worker is implemented as a class, it is still stateless — the class instance only lasts for the duration of the invocation. If you need to persist or coordinate state in Workers, you should use Durable Objects.

​​ Bindings (env)

The env object is exposed as a class property of the WorkerEntrypoint class.

For example, a Worker that declares a binding to the environment variable GREETING:

wrangler.toml
name = "my-worker"
[vars]
GREETING = "Hello"

Can access it by calling this.env.GREETING:

import { WorkerEntrypoint } from "cloudflare:workers";
export default class extends WorkerEntrypoint {
fetch() { return new Response("Hello from my-worker"); }
async greet(name) {
return this.env.GREETING + name;
}
}

You can use any type of binding this way.

​​ Lifecycle methods (ctx)

The ctx object is exposed as a class property of the WorkerEntrypoint class.

For example, you can extend the lifetime of the invocation context by calling the waitUntil() method:

import { WorkerEntrypoint } from "cloudflare:workers";
export default class extends WorkerEntrypoint {
fetch() { return new Response("Hello from my-worker"); }
async signup(email, name) {
// sendEvent() will continue running, even after this method returns a value to the caller
this.ctx.waitUntil(this.#sendEvent("signup", email))
// Perform any other work
return "Success";
}
async #sendEvent(eventName, email) {
//...
}
}

​​ Named entrypoints

You can also export any number of named WorkerEntrypoint classes from within a single Worker, in addition to the default export. You can then declare a Service binding to a specific named entrypoint.

You can use this to group multiple pieces of compute together. For example, you might create a distinct WorkerEntrypoint for each permission role in your application, and use these to provide role-specific RPC methods:

wrangler.toml
name = "todo-app"
[[d1_databases]]
binding = "D1"
database_name = "todo-app-db"
database_id = "<unique-ID-for-your-database>"
import { WorkerEntrypoint } from "cloudflare:workers";
export class AdminEntrypoint extends WorkerEntrypoint {
async createUser(username) {
await this.env.D1.prepare("INSERT INTO users (username) VALUES (?)")
.bind(username)
.run();
}
async deleteUser(username) {
await this.env.D1.prepare("DELETE FROM users WHERE username = ?")
.bind(username)
.run();
}
}
export class UserEntrypoint extends WorkerEntrypoint {
async getTasks(userId) {
return await this.env.D1.prepare(
"SELECT title FROM tasks WHERE user_id = ?"
)
.bind(userId)
.all();
}
async createTask(userId, title) {
await this.env.D1.prepare(
"INSERT INTO tasks (user_id, title) VALUES (?, ?)"
)
.bind(userId, title)
.run();
}
}
export default class extends WorkerEntrypoint {
async fetch(request, env) {
return new Response("Hello from my to do app");
}
}

You can then declare a Service binding directly to AdminEntrypoint in another Worker:

wrangler.toml
name = "admin-app"
[[services]]
binding = "ADMIN"
service = "todo-app"
entrypoint = "AdminEntrypoint"
export default {
async fetch(request, env) {
await env.ADMIN.createUser("aNewUser");
return new Response("Hello from admin app");
},
};

You can learn more about how to configure D1 in the D1 documentation.

You can try out a complete example of this to do app, as well as a Discord bot built with named entrypoints, by cloning the cloudflare/js-rpc-and-entrypoints-demo repository from GitHub.

​​ Further reading

  • Lifecycle: Memory management, resource management, and the lifecycle of RPC stubs.
  • Reserved Methods: Reserved methods with special behavior that are treated differently.
  • Visibility and Security Model: Which properties are and are not exposed to clients that communicate with your Worker or Durable Object via RPC
  • TypeScript: How TypeScript types for your Worker or Durable Object's RPC methods are generated and exposed to clients
  • Error handling: How exceptions, stack traces, and logging works with the Workers RPC system.