Skip to content

Commit

Permalink
Merge pull request #7 from jeremydaly/v0.1.0
Browse files Browse the repository at this point in the history
v0.1.0
  • Loading branch information
jeremydaly authored Jan 28, 2018
2 parents dd086c1 + 702cebc commit 7579ee3
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 29 deletions.
55 changes: 33 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,32 @@
# lambda-api
**PLEASE NOTE:** This project is still in beta and should be used with caution in production.

[![Build Status](https://travis-ci.org/jeremydaly/lambda-api.svg?branch=master)](https://travis-ci.org/jeremydaly/lambda-api)
[![npm](https://img.shields.io/npm/v/lambda-api.svg)](https://www.npmjs.com/package/lambda-api)
[![npm](https://img.shields.io/npm/l/lambda-api.svg)](https://www.npmjs.com/package/lambda-api)

### Lightweight Node.js API for AWS Lambda

Lambda API is a lightweight Node.js API router for use with AWS API Gateway and AWS Lambda using Lambda Proxy integration. This closely mirrors (and is based on Express.js) but is significantly stripped down to maximize performance with Lambda's stateless, single run executions. The API uses Bluebird promises to serialize asynchronous execution.
Lambda API is a lightweight Node.js API router for use with AWS API Gateway and AWS Lambda using Lambda Proxy integration. This closely mirrors (and is based on) other routers like Express.js but is significantly stripped down to maximize performance with Lambda's stateless, single run executions. The API uses Bluebird promises to serialize asynchronous execution.

## Simple Example

```javascript
const API = require('lambda-api') // API library

// Init API instance
const api = new API({ version: 'v1.0', base: 'v1' });

api.get('/test', function(req,res) {
res.status(200).json({ status: 'ok' })
})

module.exports.handler = (event, context, callback) => {

// Run the request
api.run(event,context,callback);

} // end handler
```

## Lambda Proxy integration
Lambda Proxy Integration is an option in API Gateway that allows the details of an API request to be passed as the `event` parameter of a Lambda function. A typical API Gateway request event with Lambda Proxy Integration enabled looks like this:
Expand Down Expand Up @@ -73,26 +92,6 @@ Lambda Proxy Integration is an option in API Gateway that allows the details of

The API automatically parses this information to create a normalized `REQUEST` object. The request can then be routed using the APIs methods.

## Simple Example

```javascript
const API = require('lambda-api') // API library

// Init API instance
const api = new API({ version: 'v1.0', base: 'v1' });

api.get('/test', function(req,res) {
res.status(200).json({ status: 'ok' })
})

module.exports.handler = (event, context, callback) => {

// Run the request
api.run(event,context,callback);

} // end handler
```

## Configuration

Include the `lambda-api` module into your Lambda handler script and initialize an instance. You can initialize the API with an optional `version` which can be accessed via the `REQUEST` object and a `base` path. The base path can be used to route multiple versions to different instances.
Expand Down Expand Up @@ -144,6 +143,7 @@ The `REQUEST` object contains a parsed and normalized request from API Gateway.
- If the `Content-Type` header is `application/x-www-form-urlencoded`, it will attempt to parse a URL encoded string using `querystring`
- Otherwise it will be plain text.
- `route`: The matched route of the request
- `requestContext`: The `requestContext` passed from the API Gateway

The request object can be used to pass additional information through the processing chain. For example, if you are using a piece of authentication middleware, you can add additional keys to the `REQUEST` object with information about the user. See [middleware](#middleware) for more information.

Expand Down Expand Up @@ -248,6 +248,17 @@ api.use(function(req,res,next) {

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.

### Clean Up
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:

```javascript
api.finally(function(req,res) {
// close unneeded database connections and perform clean up
})
```

The `RESPONSE` **CANNOT** be manipulated since it has already been generated. Only one `finally()` method can be defined. This uses the Bluebird `finally()` method internally and will execute after properly handled errors as well.

## Error Handling
The API has simple built-in error handling that will log the error using `console.log`. These will be available via CloudWatch Logs. By default, errors will trigger a JSON response with the error message. If you would like to define additional error handling, you can define them using the `use` method similar to middleware. Error handling middleware must be defined as a function with **four** arguments instead of three like normal middleware. An additional `error` parameter must be added as the first parameter. This will contain the error object generated.

Expand Down
7 changes: 7 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
'use strict';

/**
* Lightweight Node.js API for AWS Lambda
* @author Jeremy Daly <jeremy@jeremydaly>
* @version 0.1.0
* @license MIT
*/

const REQUEST = require('./request.js') // Response object
const RESPONSE = require('./response.js') // Response object
const Promise = require('bluebird') // Promise library
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "lambda-api",
"version": "0.1.0-beta",
"version": "0.1.0",
"description": "Lightweight Node.js API for AWS Lambda",
"main": "index.js",
"scripts": {
Expand Down
8 changes: 2 additions & 6 deletions request.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,9 @@ class REQUEST {

// Set the headers
this.headers = app._event.headers

// Set the requestContext from the event where one can find the authorizer Context from AWS
this.requestContext = app._event.requestContext

// console.log(this.headers);
// console.log(app._event.body);
// console.log('Content-Type', this.headers['Content-Type']);
// Set the requestContext
this.requestContext = app._event.requestContext

// Set the body
if (this.headers['Content-Type'] && this.headers['Content-Type'].includes("application/x-www-form-urlencoded")) {
Expand Down
70 changes: 70 additions & 0 deletions test/finally.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
'use strict';

const Promise = require('bluebird') // Promise library
const expect = require('chai').expect // Assertion library
const API = require('../index') // API library

// Init API instance
const api = new API({ version: 'v1.0', base: 'v1' });

// Simulate database module
const fakeDatabase = { connected: true }

// NOTE: Set test to true
api._test = true;

let event = {
httpMethod: 'get',
path: '/test',
body: {},
headers: {
'Content-Type': 'application/json'
}
}

/******************************************************************************/
/*** DEFINE TEST ROUTE ***/
/******************************************************************************/
api.get('/test', function(req,res) {
res.status(200).json({
method: 'get',
status: 'ok',
connected: fakeDatabase.connected
})
})

/******************************************************************************/
/*** DEFINE FINALLY METHOD ***/
/******************************************************************************/
api.finally(function(req,res) {
fakeDatabase.connected = false
})

/******************************************************************************/
/*** BEGIN TESTS ***/
/******************************************************************************/

describe('Finally Tests:', function() {

it('Connected on first execution and after callback', function() {
let _event = Object.assign({},event,{})

return new Promise((resolve,reject) => {
api.run(_event,{},function(err,res) { resolve(res) })
}).then((result) => {
expect(result).to.deep.equal({ headers: { 'Content-Type': 'application/json' }, statusCode: 200, body: '{"method":"get","status":"ok","connected":true}' })
expect(fakeDatabase).to.deep.equal({ connected: true })
})
}) // end it

it('Disconnected on second execution', function() {
let _event = Object.assign({},event,{})

return new Promise((resolve,reject) => {
api.run(_event,{},function(err,res) { resolve(res) })
}).then((result) => {
expect(result).to.deep.equal({ headers: { 'Content-Type': 'application/json' }, statusCode: 200, body: '{"method":"get","status":"ok","connected":false}' })
})
}) // end it

}) // end FINALLY tests

0 comments on commit 7579ee3

Please sign in to comment.