From c55cc7f6bff520a9ed3f2057dd431e5b8f7d2f38 Mon Sep 17 00:00:00 2001 From: Jeremy Daly Date: Sun, 28 Jan 2018 11:29:56 -0500 Subject: [PATCH 1/5] add tests for finally() method --- test/finally.js | 70 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 test/finally.js diff --git a/test/finally.js b/test/finally.js new file mode 100644 index 0000000..692ff9e --- /dev/null +++ b/test/finally.js @@ -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 From 71f69f562019e5c6b16a3920f047cc4fbcc46b36 Mon Sep 17 00:00:00 2001 From: Jeremy Daly Date: Sun, 28 Jan 2018 11:30:31 -0500 Subject: [PATCH 2/5] add author comment --- index.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/index.js b/index.js index 46e7726..fede7d2 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,12 @@ 'use strict'; +/** + * Lightweight Node.js API for AWS Lambda + * @author Jeremy Daly + * @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 From ca019d95264ca39a23e915327a9591831864b485 Mon Sep 17 00:00:00 2001 From: Jeremy Daly Date: Sun, 28 Jan 2018 11:32:34 -0500 Subject: [PATCH 3/5] clean up comments for requestContext pull request --- request.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/request.js b/request.js index 8e5937f..d102fb8 100644 --- a/request.js +++ b/request.js @@ -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")) { From 71a00c160160e139bafcb1240c1b7da4b3a92e87 Mon Sep 17 00:00:00 2001 From: Jeremy Daly Date: Sun, 28 Jan 2018 11:33:14 -0500 Subject: [PATCH 4/5] add documentation for finally() method, general edits --- README.md | 55 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 13dae03..bcba01c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ # 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) @@ -7,7 +6,27 @@ ### 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: @@ -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. @@ -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. @@ -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. From 702cebc8b528ddccd00c55c1a8fbfa4602915334 Mon Sep 17 00:00:00 2001 From: Jeremy Daly Date: Sun, 28 Jan 2018 11:34:12 -0500 Subject: [PATCH 5/5] v0.1.0 version bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 45bdcef..bc7ab06 100644 --- a/package.json +++ b/package.json @@ -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": {