Lambda Invoke Modes: BUFFERED Versus RESPONSE_STREAM by Client Type

Lambda Invoke Modes: BUFFERED Versus RESPONSE_STREAM by Client Type
6 views
BUFFERED is still the right default for most APIs. RESPONSE_STREAM wins when time-to-first-byte matters (SSR, token UIs, large downloads) and the client can consume an HTTP stream. Pick per entrypoint — not globally — and measure memory vs TTFT under realistic clients.
⚡ TL;DR: Mobile/JSON CRUD → BUFFERED; SSR/HTML and LLM token UIs → RESPONSE_STREAM; batch/SQS → BUFFERED (or async). Cap streamed payloads; handle client abort. Pair with Lambda Plus Bedrock streaming and Lambda Warm Pools.

Lambda Invoke Modes: production guidance

Client Mode Why
Mobile app JSON < 1 MB BUFFERED Simple retries, full-response caches
Next.js SSR via Function URL RESPONSE_STREAM TTFT / TTFB UX
Bedrock token proxy RESPONSE_STREAM Tokens as produced
SQS / EventBridge worker BUFFERED (async invoke) No HTTP client
Admin CSV export 50 MB RESPONSE_STREAM + S3 prefer Avoid API GW 10 MB traps

Function URL streaming handler

typescript
// stream-handler.ts — Node 20 response streaming
import { pipeline } from "node:stream/promises";
import { Readable } from "node:stream";

export const handler = awslambda.streamifyResponse(
 async (event, responseStream, context) => {
 const http = awslambda.HttpResponseStream.from(responseStream, {
 statusCode: 200,
 headers: { "Content-Type": "text/html; charset=utf-8" },
 });
 try {
 const body = Readable.from(renderChunks(event));
 await pipeline(body, http);
 } catch (err) {
 // ✅ Client abort surfaces here — don't retry blindly
 console.error("stream_failed", err);
 http.end();
 }
 },
);

async function* renderChunks(event: unknown) {
 yield "<!doctype html><html><body>";
 for await (const part of fetchParts(event)) yield part;
 yield "</body></html>";
}

BUFFERED remains correct for CRUD

typescript
// ✅ Classic JSON API — buffered invoke via API Gateway
export async function handler(event: APIGatewayProxyEvent) {
 const order = await getOrder(event.pathParameters!.id!);
 return {
 statusCode: 200,
 headers: { "content-type": "application/json" },
 body: JSON.stringify(order),
 };
}
Streaming a 2 KB JSON body adds complexity (partial failures, harder middlewares) with no TTFT win. See Lambda Timeouts, Retries, and DLQs for buffered retry semantics.

Load-test both modes

bash
# illustrative — compare TTFT and RSS
k6 run --env MODE=buffered scripts/invoke-mixed.js
k6 run --env MODE=stream scripts/invoke-mixed.js
# watch: p50/p95 TTFT, Max Memory Used, 5XX on client cancel
Abort mid-stream must not leak provisioned concurrency. Alarm on Timeout spikes when clients disconnect early.

Closing checklist

✅ Dos – ✅ Choose mode per Function URL / API entrypoint – ✅ Stream only when TTFT or oversized body demands it – ✅ Handle client abort; bound stream duration – ✅ Prefer S3 pre-signed URLs for huge artifacts – ✅ Load-test mobile vs SSR clients separately ❌ Don’ts – ❌ Don’t enable RESPONSE_STREAM globally “for modernity” – ❌ Don’t stream through API Gateway without checking limits – ❌ Don’t ignore memory profiles under slow consumers – ❌ Don’t retry streamed side effects without idempotency

Related reading

Last updated on September 11, 2026 Related: Lambda Response Streaming: Keep AI Coding Gateways Under Client Timeouts; Lambda Function URLs with Streaming: Replace API Gateway for 90% of Use Cases; Lambda Response Streaming: Backpressure When Clients Stall Mid-Transfer.

Watch: 100 Days of AI on YouTube

Short videos from CheatCoders, one AI engineering topic per day.

Open the playlist on YouTube · CheatCoders on YouTube

Deep-dive PDF

Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.

Already subscribed? or open the subscribe page.


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a comment

No account needed. Name and email are optional.