---
title: "Batch + pipelining"
description: "Three dependent RPC calls in a single HTTP round trip, running live, with the source that produces it."
---

> Documentation Index
> Fetch the complete documentation index at: https://255.pr.capnweb.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Batch + pipelining

import { exampleBySlug } from '../../../examples.ts';

Authenticate a user, then fetch that user's profile and notifications, both of which need the user
ID that the first call returns. Pipelined, all three travel in **one** HTTP request. Written the
ordinary way, they take three.

Drag the latency slider and run it again. The server does identical work in both columns; the only
difference is how many times the browser has to cross the network.

:::note[There is no server behind this page.]
The Worker on the left is bundled into the page and answers its own requests, so the round-trip
counts are real but nothing crosses the network. The code and the protocol are identical either way.
See [how the playground works](#how-this-page-runs).
:::

## What makes it one round trip

The three calls are issued against a single session, and the second and third are built from a
promise that has not resolved yet:

```js
const api = newHttpBatchRpcSession(RPC_URL);

const user = api.authenticate('cookie-123');       // not awaited
const profile = api.getUserProfile(user.id);       // uses user.id anyway
const notifications = api.getNotifications(user.id);

const [u, p, n] = await Promise.all([user, profile, notifications]);
```

`user.id` is not a string here. It is a *reference* to a field of a result the server has not
produced yet. Cap'n Web sends that reference as part of the same batch, and the server substitutes
the real value when it gets there. Nothing has to come back to the client in between.

The sequential version awaits each call before building the next, so the client cannot know
`user.id` until a full round trip has completed:

```js
const user = await newHttpBatchRpcSession(RPC_URL).authenticate('cookie-123');
const profile = await newHttpBatchRpcSession(RPC_URL).getUserProfile(user.id);
const notifications = await newHttpBatchRpcSession(RPC_URL).getNotifications(user.id);
```

Same data, same server work, three times the network cost, and that cost grows with the length of
the chain, which is the part that hurts on a slow connection.

## Where the latency comes from

Two separate knobs, deliberately kept apart:

- **Server-side work**: per-method delays set by `DELAY_AUTH_MS`, `DELAY_PROFILE_MS` and
  `DELAY_NOTIFS_MS`. Identical in both modes, so it is *not* what the demo measures.
- **Network round trips**: simulated in the browser by the slider. This is the part pipelining
  removes.

Keeping the round-trip cost on the client means the deployed Worker adds no artificial delay, and
the page can change it without a redeploy.

## How this page runs

The demo above is the example's unmodified Worker and browser client, bundled together into one
page. A small shim replaces `fetch` for the `/rpc` path and hands the request straight to the
Worker's `fetch` handler:

```js
globalThis.fetch = async (input, init) => {
  const request = new Request(input, init);
  if (new URL(request.url).pathname === '/rpc') {
return await worker.fetch(request, ENV, ctx);
  }
  return upstream(input, init);
};
```

Everything above that line is untouched: the same session setup, the same batch encoding, the same
`newWorkersRpcResponse` on the other end. Only the transport hop is gone, which is why the round-trip
counts mean what they say and why these docs deploy as static files.

## Run it yourself

```sh
npm run build   # the examples resolve capnweb to dist/
npx wrangler dev --cwd examples/batch-pipelining --ip 127.0.0.1 --port 8788
```

The terminal client runs the same comparison against a server in a separate process:

```sh
node examples/batch-pipelining/server-node.mjs   # in one shell
node examples/batch-pipelining/client.mjs        # in another
```

## Next

- [RpcPromise & pipelining](/concepts/promises/): how the promise references work.
- [The pipelining tour](/start/pipelining-tour/): a step-by-step walkthrough, including `.map()`.
- [HTTP batch transport](/transports/http-batch/): what this example is running on.

Source: https://255.pr.capnweb.com/examples/batch-pipelining/index.mdx
