Skip to content

Commit ae1c4c6

Browse files
committed
update error response to be status code 500 instead of 503
1 parent 61590b8 commit ae1c4c6

File tree

8 files changed

+291
-5
lines changed

8 files changed

+291
-5
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html)
66

7+
## [3.0.0]  (2020-01-05)
8+
9+
### Changed
10+
11+
- Api error response generates a 500 status code instead of 503
12+
713
## [2.1.1]  (2020-01-05)
814

915
### Added
@@ -143,6 +149,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/),
143149
- Update older libraries
144150
- Now publish from Git tags instead of master pushes
145151

152+
[3.0.0]: https://github.com/manwaring/lambda-wrapper/compare/v2.1.1...v3.0.0
146153
[2.1.1]: https://github.com/manwaring/lambda-wrapper/compare/v2.1.0...v2.1.1
147154
[2.1.0]: https://github.com/manwaring/lambda-wrapper/compare/v2.0.1...v2.1.0
148155
[2.0.1]: https://github.com/manwaring/lambda-wrapper/compare/v2.0.0...v2.0.1

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
# AWS Lambda wrapper library
1111

12-
### This documentation is for v2 of the library - [go here for v1 documentation](old-docs/v1/README.md)
12+
### This documentation is for v3 of the library - [go here for v1](old-docs/v1/README.md) and [here for v2](old-docs/v2/README.md)
1313

