What is the Asynchronous Request-Reply pattern?
The server accepts the request, starts the work in the background and immediately returns 202 Accepted with a URL where the status can be checked. The client polls that URL, or is notified through a webhook or WebSocket, and fetches the result when it is ready.
What problem does the Asynchronous Request-Reply pattern solve?
HTTP connections, load balancers and gateways have timeouts, usually tens of seconds. Work that takes longer than that fails even when it would have succeeded. Holding a connection open also ties up resources on every hop in between.
How do you implement the Asynchronous Request-Reply pattern?
- Return 202 Accepted with a job ID and a status URL as soon as the request is validated.
- Hand the work to a worker through a queue.
- Have the status endpoint return pending, done (with a link to the result) or failed (with a reason).
- Tell clients how often to poll with Retry-After, or offer a callback.
- Make job submission idempotent and expire old jobs and results.
When should you use it?
- Work regularly takes longer than a few seconds, such as exports, reports and media processing.
- The client can show progress or continue with other things.
When should you avoid it?
- The work is fast. Polling adds latency and complexity for nothing.
- The caller cannot continue without the result and has no way to wait.
What do you gain?
- No timeouts on long work
- Workers scale separately from the API
- Clients can show progress
What does it cost?
- More endpoints and job state to manage
- Polling adds load and latency
- Results need storage and cleanup
What are common mistakes with the Asynchronous Request-Reply pattern?
- Clients polling in a tight loop with no Retry-After guidance.
- Jobs that fail silently and stay 'pending' forever.
- Duplicate jobs when the client retries the original request.
Asynchronous Request-Reply in practice
Canvasly
Exporting a large design took up to a minute, longer than the load balancer's timeout. Users saw gateway errors even when the export eventually finished.
The export endpoint now returns 202 Accepted with a status URL, and a worker does the job. The client polls the status URL and downloads the file when it is ready.
Example decision record
- Context
- Generating large reports takes longer than the load balancer's request timeout.
- Decision
- Accept report requests with 202 and a status URL, and generate reports in a worker.
- Consequences
- No request is held open for the length of the job.
- Clients must poll or subscribe for completion, and jobs need expiry and cleanup.