Skip to content

Commit 7c76f28

Browse files
authored
Merge pull request jeremydaly#43 from jeremydaly/v0.6.0
v0.6.0
2 parents 5e13c01 + 8ac2c86 commit 7c76f28

12 files changed

Lines changed: 952 additions & 88 deletions

File tree

README.md

Lines changed: 166 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,50 @@ Lambda API has **ZERO** dependencies.
3737

3838
Lambda API was written to be extremely lightweight and built specifically for serverless applications using AWS Lambda. It provides support for API routing, serving up HTML pages, issuing redirects, serving binary files and much more. It has a powerful middleware and error handling system, allowing you to implement everything from custom authentication to complex logging systems. Best of all, it was designed to work with Lambda's Proxy Integration, automatically handling all the interaction with API Gateway for you. It parses **REQUESTS** and formats **RESPONSES** for you, allowing you to focus on your application's core functionality, instead of fiddling with inputs and outputs.
3939

40+
## Table of Contents
41+
- [Installation](#installation)
42+
- [Requirements](#requirements)
43+
- [Configuration](#configuration)
44+
- [Recent Updates](#recent-updates)
45+
- [Routes and HTTP Methods](#routes-and-http-methods)
46+
- [Returning Responses](#returning-responses)
47+
- [Async/Await](#asyncawait)
48+
- [Promises](#promises)
49+
- [Route Prefixing](#route-prefixing)
50+
- [Debugging Routes](#debugging-routes)
51+
- [REQUEST](#request)
52+
- [RESPONSE](#response)
53+
- [attachment()](#attachmentfilename)
54+
- [clearCookie()](#clearcookiename-options)
55+
- [cookie()](#cookiename-value-options)
56+
- [cors()](#corsoptions)
57+
- [download()](#downloadfile--filename--options--callback)
58+
- [error()](#errormessage)
59+
- [etag()](#etagboolean)
60+
- [getHeader()](#getheaderkey)
61+
- [hasHeader()](#hasheaderkey)
62+
- [header()](#headerkey-value)
63+
- [html()](#htmlbody)
64+
- [json()](#jsonbody)
65+
- [jsonp()](#jsonpbody)
66+
- [location](#locationpath)
67+
- [redirect()](#redirectstatus-path)
68+
- [removeHeader()](#removeheaderkey)
69+
- [send()](#sendbody)
70+
- [sendFile()](#sendfilefile--options--callback)
71+
- [status()](#statuscode)
72+
- [type()](#typetype)
73+
- [Enabling Binary Support](#enabling-binary-support)
74+
- [Path Parameters](#path-parameters)
75+
- [Wildcard Routes](#wildcard-routes)
76+
- [Middleware](#middleware)
77+
- [Clean Up](#clean-up)
78+
- [Error Handling](#error-handling)
79+
- [Namespaces](#namespaces)
80+
- [CORS Support](#cors-support)
81+
- [Lambda Proxy Integration](#lambda-proxy-integration)
82+
- [Configuring Routes in API Gateway](#configuring-routes-in-api-gateway)
83+
- [Contributions](#contributions)
4084

4185
## Installation
4286
```
@@ -65,6 +109,9 @@ const api = require('lambda-api')({ version: 'v1.0', base: 'v1' });
65109
## Recent Updates
66110
For detailed release notes see [Releases](https://github.com/jeremydaly/lambda-api/releases).
67111

112+
### v0.6: Support for both `callback-style` and `async-await`
113+
In additional to `res.send()`, you can now simply `return` the body from your route and middleware functions. See [Returning Responses](#returning-responses) for more information.
114+
68115
### v0.5: Remove Bluebird Promises Dependency
69116
Now that AWS Lambda supports Node v8.10, asynchronous operations can be handled more efficiently with `async/await` rather than with promises. The core Lambda API execution engine has been rewritten to take advantage of `async/await`, which means we no longer need to depend on Bluebird. We now have **ZERO** dependencies.
70117

@@ -88,7 +135,7 @@ const api = require('lambda-api')({ version: 'v1.0', base: 'v1' });
88135

89136
## Routes and HTTP Methods
90137

91-
Routes are defined by using convenience methods or the `METHOD` method. There are currently six convenience route methods: `get()`, `post()`, `put()`, `patch()`, `delete()` and `options()`. Convenience route methods require two parameters, a *route* and a function that accepts two arguments. A *route* is simply a path such as `/users`. The second parameter must be a function that accepts a `REQUEST` and a `RESPONSE` argument. These arguments can be named whatever you like, but convention dictates `req` and `res`. Examples using convenience route methods:
138+
Routes are defined by using convenience methods or the `METHOD` method. There are currently eight convenience route methods: `get()`, `post()`, `put()`, `patch()`, `delete()`, `head()`, `options()` and `any()`. Convenience route methods require two parameters, a *route* and a function that accepts two arguments. A *route* is simply a path such as `/users`. The second parameter must be a function that accepts a `REQUEST` and a `RESPONSE` argument. These arguments can be named whatever you like, but convention dictates `req` and `res`. Examples using convenience route methods:
92139

93140
```javascript
94141
api.get('/users', (req,res) => {
@@ -103,15 +150,90 @@ api.delete('/users', (req,res) => {
103150
// do something
104151
})
105152
```
106-
Additional methods are support by calling the `METHOD` method with three arguments. The first argument is the HTTP method, a *route*, and a function that accepts a `REQUEST` and a `RESPONSE` argument.
153+
Additional methods are support by calling the `METHOD` method with three arguments. The first argument is the HTTP method (or array of methods), a *route*, and a function that accepts a `REQUEST` and a `RESPONSE` argument.
107154

108155
```javascript
109156
api.METHOD('trace','/users', (req,res) => {
110-
// do something
157+
// do something on TRACE
158+
})
159+
160+
api.METHOD(['post','put'],'/users', (req,res) => {
161+
// do something on POST -or- PUT
162+
})
163+
```
164+
165+
All `GET` methods have a `HEAD` alias that executes the `GET` request but returns a blank `body`. `GET` requests should be idempotent with no side effects. The `head()` convenience method can be used to set specific paths for `HEAD` requests or to override default `GET` aliasing.
166+
167+
Routes that use the `any()` method or pass `ANY` to `api.METHOD` will respond to all HTTP methods. Routes that specify a specific method (such as `GET` or `POST`), will override the route for that method. For example:
168+
169+
```javascript
170+
api.any('/users', (req,res) => { res.send('any') })
171+
api.get('/users', (req,res) => { res.send('get') })
172+
```
173+
174+
A `POST` to `/users` will return "any", but a `GET` request would return "get". Please note that routes defined with an `ANY` method will override default `HEAD` aliasing for `GET` routes.
175+
176+
## Returning Responses
177+
178+
Lambda API supports both `callback-style` and `async-await` for returning responses to users. The [RESPONSE](#response) object has several callbacks that will trigger a response (`send()`, `json()`, `html()`, etc.) You can use any of these callbacks from within route functions and middleware to send the response:
179+
180+
```javascript
181+
api.get('/users', (req,res) => {
182+
res.send({ foo: 'bar' })
183+
})
184+
```
185+
186+
You can also `return` data from route functions and middleware. The contents will be sent as the body:
187+
188+
```javascript
189+
api.get('/users', (req,res) => {
190+
return { foo: 'bar' }
191+
})
192+
```
193+
194+
### Async/Await
195+
196+
If you prefer to use `async/await`, you can easily apply this to your route functions.
197+
198+
Using `return`:
199+
```javascript
200+
api.get('/users', async (req,res) => {
201+
let users = await getUsers()
202+
return users
203+
})
204+
```
205+
206+
Or using callbacks:
207+
```javascript
208+
api.get('/users', async (req,res) => {
209+
let users = await getUsers()
210+
res.send(users)
211+
})
212+
```
213+
214+
### Promises
215+
216+
If you like promises, you can either use a callback like `res.send()` at the end of your promise chain, or you can simply `return` the resolved promise:
217+
218+
```javascript
219+
api.get('/users', (req,res) => {
220+
getUsers().then(users => {
221+
res.send(users)
222+
})
111223
})
112224
```
113225

114-
All `GET` methods have a `HEAD` alias that executes the `GET` request but returns a blank `body`. `GET` requests should be idempotent with no side effects.
226+
OR
227+
228+
```javascript
229+
api.get('/users', (req,res) => {
230+
return getUsers().then(users => {
231+
return users
232+
})
233+
})
234+
```
235+
236+
**IMPORTANT:** You must either use a callback like `res.send()` **OR** `return` a value. Otherwise the execution will hang and no data will be sent to the user. Also, be sure not to return `undefined`, otherwise it will assume no response.
115237

116238
## Route Prefixing
117239

@@ -158,6 +280,39 @@ module.exports = (api, opts) => {
158280

159281
This would create a `/v1/product` and `/v1/v2/product` route. You can also use `register()` to load routes from an external file without the `prefix`. This will just add routes to your `base` path. **NOTE:** Prefixed routes are built off of your `base` path if one is set. If your `base` was set to `/api`, then the first example above would produce the routes: `/api/v1/product` and `/api/v2/product`.
160282

283+
## Debugging Routes
284+
285+
Lambda API has a `routes()` method that can be called on the main instance that will return an array containing the `METHOD` and full `PATH` of every configured route. This will include base paths and prefixed routes. This is helpful for debugging your routes.
286+
287+
```javascript
288+
const api = require('lambda-api')()
289+
290+
api.get('/', (req,res) => {})
291+
api.post('/test', (req,res) => {})
292+
293+
api.routes() // => [ [ 'GET', '/' ], [ 'POST', '/test' ] ]
294+
```
295+
296+
You can also log the paths in table form to the console by passing in `true` as the only parameter.
297+
298+
```javascript
299+
const api = require('lambda-api')()
300+
301+
api.get('/', (req,res) => {})
302+
api.post('/test', (req,res) => {})
303+
304+
api.routes(true)
305+
306+
// Outputs to console
307+
╔═══════════╤═════════════════╗
308+
METHODROUTE
309+
╟───────────┼─────────────────╢
310+
GET/
311+
╟───────────┼─────────────────╢
312+
POST/test ║
313+
╚═══════════╧═════════════════╝
314+
```
315+
161316

162317
## REQUEST
163318

@@ -178,6 +333,7 @@ The `REQUEST` object contains a parsed and normalized request from API Gateway.
178333
- `rawBody`: If the `isBase64Encoded` flag is `true`, this is a copy of the original, base64 encoded body
179334
- `route`: The matched route of the request
180335
- `requestContext`: The `requestContext` passed from the API Gateway
336+
- `auth`: An object containing the `type` and `value` of an authorization header. Currently supports `Bearer`, `Basic`, `OAuth`, and `Digest` schemas. For the `Basic` schema, the object is extended with additional fields for username/password. For the `OAuth` schema, the object is extended with key/value pairs of the supplied OAuth 1.0 values.
181337
- `namespace` or `ns`: A reference to modules added to the app's namespace (see [namespaces](#namespaces))
182338
- `cookies`: An object containing cookies sent from the browser (see the [cookie](#cookiename-value-options) `RESPONSE` method)
183339

@@ -388,6 +544,9 @@ res.clearCookie('fooArray', { path: '/', httpOnly: true }).send()
388544
```
389545
**NOTE:** The `clearCookie()` method only sets the header. A execution ending method like `send()`, `json()`, etc. must be called to send the response.
390546

547+
### etag([boolean])
548+
Enables Etag generation for the response if at value of `true` is passed in. Lambda API will generate an Etag based on the body of the response and return the appropriate header. If the request contains an `If-No-Match` header that matches the generated Etag, a `304 Not Modified` response will be returned with a blank body.
549+
391550
### attachment([filename])
392551
Sets the HTTP response `Content-Disposition` header field to "attachment". If a `filename` is provided, then the `Content-Type` is set based on the file extension using the `type()` method and the "filename=" parameter is added to the `Content-Disposition` header.
393552

@@ -508,6 +667,8 @@ api.use((req,res,next) => {
508667

509668
The `next()` callback tells the system to continue executing. If this is not called then the system will hang and eventually timeout unless another request ending call such as `error` is called. You can define as many middleware functions as you want. They will execute serially and synchronously in the order in which they are defined.
510669

670+
**NOTE:** Middleware can use either callbacks like `res.send()` or `return` to trigger a response to the user. Please note that calling either one of these from within a middleware function will terminate execution and return the response immediately.
671+
511672
## Clean Up
512673
The API has a built-in clean up method called 'finally()' that will execute after all middleware and routes have been completed, but before execution is complete. This can be used to close database connections or to perform other clean up functions. A clean up function can be defined using the `finally` method and requires a function with two parameters for the REQUEST and the RESPONSE as its only argument. For example:
513674

@@ -529,7 +690,7 @@ api.use((err,req,res,next) => {
529690
})
530691
```
531692

532-
The `next()` callback will cause the script to continue executing and eventually call the standard error handling function. You can short-circuit the default handler by calling a request ending method such as `send`, `html`, or `json`.
693+
The `next()` callback will cause the script to continue executing and eventually call the standard error handling function. You can short-circuit the default handler by calling a request ending method such as `send`, `html`, or `json` OR by `return`ing data from your handler.
533694

534695
## Namespaces
535696
Lambda API allows you to map specific modules to namespaces that can be accessed from the `REQUEST` object. This is helpful when using the pattern in which you create a module that exports middleware, error, or route functions. In the example below, the `data` namespace is added to the API and then accessed by reference within an included module.

0 commit comments

Comments
 (0)