Implement middleware pattern in Azure Functions

Emanuel Casco
Jan 4 · 3 min read

How to implement middleware pattern in Azure Functions to keep our functions clean.

Movement — Ph: Burak K

Also available in spanish.

Introduction

I wrote this post to share my experience implementing middleware pattern in Azure Functions.

Azure Functions is a serverless compute service that enables you to run code on-demand without having to explicitly manage infrastructure.

Biggest advantage of serverless computing is that you can . You can write code only for what truly matters to your business.

Biggest advantage of serverless computing is that you can ,allowing you to concentrate only on what really matters for the business.

But in real world applications you have to deal with some common technical concerns outside business logic, like input parsing and validation, output serialization, error handling, and more.

Very often, all this necessary code ends up polluting the pure business logic code in your functions, .

Web frameworks, like Express, Fastify or Hapi, has solved this problem using the middleware pattern. This pattern allows developers to isolate these common technical concerns into “steps” that decorate the main business logic code.

Implementation

After deciding to implement this pattern in my project, I made a small search to check if someone had already implemented a similar solution.

Unfortunately, the few solutions I found didn‘t meet my needs, so I decided to implement it myself.

That’s is how Azure-Middleware was born.

How it works

Validation

In serverless arquitectures is . Therefore, in order to avoid unexpected behaviors, is important ensure that function inputs belong to its domain.

To accomplish this mission Azure-Middleware uses Joi. It allows us to define a schema and check if the input message is valid or not.

With the validate method you can define the scheme that will be used to validate the messages. If your function is called with an invalid message then an exception will be thrown and your function won’t be executed.

module.exports = new MiddlewareHandler()
.validate(invalidJoiSchema)
.use(functionHandler)
.catch(errorHandler)
.listen();

Function chaining

use method is used to chain different function handlers, or middlewares, as “steps”. It expect a function handler as argument.

Each middleware is executed sequentially in the order in which the function was defined. Information flow passes to the next element of the chain when calling context.next.

module.exports = new MiddlewareHandler()
.validate(schema)
.use((ctx, msg) => {
ctx.log.info('Print first');
ctx.next();
})
.use((ctx, msg) => {
ctx.log.info('Print second');
ctx.done();
})
.catch(errorHandler)
.listen();

next is a method injected into context. It is used to iterate the middlewares chain.

Error handling

Error handling is very similar as it works in web frameworks like Express. When an exception is thrown, the first error handler into the middlewares chain will be executed. While all function handlers before will be ignored.

Also, you can jump to the next error handler using next. If this method receives an argument as first argument then it will be handled as an error.

Also, you can jump to the next error handler using context.next. If this method receives a non-nil value as first argument, it will be handled as an error.

Unlike the function handlers, the error handlers receive an error as the first argument.

module.exports = new MiddlewareHandler()
.use((ctx, msg) => {
ctx.log.info('Hello world');
ctx.next('ERROR!');
})
.use((ctx, msg) => {
ctx.log.info('Not executed :(');
ctx.next();
})
.catch((error, ctx, msg) => {
ctx.log.info(errors); // ERROR!
ctx.next();
})
.listen();

Wrap up

The package is still in development and I have some ideas to improve it. However, if you have any suggestion, please don’t doubt in contact me and let me know about it!

Thanks for reading. If you have thoughts on this, be sure to leave a comment.

You can follow me on Twitter, Github or LinkedIn.