Skip to content

Commit a5f5e2c

Browse files
authored
fix(aws-apigateway): make LambdaRestApi proxy by default (#963)
The LambdaRestApi construct now proxies all paths and methods by default, which can be switched off using `proxy: false`. BREAKING CHANGE: specifying a path no longer works. If you used to provide a '/', remove it. Otherwise, you will have to supply `proxy: false` and construct more complex resource paths yourself. Fixes #959.
1 parent d238ffd commit a5f5e2c

File tree

4 files changed

+54
-52
lines changed

4 files changed

+54
-52
lines changed

packages/@aws-cdk/aws-apigateway/README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,24 @@ book.addMethod('DELETE');
3434
A very common practice is to use Amazon API Gateway with AWS Lambda as the
3535
backend integration. The `LambdaRestApi` construct makes it easy:
3636

37-
The following code defines a REST API that uses a greedy `{proxy+}` resource
38-
mounted under `/api/v1` and integrates all methods (`"ANY"`) with the specified
39-
AWS Lambda function:
37+
The following code defines a REST API that routes all requests to the
38+
specified AWS Lambda function:
4039

4140
```ts
4241
const backend = new lambda.Function(...);
4342
new apigateway.LambdaRestApi(this, 'myapi', {
4443
handler: backend,
45-
proxyPath: '/api/v1'
4644
});
4745
```
4846

49-
If `proxyPath` is not defined, you will have to explicitly define the API model:
47+
You can also supply `proxy: false`, in which case you will have to explicitly
48+
define the API model:
5049

5150
```ts
5251
const backend = new lambda.Function(...);
5352
const api = new apigateway.LambdaRestApi(this, 'myapi', {
54-
handler: backend
53+
handler: backend,
54+
proxy: false
5555
});
5656

5757
const items = api.root.addResource('items');

packages/@aws-cdk/aws-apigateway/lib/lambda-api.ts

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import lambda = require('@aws-cdk/aws-lambda');
22
import cdk = require('@aws-cdk/cdk');
33
import { LambdaIntegration } from './integrations';
4+
import { Method } from './method';
5+
import { ProxyResource, Resource } from './resource';
46
import { RestApi, RestApiProps } from './restapi';
57

68
export interface LambdaRestApiProps {
@@ -13,15 +15,14 @@ export interface LambdaRestApiProps {
1315
handler: lambda.Function;
1416

1517
/**
16-
* An API path for a greedy proxy with an "ANY" method, which will route all
17-
* requests under that path to the defined handler.
18+
* If true, route all requests to the Lambda Function
1819
*
19-
* If not defined, you will need to explicitly define the API model using
20+
* If set to false, you will need to explicitly define the API model using
2021
* `addResource` and `addMethod` (or `addProxy`).
2122
*
22-
* @default undefined
23+
* @default true
2324
*/
24-
proxyPath?: string;
25+
proxy?: boolean;
2526

2627
/**
2728
* Further customization of the REST API.
@@ -49,16 +50,25 @@ export class LambdaRestApi extends RestApi {
4950
...props.options
5051
});
5152

52-
// if proxyPath is specified, add a proxy at the specified path
53-
// we will need to create all resources along the path.
54-
const proxyPath = props.proxyPath;
55-
if (proxyPath) {
56-
const route = proxyPath.split('/').filter(x => x);
57-
let curr = this.root;
58-
for (const part of route) {
59-
curr = curr.addResource(part);
60-
}
61-
curr.addProxy();
53+
if (props.proxy !== false) {
54+
this.root.addProxy();
55+
56+
// Make sure users cannot call any other resource adding function
57+
this.root.addResource = addResourceThrows;
58+
this.root.addMethod = addMethodThrows;
59+
this.root.addProxy = addProxyThrows;
6260
}
6361
}
62+
}
63+
64+
function addResourceThrows(): Resource {
65+
throw new Error(`Cannot call 'addResource' on a proxying LambdaRestApi; set 'proxy' to false`);
66+
}
67+
68+
function addMethodThrows(): Method {
69+
throw new Error(`Cannot call 'addMethod' on a proxying LambdaRestApi; set 'proxy' to false`);
70+
}
71+
72+
function addProxyThrows(): ProxyResource {
73+
throw new Error(`Cannot call 'addProxy' on a proxying LambdaRestApi; set 'proxy' to false`);
6474
}

packages/@aws-cdk/aws-apigateway/lib/resource.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ export class ProxyResource extends Resource {
171171
*/
172172
public readonly anyMethod?: Method;
173173

174+
private readonly parentResource: IRestApiResource;
175+
174176
constructor(parent: cdk.Construct, id: string, props: ProxyResourceProps) {
175177
super(parent, id, {
176178
parent: props.parent,
@@ -179,11 +181,22 @@ export class ProxyResource extends Resource {
179181
defaultMethodOptions: props.defaultMethodOptions,
180182
});
181183

184+
this.parentResource = props.parent;
185+
182186
const anyMethod = props.anyMethod !== undefined ? props.anyMethod : true;
183187
if (anyMethod) {
184188
this.anyMethod = this.addMethod('ANY');
185189
}
186190
}
191+
192+
public addMethod(httpMethod: string, integration?: Integration, options?: MethodOptions): Method {
193+
// In case this proxy is mounted under the root, also add this method to
194+
// the root so that empty paths are proxied as well.
195+
if (this.parentResource.resourcePath === '/') {
196+
this.parentResource.addMethod(httpMethod);
197+
}
198+
return super.addMethod(httpMethod, integration, options);
199+
}
187200
}
188201

189202
function validateResourcePathPart(part: string) {

packages/@aws-cdk/aws-apigateway/test/test.lambda-api.ts

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,14 @@ export = {
1818
});
1919

2020
// WHEN
21-
new apigw.LambdaRestApi(stack, 'lambda-rest-api', { handler, proxyPath: '/' });
21+
const api = new apigw.LambdaRestApi(stack, 'lambda-rest-api', { handler });
2222

23-
// THEN
23+
// THEN -- can't customize further
24+
test.throws(() => {
25+
api.root.addResource('cant-touch-this');
26+
});
27+
28+
// THEN -- template proxies everything
2429
expect(stack).to(haveResource('AWS::ApiGateway::Resource', {
2530
"PathPart": "{proxy+}"
2631
}));
@@ -81,33 +86,7 @@ export = {
8186
test.done();
8287
},
8388

84-
'proxyPath can be used to attach the proxy to any route'(test: Test) {
85-
// GIVEN
86-
const stack = new cdk.Stack();
87-
88-
const handler = new lambda.Function(stack, 'handler', {
89-
handler: 'index.handler',
90-
code: lambda.Code.inline('boom'),
91-
runtime: lambda.Runtime.NodeJS610,
92-
});
93-
94-
// WHEN
95-
new apigw.LambdaRestApi(stack, 'lambda-rest-api', {
96-
handler,
97-
proxyPath: '/backend/v2'
98-
});
99-
100-
// THEN
101-
expect(stack).to(haveResource('AWS::ApiGateway::Method', {
102-
"ResourceId": {
103-
"Ref": "lambdarestapibackendv2proxyC4980BD5"
104-
}
105-
}));
106-
107-
test.done();
108-
},
109-
110-
'when "proxyPath" is not specified, users need to define the model'(test: Test) {
89+
'when "proxy" is set to false, users need to define the model'(test: Test) {
11190
// GIVEN
11291
const stack = new cdk.Stack();
11392

@@ -118,7 +97,7 @@ export = {
11897
});
11998

12099
// WHEN
121-
const api = new apigw.LambdaRestApi(stack, 'lambda-rest-api', { handler });
100+
const api = new apigw.LambdaRestApi(stack, 'lambda-rest-api', { handler, proxy: false });
122101

123102
const tasks = api.root.addResource('tasks');
124103
tasks.addMethod('GET');
@@ -162,5 +141,5 @@ export = {
162141
}), /Cannot specify \"options\.defaultIntegration\" since Lambda integration is automatically defined/);
163142

164143
test.done();
165-
}
144+
},
166145
};

0 commit comments

Comments
 (0)