@contextualize/lambda-router 0.0.18 → 0.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/handler.d.ts +23 -2
- package/dist/handler.js +45 -19
- package/dist/router.js +3 -2
- package/package.json +1 -1
- package/readme.md +112 -3
package/dist/handler.d.ts
CHANGED
|
@@ -11,9 +11,30 @@ export default class Handler {
|
|
|
11
11
|
private canDoBackground;
|
|
12
12
|
private backgroundProcesses;
|
|
13
13
|
private static executeRouter;
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Register a background process that can past a resolution being returned to the lambda handler
|
|
16
|
+
*/
|
|
15
17
|
background(process: BackgroundProcess | (() => BackgroundProcess)): void;
|
|
18
|
+
/**
|
|
19
|
+
* Wait for the promise to be resolved before killing the lambda.
|
|
20
|
+
*
|
|
21
|
+
* Can be run in standard handler
|
|
22
|
+
*/
|
|
23
|
+
await(process: BackgroundProcess | (() => BackgroundProcess)): void;
|
|
16
24
|
waitForBackgroundProcesses(): Promise<void>;
|
|
17
|
-
|
|
25
|
+
/**
|
|
26
|
+
* A standard handler. No response will be returned until all processes have completed
|
|
27
|
+
*/
|
|
28
|
+
static handler(setup: HandlerSetup, cleanup?: HandlerCleanup): (event: APIGatewayEvent) => Promise<APIGatewayResponse<any>>;
|
|
29
|
+
/**
|
|
30
|
+
* An advanced handler that allows promises to keep running in the background.
|
|
31
|
+
* Allows for quick responses when triggering long running tasks.
|
|
32
|
+
*/
|
|
18
33
|
static backgroundHandler(setup: HandlerSetup, cleanup?: HandlerCleanup): import("aws-lambda").Handler<any, any>;
|
|
34
|
+
/**
|
|
35
|
+
* Starts a node native HTTP/S server
|
|
36
|
+
*
|
|
37
|
+
* **Alternatively:** Start the lambda using `lambda-router serve`
|
|
38
|
+
*/
|
|
39
|
+
static serverHandler(setup: HandlerSetup, cleanup?: HandlerCleanup, props?: Parameters<typeof Server.serve>[1]): Promise<import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse> | import("https").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>>;
|
|
19
40
|
}
|
package/dist/handler.js
CHANGED
|
@@ -18,25 +18,24 @@ class Handler {
|
|
|
18
18
|
result = new response_1.default();
|
|
19
19
|
result.status(500).json({ message: "An unhandled error was encountered" });
|
|
20
20
|
}
|
|
21
|
-
handler.isInHandler = false;
|
|
22
21
|
return result;
|
|
23
22
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return async (event) => {
|
|
28
|
-
const result = await this.executeRouter(event, handler, setup);
|
|
29
|
-
if (cleanup) {
|
|
30
|
-
await cleanup(event);
|
|
31
|
-
}
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
}
|
|
23
|
+
/**
|
|
24
|
+
* Register a background process that can past a resolution being returned to the lambda handler
|
|
25
|
+
*/
|
|
35
26
|
background(process) {
|
|
36
27
|
console.log("Registering background process");
|
|
37
28
|
if (!this.canDoBackground) {
|
|
38
29
|
throw new Error("Lambda handler not configured to use background processes");
|
|
39
30
|
}
|
|
31
|
+
this.await(process);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Wait for the promise to be resolved before killing the lambda.
|
|
35
|
+
*
|
|
36
|
+
* Can be run in standard handler
|
|
37
|
+
*/
|
|
38
|
+
await(process) {
|
|
40
39
|
if (!this.isInHandler) {
|
|
41
40
|
throw new Error("Cannot push a new background process outside of router execution");
|
|
42
41
|
}
|
|
@@ -46,6 +45,7 @@ class Handler {
|
|
|
46
45
|
this.backgroundProcesses.push(process);
|
|
47
46
|
}
|
|
48
47
|
async waitForBackgroundProcesses() {
|
|
48
|
+
this.isInHandler = false;
|
|
49
49
|
this.canDoBackground = false;
|
|
50
50
|
if (this.backgroundProcesses.length > 0) {
|
|
51
51
|
console.log("Awaiting background processes");
|
|
@@ -58,21 +58,29 @@ class Handler {
|
|
|
58
58
|
});
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
-
|
|
61
|
+
/**
|
|
62
|
+
* A standard handler. No response will be returned until all processes have completed
|
|
63
|
+
*/
|
|
64
|
+
static handler(setup, cleanup) {
|
|
62
65
|
const handler = new Handler();
|
|
63
|
-
handler.canDoBackground =
|
|
64
|
-
return
|
|
65
|
-
|
|
66
|
-
response(result);
|
|
66
|
+
handler.canDoBackground = false;
|
|
67
|
+
return async (event) => {
|
|
68
|
+
const result = await this.executeRouter(event, handler, setup);
|
|
67
69
|
await handler.waitForBackgroundProcesses();
|
|
68
70
|
if (cleanup) {
|
|
69
71
|
await cleanup(event);
|
|
70
72
|
}
|
|
71
|
-
|
|
73
|
+
return result;
|
|
74
|
+
};
|
|
72
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* An advanced handler that allows promises to keep running in the background.
|
|
78
|
+
* Allows for quick responses when triggering long running tasks.
|
|
79
|
+
*/
|
|
73
80
|
static backgroundHandler(setup, cleanup) {
|
|
74
|
-
return awslambda.streamifyResponse(async (event, responseStream,
|
|
81
|
+
return awslambda.streamifyResponse(async (event, responseStream, context) => {
|
|
75
82
|
const handler = new Handler();
|
|
83
|
+
context.callbackWaitsForEmptyEventLoop = false;
|
|
76
84
|
handler.canDoBackground = true;
|
|
77
85
|
let result = await this.executeRouter(event, handler, setup);
|
|
78
86
|
responseStream.write(JSON.stringify(result));
|
|
@@ -83,5 +91,23 @@ class Handler {
|
|
|
83
91
|
}
|
|
84
92
|
});
|
|
85
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Starts a node native HTTP/S server
|
|
96
|
+
*
|
|
97
|
+
* **Alternatively:** Start the lambda using `lambda-router serve`
|
|
98
|
+
*/
|
|
99
|
+
static serverHandler(setup, cleanup, props) {
|
|
100
|
+
return server_1.default.serve((async (event, response) => {
|
|
101
|
+
const handler = new Handler();
|
|
102
|
+
handler.canDoBackground = true;
|
|
103
|
+
handler.isInHandler = true;
|
|
104
|
+
let result = await this.executeRouter(event, handler, setup);
|
|
105
|
+
response(result);
|
|
106
|
+
await handler.waitForBackgroundProcesses();
|
|
107
|
+
if (cleanup) {
|
|
108
|
+
await cleanup(event);
|
|
109
|
+
}
|
|
110
|
+
}), props);
|
|
111
|
+
}
|
|
86
112
|
}
|
|
87
113
|
exports.default = Handler;
|
package/dist/router.js
CHANGED
|
@@ -66,8 +66,9 @@ class Router {
|
|
|
66
66
|
response.json({ message: exception.message });
|
|
67
67
|
}
|
|
68
68
|
else {
|
|
69
|
-
console.error(`Request experienced a critical error
|
|
70
|
-
|
|
69
|
+
console.error(`Request experienced a critical error: `, exception);
|
|
70
|
+
response.statusCode = 500;
|
|
71
|
+
response.json({ message: "Internal Server Error" });
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
return response;
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Allows for the creation of Express-like APIs within AWS Lambda.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
5
|
+
## Routers
|
|
6
|
+
Routers allow you to construct API paths for your application
|
|
7
|
+
### Basic Usage
|
|
7
8
|
```js
|
|
8
9
|
import { Router, Handler } from '@contextualize/lambda-router';
|
|
9
10
|
|
|
@@ -19,6 +20,113 @@ export const lambdaHandler = Handler.handler(()=>router);
|
|
|
19
20
|
|
|
20
21
|
```
|
|
21
22
|
|
|
23
|
+
### Errors
|
|
24
|
+
Lambda router has a number of built-in exceptions for common HTTP error codes. Rather than explicity sending an error response and returning from your endpoint code, you can just throw one of these exceptions.
|
|
25
|
+
|
|
26
|
+
If your application throws an error that does not extend from `@contextualize/lambda-router/PublicError`, a HTTP 500 status is returned by default with 'Internal Server Error'
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
router.post('/', (req, res)=>{
|
|
30
|
+
if(!("required" in req.body)){
|
|
31
|
+
// Ends execution
|
|
32
|
+
// Returns a HTTP 400 error to the user
|
|
33
|
+
// Error message is supplied to user in the response body as json
|
|
34
|
+
throw new BadRequestError("'required' must be in request body");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return res.status(200).json({
|
|
38
|
+
status: "ok"
|
|
39
|
+
});
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Path Parameters
|
|
45
|
+
Parameters can be passed in through the path using `{}`. Lambda Router will always prioritize named paths before attempting to use a parameterized path.
|
|
46
|
+
```js
|
|
47
|
+
router.get('/{myParam}', (req, res)=>{
|
|
48
|
+
return res.status(200).json({
|
|
49
|
+
status: "ok"
|
|
50
|
+
myParam: req.params['myParam']
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
router.get('/list', (req, res)=>{
|
|
56
|
+
return res.status(200).json({
|
|
57
|
+
status: "ok"
|
|
58
|
+
list: ['stuff', 'and', 'things']
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Middleware
|
|
64
|
+
Middleware allow you to run a block of code before all subsequent paths.
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
// Not protected by token validation
|
|
69
|
+
router.get('/', (req,res)=>{
|
|
70
|
+
return res.status(200).json({
|
|
71
|
+
message: "Welcome!"
|
|
72
|
+
});
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
// Check user token
|
|
76
|
+
router.use((req)=>{
|
|
77
|
+
const token = req.headers['Authorization'];
|
|
78
|
+
|
|
79
|
+
// Do some token validation
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// Protected by token validation
|
|
83
|
+
router.get('/protected', (req, res)=>{
|
|
84
|
+
return res.status(200).json({
|
|
85
|
+
message: "You are authenticated"
|
|
86
|
+
});
|
|
87
|
+
})
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Subrouting
|
|
91
|
+
Subrouters allow you to organize like routes as well as define known states of the request a certain paths.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
const router = new Router();
|
|
95
|
+
|
|
96
|
+
router.get('/', (req, res)=>{
|
|
97
|
+
|
|
98
|
+
res.status(200).json({
|
|
99
|
+
message: 'Home'
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
type UserApp = {data: UserProfile}
|
|
104
|
+
const userRouter = new Router<UserApp>();
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
dataRouter.use(async (req)=>{
|
|
108
|
+
// Load user's profile from the database
|
|
109
|
+
req.app = {data: await User.load(req.params['username'])};
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
userRouter.get('/', (req, res)=>{
|
|
113
|
+
// Return user's data
|
|
114
|
+
res.status(200).json({
|
|
115
|
+
...req.app.data
|
|
116
|
+
})
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
userRouter.get('/groups', (req, res)=>{
|
|
120
|
+
// Return user's groups
|
|
121
|
+
res.status(200).json({
|
|
122
|
+
groups: req.app.data.groups
|
|
123
|
+
})
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
router.subroute('/users/{username}', userRouter);
|
|
128
|
+
```
|
|
129
|
+
|
|
22
130
|
## Background Handler
|
|
23
131
|
Some lambdas may require tasks to run in the background after a request has been handled. `Handler.backgroundHandler` allows you to defer promises for after the lambda has returned a response to the client.
|
|
24
132
|
|
|
@@ -30,7 +138,8 @@ const router = new Router();
|
|
|
30
138
|
router.get('/', (req, res)=>{
|
|
31
139
|
req.handler.background(async ()=>{
|
|
32
140
|
|
|
33
|
-
// Assume this a request that is going to take a long time
|
|
141
|
+
// Assume this is a request that is going to take a long time
|
|
142
|
+
// i.e. uploading a file, running a put query
|
|
34
143
|
await fetch('https://example.com')
|
|
35
144
|
})
|
|
36
145
|
|