CerberusMVC Documentation

Middleware


The sample project generated with the cli will make use of middleware bundled with the stack. You will see where the handler uses them, injects them. You can also write your own and bundle them in too.

Middleware, extended from base middleware is ran in order, first to last in... then the controller is ran, then first to last out. You can inject in as an array or individually using the auto or specific methods.

Bundled Middleware Classes

The following bundled middleware classes already exist in cerberus-mvc...

cerberus-mvc/Middleware/Cors

exports Cors

A great way to get cors working right away, utlises the client global proeprty to find the client origin for return.

cerberus-mvc/Middleware/Knex

exports Knex

Use as the last middleware in the chain, ensures your knex service dsiconnects from the database after any responses, so you dont have to do it on each query/model method.

Application Methods

Middleware needs injecting into the appplication instance form the handler. You can use the following methods to do this in teh handler.

  • app.middleware() - auto injection, pass a single or array of middleware objects, auto sniffs in, out and organises running order.
  • app.middlewareIn() - injection in specific middlware, pass a single or array of middleware objects.
  • app.middlewareOut() - injection Out specific middlware, pass a single or array of middleware objects.

Middleware can be passed in as a large array or teh commands can be called over and over adding middleware to the stack.

Example Middleware injection in AWS handler.js

'use strict';

const Application = require('cerberus-mvc/System/Application');
const CorsMiddleware = require('cerberus-mvc/Middleware/Cors');
const KnexMiddleware = require('cerberus-mvc/Middleware/Knex');
const KnexService = require('cerberus-mvc/Service/Knex');

exports.run = (event, context, callback) => { 
	const app = new Application('aws');
	const corsMiddleware = new CorsMiddleware(); 
	const knexMiddleware = new KnexMiddleware(); 
	const yourDBKnexService = new KnexService('postgres', '192.168.1.10', 5432, 'your_db', 'your_user', 'your_password'); 
	
	app.service(yourDBKnexService);
	app.middleware(corsMiddleware);
	app.middleware(knexMiddleware);
	
	app.run(event).then((response) => callback(null, response))
};

In

Middleware in will take in a request and should pass back out a request/modified request either directly or as a promise chain that returns it. Example below of authentication middleware, calling an authorization service (not bundled)

Example Auth Middleware (using a service)

'use strict';

const Middleware = require('cerberus-mvc/Base/Middleware');
const RestError = require('cerberus-mvc/Error/Rest');

/**
 * @namespace API/Middleware
 * @class Auth
 * @extends Middleware
 * @description Middleware class providing authentication actions in all incomming requests
 * @author Paul Smith (ulsmith)  
 * @copyright 2020 Paul Smith (ulsmith) all rights reserved
 * @license MIT
 */
class Auth extends Middleware {

	/**
	 * @public @method constructor
	 * @description Base method when instantiating class
	 */
	constructor() {
		super();
	}

    /**
	 * @public @method in
	 * @description Invoke middleware for incomming event
     * @param {Object} request The incoming request
     */
	in(request) {
		// is this an internal message from aws?
		if (request.source === 'event') {
			// incoming only
			if (this.$client.origin) throw new RestError('Origin cannot be set on message, access denied', 401);

			// public access, do not authorize
			if (request.access !== request.context.service) throw new RestError('No access to this function for [' + request.context.service + '], access denied', 401);

			return request;
		}

		// API GATEWAY API REQUEST
		
		// incoming only
		if (!this.$client.origin) throw new RestError('Origin is not set, access denied', 401);

		// origin failed to auth to white list
		if (this.$environment.API_CORS_LIST.replace(' ', '').split(',').indexOf(this.$client.origin) < 0) throw new RestError('Origin [' + this.$client.origin + '] is not allowed, access denied', 401);

		// public access, do not authorize, gets from controllers getter that matches method
		if (request.access === 'public') return request;

		// missing token
		if (!request.headers.Authorization) throw new RestError('Missing Authorization Token, invalid', 401);

		// get token bits
		if (request.headers.Authorization.split(' ')[0].toLowerCase() !== 'bearer') throw new RestError('Malformed Token due to missing "Bearer", invalid', 401);

		// verify against auth service, throws restError on failure
		return this.$services.auth.verify(request.headers.Authorization, request.headers['User-Agent']).then(() => request);
	}
}

module.exports = Auth;

Out

Middleware out will pass in a response and should pass back out the response/modified response eithe rdirectly or as a promise chain returning it. Example below of cors middleware (bundled)

Example CORS Middleware (using a service)

'use strict';

const Middleware = require('cerberus-mvc/Base/Middleware');

/**
 * @namespace MVC/Middleware
 * @class Cors
 * @extends Middleware
 * @description Middleware class providing cors patching to outgoing response
 * @author Paul Smith (ulsmith)  
 * @copyright 2020 Paul Smith (ulsmith) all rights reserved
 * @license MIT
 */
class Cors extends Middleware {

	/**
	 * @public @method constructor
	 * @description Base method when instantiating class
	 */
	constructor() {
		super();
	}

    /**
	 * @public @method out
	 * @description Invoke middleware for outgoing response
     * @param {Object} response The outgoing response to API Gateway
     * @param {Object} context The lambda context
     */
	out(response) {
		// update headers on way back out, for all requests that are not options (handled by API gateway directly)
		response.headers['Access-Control-Allow-Origin'] = this.$client.origin;
		response.headers['Access-Control-Allow-Credentials'] = 'true';
		response.headers['Access-Control-Allow-Headers'] = 'Accept, Cache-Control, Content-Type, Content-Length, Authorization, Pragma, Expires';
		response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS, PATCH';
		response.headers['Access-Control-Expose-Headers'] = 'Cache-Control, Content-Type, Authorization, Pragma, Expires';

		return response;
	}
}

module.exports = Cors;