# Batching

Real programs spend a lot of time talking to slow, remote things: databases, HTTP APIs,
caches. When a single logical operation fans out into dozens of individual lookups — one
query per user, one fetch per id — the naive approach issues one round-trip per call. The
network round-trips, not the CPU, become the bottleneck.

Effect's batching system lets you describe *what* you need (a **Request**) separately from
*how* it is fetched (a **RequestResolver**). When many requests are made — even across
concurrent fibers — Effect collects them into a single batch, hands them to the resolver
together, and automatically deduplicates identical requests. Your business logic keeps
asking for one thing at a time; the runtime turns that into one efficient external call.

```ts
import { Effect } from "effect"

// `getUserById` looks like a simple per-id lookup...
declare const getUserById: (id: number) => Effect.Effect<string>

// ...but running many of them concurrently produces a *single* batched
// call to the underlying resolver, with duplicate ids (1 and 2) collapsed.
const program = Effect.forEach([1, 2, 1, 3, 2], getUserById, {
  concurrency: "unbounded"
})
```

## How it works

- A **[Request](https://effect.plants.sh/batching/request-resolver/)** is a typed description of one piece of data
  you want — its inputs, its success type, and its error type. Requests are plain values,
  so two identical requests are *equal* and can be deduplicated.
- A **RequestResolver** receives a non-empty batch of pending requests and completes each
  one. This is the only place that touches the external system, so it is where you write a
  bulk query (`WHERE id IN (...)`) or a batched API call.
- `Effect.request(req, resolver)` ties them together. The runtime gathers all requests made
  within a short delay window (across concurrent fibers), passes the unique set to the
  resolver, and routes each result back to the fiber that asked for it.

## In this section

- **[Request & RequestResolver](https://effect.plants.sh/batching/request-resolver/)** — define request types, write
  a batching resolver, deduplicate, add a delay window, tracing, and a cache.

## Related

- [Caching](https://effect.plants.sh/caching/) memoizes effects by input; batching collapses *concurrent* calls into
  one round-trip. The two compose — `RequestResolver.withCache` adds caching on top of a
  resolver.
- [Concurrency](https://effect.plants.sh/concurrency/) — batching shines when requests are issued from many fibers
  with `Effect.forEach(..., { concurrency })`.
- [Observability](https://effect.plants.sh/observability/) — resolvers can wrap each batch in a span so you can see
  exactly how many requests collapsed into one call.