aws / aws/aws-cdk

(apigateway): L2 construct support for Routing Rules on custom domain names

Open
#36,917 2 comments 0 reactions 0 assignees View on GitHub
@aws-cdk/aws-apigateway effort/medium feature-request p2
Dominant language
TypeScript
Stars
12.9k
Forks
4.6k
Avg merge
2d 3h
Merged PRs (30d)
83

Description

### Describe the feature

The `DomainName` construct in `aws-apigateway` supports base path mappings and API mappings, but there's no L2 for **Routing Rules** (`AWS::ApiGatewayV2::RoutingRule`). To use routing rules today, you have to drop down to L1 constructs, manually build domain name ARNs, and give up synth-time validation entirely.

This proposal adds

1. A `RoutingRule` L2 construct in `aws-apigateway`
2. A `RoutingMode` enum and `routingMode` property on `DomainName`
3. An `addRoutingRule()` method on `DomainName`

### Use Case

A common pattern with routing rules is putting multiple REST APIs behind a single custom domain and splitting traffic by path or header.

- `/users/*` → Users Service API
- `/orders/*` → Orders Service API
- Header `x-api-version: v2` → New Orders Service API
- Everything else → Default API (catch-all)

### Current workaround (L1)

```ts
const cfnDomainName = new apigw.CfnDomainName(this, 'CustomDomain', {
domainName: 'api.example.com',
regionalCertificateArn: certificate.certificateArn,
endpointConfiguration: { types: ['REGIONAL'] },
routingMode: 'ROUTING_RULE_ONLY',
});

const domainNameArn = cdk.Arn.format(
{
service: 'apigateway',
resource: '/domainnames',
resourceName: 'api.example.com',
},
cdk.Stack.of(this),
);

new apigwv2.CfnRoutingRule(this, 'UsersRule', {
domainNameArn,
priority: 100,
conditions: [{ matchBasePaths: { anyOf: ['users'] } }],
actions: [
{
invokeApi: {
apiId: usersApi.restApiId,
stage: usersApi.deploymentStage.stageName,
stripBasePath: true,
},
},
],
});

// ... and repeat for every rule
```

The main pain points with this approach

- You have to know the ARN format for API Gateway domain names and build it yourself with `cdk.Arn.format()`
- You need to import `aws-apigatewayv2` even though routing rules are a REST API feature, which is just confusing
- The CloudFormation structure is deeply nested (`matchBasePaths.anyOf`, `invokeApi.apiId`, etc.) and easy to get wrong
- There's zero validation until deploy time, so typos in header names, out-of-range priorities, or routing mode mismatches all turn into slow CloudFormation rollbacks
- Nothing prevents you from mixing `addBasePathMapping()` with `ROUTING_RULE_ONLY` mode or vice versa

### Proposed Solution

### Proposed API

```ts
import { DomainName, RoutingMode } from 'aws-cdk-lib/aws-apigateway';

const domain = new DomainName(this, 'Domain', {
domainName: 'api.example.com',
certificate: certificate,
routingMode: RoutingMode.ROUTING_RULE_ONLY,
});

// Path-based routing
domain.addRoutingRule('UsersRule', {
priority: 100,
conditions: {
basePaths: ['users'],
},
action: {
restApi: usersApi,
stripBasePath: true,
},
});

// Header-based routing
domain.addRoutingRule('HeaderV2Rule', {
priority: 50,
conditions: {
headers: [{ header: 'x-api-version', valueGlob: 'v2' }],
},
action: {
restApi: ordersApiV2,
},
});

// Combined conditions (headers AND basePaths)
domain.addRoutingRule('V2OrdersRule', {
priority: 75,
conditions: {
basePaths: ['orders'],
headers: [{ header: 'x-api-version', valueGlob: 'v2' }],
},
action: {
restApi: ordersApiV2,
stripBasePath: true,
},
});

// Catch-all (no conditions)
domain.addRoutingRule('CatchAllRule', {
priority: 999999,
action: {
restApi: defaultApi,
},
});
```

### Design Decisions

**`addRoutingRule()` on `DomainName` with `RoutingMode` enforcement**

`addRoutingRule()` sits alongside the existing `addBasePathMapping()` and `addApiMapping()`. The construct checks that the method matches the configured routing mode — calling `addRoutingRule()` on a `BASE_PATH_MAPPING_ONLY` domain (the default) gives a clear error telling you to set the mode. Same in the other direction.

**Synth-time validation**

The construct validates all the constraints from the [Routing rules restrictions](https://docs.aws.amazon.com/apigateway/latest/developerguide/rest-api-routing-rules.html#rest-api-routing-rules-restrictions) docs at synth time, so you don't have to wait for a deploy to find out your header name is restricted or your priority is out of range. Unresolved tokens skip validation as usual.

**`RoutingRule` also works as a standalone construct**

For cross-stack scenarios, `RoutingRule` can be instantiated directly with a `domainName: IDomainName` prop, same pattern as `BasePathMapping`.

```ts
new RoutingRule(this, 'UsersRule', {
domainName: importedDomain,
priority: 100,
conditions: {
basePaths: ['users'],
},
action: {
restApi: usersApi,
stage: usersApi.deploymentStage,
stripBasePath: true,
},
});

```

### Other Information

- Under the hood this uses `AWS::ApiGatewayV2::RoutingRule`. It's a REST API feature but lives in the v2 namespace on the CloudFormation side. The existing `DomainName` L2 already uses `apigwv2.CfnApiMapping` internally, so this isn't a new pattern.
- Routing rules only work with REGIONAL endpoints.
- AWS docs
- [Routing rules overview](https://docs.aws.amazon.com/apigateway/latest/developerguide/rest-api-routing-rules.html)
- [How to use routing rules](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-routing-rules-use.html)
- [Set the routing mode](https://docs.aws.amazon.com/apigateway/latest/developerguide/set-routing-mode.html)

### 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

Open the contributing guide

Research direction

Start by reading the existing aws-apigateway DomainName construct and its addBasePathMapping(), addApiMapping(), and apigatewayv2 CfnApiMapping patterns. Trace how the proposed RoutingRule, RoutingMode, routing-mode enforcement, and synth-time validation should fit those APIs; done means the L2 construct, standalone form, validation, and documented routing behavior are covered.

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
Active
Clarity
Clearly specified
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.