(cloudfront-origins): add HttpApiOrigin to support API Gateway HTTP API as CloudFront origin
- Dominant language
- TypeScript
- Stars
- 12.9k
- Forks
- 4.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 83
Description
### Describe the feature
The `aws-cloudfront-origins` module currently provides `RestApiOrigin` for API Gateway REST API (v1), but there is no equivalent L2 construct for API Gateway HTTP API (v2). Users who want to use an HTTP API as a CloudFront origin must manually construct an `HttpOrigin`, extracting the domain name and origin path from the HTTP API's URL themselves.
This proposal adds an `HttpApiOrigin` class that accepts an `IHttpApi` and provides the same level of abstraction that RestApiOrigin provides for REST APIs.
**Why is this missing today?**
HTTP API has several characteristics that make it more complex than REST API in this context.
1. URL can be undefined
When `createDefaultStage: false` is set, `httpApi.url` is undefined, whereas restApi.url is always a string (when `deploy: true`, the default).
2. Multiple stages
HTTP API supports multiple stages per API. REST API's `RestApiOrigin` implicitly uses the deploymentStage managed by the RestApi construct, but HTTP API can have multiple stages and there is no single obvious default to use.
3. Default stage URL format
The default stage URL (https://xxx.execute-api.region.amazonaws.com/) has no stage name segment, while custom stages include one (e.g., /prod/), requiring different originPath handling.
**Proposed API**
```
import { HttpApiOrigin } from 'aws-cdk-lib/aws-cloudfront-origins';
import { HttpApi } from 'aws-cdk-lib/aws-apigatewayv2';
// Simple case: uses defaultStage automatically
const httpApi = new HttpApi(this, 'Api');
new cloudfront.Distribution(this, 'Dist', {
defaultBehavior: {
origin: new HttpApiOrigin(httpApi),
},
});
// Custom stage: explicitly specify via props
const prodStage = httpApi.addStage('prod', { autoDeploy: true });
new cloudfront.Distribution(this, 'ProdDist', {
defaultBehavior: {
origin: new HttpApiOrigin(httpApi, { stage: prodStage }),
},
});
```
### Use Case
When building a modern web application with CloudFront as the CDN and API Gateway HTTP API as the backend, users naturally expect a symmetric experience between REST API and HTTP API.
Currently, while REST API users can write
```
new cloudfront.Distribution(this, 'Dist', {
defaultBehavior: {
origin: new origins.RestApiOrigin(restApi),
},
});
```
HTTP API users must resort to a lower-level workaround
```
const apiUrl = httpApi.url!;
const domainName = Fn.select(2, Fn.split('/', apiUrl));
const stageName = Fn.select(3, Fn.split('/', apiUrl));
new cloudfront.Distribution(this, 'Dist', {
defaultBehavior: {
origin: new origins.HttpOrigin(domainName, {
originPath: `/${stageName}`,
}),
},
});
```
This workaround requires users to
* Understand the internal URL structure of API Gateway HTTP API
* Manually parse the URL using Fn.split / Fn.select
* Handle the difference between default stage and custom stage URLs
* Use non-null assertion (!) on httpApi.url
This creates unnecessary complexity and inconvenience for users in most common scenarios.
### Proposed Solution
I propose adding an `HttpApiOrigin` class to `aws-cdk-lib/aws-cloudfront-origins` that mirrors the design of `RestApiOrigin` while accounting for HTTP API's multi-stage nature.
**Design Decisions**
**Accept `IHttpApi` (not `IHttpStage`) as the primary argument**
This maintains symmetry with `RestApiOrigin`, which accepts `IRestApi`. The API object is the natural entry point. An optional stage prop allows overriding the default stage when needed, keeping the simple case simple while supporting advanced use cases.
**Runtime validation for missing default stage**
When `createDefaultStage: false` is used and no explicit stage is provided, the construct throws a clear, actionable error at synth time. This is a deliberate trade-off — the alternative (requiring `IHttpStage` as the primary argument) would make the common case more verbose and break symmetry with `RestApiOrigin`.
**Automatic originPath derivation**
The construct automatically extracts the stage name from the stage URL and sets it as originPath, matching RestApiOrigin's behavior. For custom stages (URL: https://xxx.execute-api.region.amazonaws.com/prod/), it becomes originPath: "/prod".
For the $default stage (URL: https://xxx.execute-api.region.amazonaws.com/), the stage name segment is empty. Setting originPath: "/" could result in double slashes in the request path. The implementation will need to handle this case — either by not setting originPath for $default stages, or by using CloudFormation conditions. The exact approach will be determined during implementation, and this is an area where reviewer feedback is welcome.
**Why not HttpStageOrigin?**
An alternative design where `IHttpStage` is the primary argument (`HttpStageOrigin`) was considered. While it avoids the undefined URL issue entirely, it sacrifices the API-level symmetry with `RestApiOrigin` and makes the most common use case (default stage) more verbose. The proposed design wraps this complexity internally while keeping the public API clean.
### Other Information
_No response_
### Acknowledgements
- [x] I may be able to implement this feature request
- [ ] This feature might incur a breaking change
### AWS CDK Library version (aws-cdk-lib)
2.x
### AWS CDK CLI version
2.x
### Environment details (OS name and version, etc.)
all
Contributor guide
Research direction
Start by reading the existing RestApiOrigin in aws-cloudfront-origins and the IHttpApi and IHttpStage entry points in aws-apigatewayv2. Determine how default and custom stage URLs expose the domain and stage path, then define the synth-time validation and originPath behavior. Done means HttpApiOrigin supports the shown default and custom-stage examples without manual URL parsing.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, typescript
- Domain
- api, cloud
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100