1414
1. [Overview](#overview)
1515
1. [Installation and setup](#installation-and-setup)

old-docs/v1/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
# AWS Lambda wrapper library
77

8+
### This documentation is for v1 of the library
9+
810
1. [Overview](#overview)
911
1. [Installation and setup](#installation-and-setup)
1012
- [Optional configuration](#optional-configuration)

old-docs/v2/README.md

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
<p align="center">
2+
<img height="150" src="https://avatars0.githubusercontent.com/u/36457275?s=400&u=16d355f384ed7f8e0655b7ed1d70ff2e411690d8&v=4e">
3+
<img height="150" src="https://user-images.githubusercontent.com/2955468/44874383-0168f780-ac69-11e8-8e51-774678cbd966.png">
4+
</p>
5+
6+
# AWS Lambda wrapper library
7+
8+
### This documentation is for v2 of the library
9+
10+
1. [Overview](#overview)
11+
1. [Installation and setup](#installation-and-setup)
12+
- [Optional configuration](#optional-configuration)
13+
1. [Supported events](#supported-events)
14+
- [API Gateway](#api-gateway)
15+
- [CloudFormation Custom Resource](#cloudformation-custom-resource)
16+
- [DynamoDB Stream](#dynamodb-stream)
17+
- [Lambda Authorizer](#lambda-authorizer)
18+
- [SNS](#sns)
19+
- [Generic event](#generic-event)
20+
1. [Example projects](#example-projects)
21+
22+
_Feedback appreciated! If you have an idea for how this library can be improved [please open an issue](https://github.com/manwaring/lambda-wrapper/issues/new)._
23+
24+
# Overview
25+
26+
### TL;DR
27+
28+
This library provides custom Lambda function wrappers which expose standard, abstracted functionality so that developers can focus on writing business logic instead of parsing event payloads and crafting response objects.
29+
30+
### Rationale and motivation
31+
32+
AWS Lambda supports a wide variety of event triggers, each with unique payloads and expected responses. The Lambda execution environment, however, only provides the raw events and has no included mechanisms for simplifying response object creation. For example, API Gateway events include only the raw request body, leaving it up to developers to implement parsing themselves. Similarly, the developer is responsible for creating a response object which includes the correct HTTP status code and headers. Given the standard nature of these kinds of concerns, this library exposes helpful abstractions like parsed HTTP bodies based on content-type headers, and success response functions which create response objects with the correct status codes and headers for returning.
33+
34+
# Installation and setup
35+
36+
Install and save the package to `package.json` as a dependency:
37+
38+
`npm i --save @manwaring/lambda-wrapper`
39+
40+
`yarn add @manwaring/lambda-wrapper`
41+
42+
## Optional configuration
43+
44+
If you want the wrapper to log request and response messages (helpful for debugging set an environemnt variable for `LAMBDA_WRAPPER_LOG=true`.
45+
46+
If you want each invocation to be tagged with the AWS region, environment/, and Git revision simply set environment variables for each: `REGION=us-east-1`, `STAGE=prod`, `REVISION=f4ba682` (see [git-rev-sync](https://www.npmjs.com/package/git-rev-sync) and [serverless-plugin-git-variables](https://www.npmjs.com/package/serverless-plugin-git-variables) for libraries that can help you set git revision)
47+
48+
# Supported events
49+
50+
All of the events bellow have a corresponding wrapper which provides a deconstructed method signature exposing parsed/unmarshalled request parameters and helper response methods.
51+
52+
1. [API Gateway](#api-gateway) with support for cors headers and 200, 302, 400, and 500 responses
53+
1. [CloudFormation Custom Resource](#cloudformation-custom-resource) with support for CloudFormation successes and failures
54+
1. [DynamoDB Stream](#dynamodb-stream) with support for success and failure responses
55+
1. [Lambda Authorizer](#lambda-authorizer) with support for creating access policies for successfully authorized requests
56+
1. [SNS](#sns) with support for success and failure responses
57+
1. [Generic event](#generic-event) wrapper with support for success and failure responses
58+
59+
## API Gateway
60+
61+
### Sample implementation
62+
63+
```ts
64+
import { api } from '@manwaring/lambda-wrapper';
65+
66+
export const handler = api(async ({ body, path, success, error }) => {
67+
try {
68+
const { pathParam1, pathParam2 } = path;
69+
const results = await doSomething(body, pathParam1, pathParam2);
70+
return success(results);
71+
} catch (err) {
72+
return error(err);
73+
}
74+
});
75+
```
76+
77+
### Properties and methods available on wrapper signature
78+
79+
```ts
80+
interface ApiSignature {
81+
event: APIGatewayEvent; // original event
82+
body: any; // JSON or form parsed body payload if exists (based on content-type headers), otherwise the raw body object
83+
path: { [name: string]: string }; // path param payload as key-value pairs
84+
query: { [name: string]: string }; // query param payload as key-value pairs
85+
headers: { [name: string]: string }; // headers param payload as key-value pairs
86+
testRequest: boolean; // indicates if this is a test request, based on presence of headers matching 'Test-Request' or process.env.TEST_REQUEST_HEADER
87+
auth: any; // auth context from custom authorizer
88+
success(payload?: any, replacer?: (this: any, key: string, value: any) => any): ApiResponse; // returns 200 status with payload
89+
invalid(errors?: string[]): ApiResponse; // returns 400 status with errors
90+
notFound(message?: string): ApiResponse; // returns 404 status with message
91+
redirect(url: string): ApiResponse; // returns 302 redirect with new url
92+
error(error?: any): ApiResponse; // returns 503 status with error
93+
}
94+
95+
interface ApiResponse {
96+
statusCode: number;
97+
headers: { [name: string]: string | boolean };
98+
body?: string;
99+
}
100+
```
101+
102+
\*Note that each callback helper functions (success, invalid, redirect, error) includes CORS-enabling header information
103+
104+
## CloudFormation Custom Resource
105+
106+
### Sample implementation
107+
108+
```ts
109+
import { cloudFormation } from '@manwaring/lambda-wrapper';
110+
111+
export const handler = cloudFormation(({ event, success, failure }) => {
112+
try {
113+
const { BucketName } = event.ResourceProperties;
114+
return success();
115+
} catch (err) {
116+
return failure(err);
117+
}
118+
});
119+
```
120+
121+
\*Note that currently the method wrapped by cloudFormation cannot be async - for reasons that aren't entirely clear to me when the method is async the requests to update CloudFormation with the correct action status fail, leaving a stack in the 'pending' state
122+
123+
### Properties and methods available on wrapper signature
124+
125+
```ts
126+
interface CloudFormationSignature {
127+
event: CloudFormationCustomResourceEvent; // original event
128+
success(payload?: any): void; // sends CloudFormation success event
129+
failure(message?: any): void; // sends CloudFormation failure event
130+
}
131+
```
132+
133+
## DynamoDB Stream
134+
135+
### Sample implementation
136+
137+
```ts
138+
import { dynamodbStream } from '@manwaring/lambda-wrapper';
139+
140+
export const handler = dynamodbStream(async ({ newVersions, success, error }) => {
141+
try {
142+
newVersions.forEach(version => console.log(version));
143+
return success(newVersions);
144+
} catch (err) {
145+
return error(err);
146+
}
147+
});
148+
```
149+
150+
### Properties and methods available on wrapper signature
151+
152+
```ts
153+
interface DynamoDBStreamSignature {
154+
event: DynamoDBStreamEvent; // original event
155+
newVersions: any[]; // array of all unmarshalled javascript objects of new images
156+
oldVersions: any[]; // array of all unmarshalled javascript objects of old images
157+
versions: Version[]; // array of full version object (new image, old image, etc - see Version interface)
158+
success(message?: any): any; // logs and returns the message
159+
error(error?: any): void; // logs the error and throws it
160+
}
161+
162+
interface Version {
163+
newVersion: any; // unmarshalled javascript object of new image (if exists) or null
164+
oldVersion: any; // unmarshalled javascript object of old image (if exists) or null
165+
keys: any; // unmarshalled javascript object of keys (includes key values)
166+
tableName: string; // name of the table the object came from
167+
tableArn: string; // arn of the table the object came from
168+
eventName: string; // name of the event (INSERT || MODIFY || REMOVE)
169+
}
170+
```
171+
172+
## Lambda Authorizer
173+
174+
### Sample implementation
175+
176+
```ts
177+
import { authorizer } from '@manwaring/lambda-wrapper';
178+
const verifier = new Verifier(); // setup and configure JWT validation library
179+
180+
export const handler = authorizer(async ({ token, valid, invalid }) => {
181+
try {
182+
if (!token) {
183+
return invalid('Missing token');
184+
}
185+
const jwt = await verifier.verifyAccessToken(token);
186+
return valid(jwt);
187+
} catch (err) {
188+
return invalid(err);
189+
}
190+
});
191+
```
192+
193+
### Properties and methods available on wrapper signature
194+
195+
```ts
196+
interface AuthorizerSignature {
197+
event: CustomAuthorizerEvent; // original event
198+
token: string; // authorizer token from original event
199+
valid(jwt: any): Policy; // returns AWS policy to authenticate request, and adds auth context if available
200+
invalid(message?: any): void; // records invalid information and throws 401 unauthorized
201+
error(error?: any): void; // records error information and throws 401 unauthorized
202+
}
203+
204+
interface Policy {
205+
principalId: string;
206+
policyDocument: {
207+
Version: string;
208+
Statement: {
209+
Action: string;
210+
Effect: string;
211+
Resource: string;
212+
}[];
213+
};
214+
}
215+
```
216+
217+
## SNS
218+
219+
### Sample implementation
220+
221+
```ts
222+
import { sns } from '@manwaring/lambda-wrapper';
223+
224+
export const handler = sns(async ({ message, success, error }) => {
225+
try {
226+
console.log(message);
227+
return success();
228+
} catch (err) {
229+
return error(err);
230+
}
231+
});
232+
```
233+
234+
### Properties and methods available on wrapper signature
235+
236+
```ts
237+
interface SnsSignature {
238+
event: SNSEvent; // original event
239+
message: any; // JSON-parsed message from event
240+
success(message?: any): any; // logs and returns the message
241+
error(error?: any): void; // logs the error and throws
242+
}
243+
```
244+
245+
## Generic event
246+
247+
### Sample implementation
248+
249+
```ts
250+
import { wrapper } from '@manwaring/lambda-wrapper';
251+
252+
export const handler = wrapper(async ({ event, success, error }) => {
253+
try {
254+
const { value1, value2 } = event;
255+
const results = await doSomething(value1, value2);
256+
return success(results);
257+
} catch (err) {
258+
return error(err);
259+
}
260+
});
261+
```
262+
263+
### Properties and methods available on wrapper signature
264+
265+
```ts
266+
interface WrapperSignature {
267+
event: any; // original event
268+
success(message?: any): any; // logs and returns the message
269+
error(error?: any): void; // logs the error and throws
270+
}
271+
```
272+
273+
# Example projects
274+
275+
THere is one [working example](examples) of how this package can be used in a simple 'hello world' serverless application:
276+
277+
1. [Using the Serverless Framework and TypeScript](examples/ts)

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@manwaring/lambda-wrapper",
33
"description": "A lambda handler wrapper to abstract common functionality and provide useful defaults",
4-
"version": "2.1.1",
4+
"version": "3.0.0",
55
"scripts": {
66
"publish-please-dry-run": "publish-please --dry-run",
77
"publish-please": "publish-please",

src/api/responses.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ describe('API responses', () => {
4444
expect(response).toEqual({
4545
headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Credentials': true },
4646
body: JSON.stringify({ message: 'error' }),
47-
statusCode: 503
47+
statusCode: 500
4848
});
4949
});
5050

src/api/responses.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export function notFound(message?: string): ApiResponse {
3535
}
3636

3737
export function error(message?: any): ApiResponse {
38-
const response = { statusCode: 503, headers: DEFAULT_HEADERS };
38+
const response = { statusCode: 500, headers: DEFAULT_HEADERS };
3939
if (message && message instanceof Error) {
4040
logger.debug('Encountered error while processing request', message);
4141
message = message.message;

0 commit comments

Comments
 (0)