Skip to content

feat: add aws-apigw-streaming wrapper for API Gateway response streaming#1181

Merged
conico974 merged 3 commits into
opennextjs:mainfrom
osama-rizk:feat/apigw-streaming-wrapper
Jun 19, 2026
Merged

feat: add aws-apigw-streaming wrapper for API Gateway response streaming#1181
conico974 merged 3 commits into
opennextjs:mainfrom
osama-rizk:feat/apigw-streaming-wrapper

Conversation

@osama-rizk

@osama-rizk osama-rizk commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Motivation

OpenNext's only streaming wrapper, aws-lambda-streaming, targets the Lambda Function URL streaming wire format. There was no streaming wrapper for API Gateway, even though:

  1. API Gateway REST APIs now support response streaming (announced Nov 2025): https://aws.amazon.com/about-aws/whats-new/2025/11/api-gateway-response-streaming-rest-apis/ — via ResponseTransferMode: STREAM on a Lambda proxy integration (InvokeWithResponseStreaming).

  2. Lambda Function URLs are unusable behind CloudFront + OAC for POST/PUT. OAC signs the request with SigV4 (including a body hash); the Function URL re-hashes the received body and the two diverge, returning 403 InvalidSignatureException on every non-empty body: https://repost.aws/questions/QUbHCI9AfyRdaUPCCo_3XKMQ/lambda-function-url-behind-cloudfront-invalidsignatureexception-only-on-post . API Gateway REST uses lambda:InvokeFunction (no body re-hash), so POST/PUT bodies — including binary uploads — pass through intact.

So the natural transport for a CloudFront-fronted SSR Lambda that must accept POST bodies and stream responses is API Gateway REST in STREAM mode. But the existing streaming wrapper can't be used there: it emits a Function-URL-only content-type marker and compresses the body, both of which break API Gateway streaming.

This is also what #1090 ("Clarify streaming support with apigateway-v2") is about — the wrapper/converter compatibility matrix implies streaming pairs with API Gateway, but the only streaming wrapper actually 500s behind a gateway. This PR adds the missing piece and addresses #1090.

Related: #1127 — origin-level compression in aws-lambda-streaming causes periodic 502s behind a Function URL, and the proposed fix is to fall back to identity. This new wrapper sidesteps that class of issue entirely: it never compresses and always emits identity, consistent with AWS's note that API Gateway streaming does not support content encoding (CloudFront compresses downstream).

What this PR changes

A new built-in wrapper, aws-apigw-streaming — 4 files, +118 lines. It keeps the framing that API Gateway STREAM shares with Function URLs (a JSON metadata prelude + 8-null-byte delimiter, then the body) but omits the two things that break API Gateway:

  1. No application/vnd.awslambda.http-integration-response content type. That marker is Function-URL-specific metadata for the Lambda runtime; API Gateway forwards it onto the wire and corrupts HTTP framing.
  2. No content encoding. API Gateway streaming does not support content encoding (per the AWS announcement above), so the body is emitted as identity. Compression, if desired, is handled by the CDN in front (e.g. CloudFront).
File Change
src/overrides/wrappers/aws-apigw-streaming.ts New wrapper. streamifyResponse, omits the FURL content-type marker, forces identity encoding. Works with either API Gateway payload format via its paired converter.
src/types/open-next.ts Add "aws-apigw-streaming" to the IncludedWrapper union.
src/build/validateConfig.ts Compatibility matrix: "aws-apigw-streaming": ["aws-apigw-v1", "aws-apigw-v2"].
src/build/generateOutput.ts canStream() recognizes the new wrapper so open-next.output.json reports streaming: true.

It does not alter aws-lambda-streaming or any existing behavior — it's purely additive, following the existing precedent of transport-specific wrapper variants (cloudflare-edge, cloudflare-node, aws-lambda-compressed).

Usage:

// open-next.config.ts
export default {
  default: {
    override: {
      wrapper: "aws-apigw-streaming",
      converter: "aws-apigw-v1", // REST API payload format (v1); use aws-apigw-v2 for HTTP API
    },
  },
};

Verification

Deployed a Next.js app built with this wrapper behind API Gateway REST in ResponseTransferMode: STREAM using hand-written AWS CDK (no SST, no framework), to prove the wrapper stands on its own.

Live demo: https://d376d74ff6kodo.cloudfront.net/streaming-demo

Source (sample app + CDK stack, public): https://github.com/osama-rizk/opennext-apigw-streaming-demo

Verified end to end on the live deployment:

Check Result
GET /streaming 10 chunks delivered progressively over ~2.7 s (not buffered)
Streaming RSC (<Suspense>) shell paints first; sections stream in at 1 s / 2 s / 3 s (9 network flushes)
POST with a 1 MB binary body round-trips byte-for-byte (sha256 match) — the case that 403s on a Function URL behind OAC
SSG / ISR (time + on-demand) / RSC / server actions / API routes all pass
Bundle inspection zero http-integration-response markers; open-next.output.json reports streaming: true

OpenNext's only streaming wrapper (aws-lambda-streaming) targets the Lambda
Function URL wire format: it sets the
application/vnd.awslambda.http-integration-response content type and gzips/
brotlis the body. Neither is valid for an API Gateway proxy integration in
ResponseTransferMode: STREAM — API Gateway forwards the content-type marker
onto the wire (breaking framing) and does not support content encoding.

Add a dedicated aws-apigw-streaming wrapper that emits the same JSON-prelude +
8-null-byte framing (which API Gateway STREAM shares with Function URLs) but
omits the FURL content-type marker and forces identity encoding. Pair it with
aws-apigw-v1 and aws-apigw-v2 in the compatibility matrix.

Refs opennextjs#1090
canStream() hard-coded the aws-lambda-streaming name, so a function using the
new aws-apigw-streaming wrapper was written to open-next.output.json with
streaming:false. Recognize both built-in streaming wrappers.
@changeset-bot

changeset-bot Bot commented Jun 6, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: efc4e9d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@opennextjs/aws Minor
app-pages-router Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@osama-rizk osama-rizk marked this pull request as ready for review June 6, 2026 11:59
@pkg-pr-new

pkg-pr-new Bot commented Jun 16, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@opennextjs/aws@1181

commit: efc4e9d

@conico974 conico974 merged commit f4faa3b into opennextjs:main Jun 19, 2026
3 checks passed
@github-actions github-actions Bot mentioned this pull request Jun 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants