npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@senacor/azure-function-middleware

v3.1.1

Published

Middleware for azure functions to handle authentication, authorization, error handling and logging

Downloads

2,050

Readme

Azure Function Middleware

Introduction

The Azure Function Middleware introduces a middleware pattern for Azure Functions in Node.js, enhancing the development experience by simplifying the integration of cross-cutting concerns such as schema validation, authorization, and error handling.

Installation

Before you integrate this middleware into your project, ensure you have Node.js installed, and you're familiar with Azure Functions. Follow these steps to set up:

npm install @senacor/azure-function-middleware

Usage

The middleware interface is intuitive, designed for expansion, and integrates seamlessly with Azure Functions. Here's a quick example to get you started:

const schema = Joi.object().keys({
    name: Joi.string().min(3).max(30).required(),
});

const functionHandler = async (context: Context, req: HttpRequest): Promise<void> => {
    context.res = { status: 201 };
};

export default middleware([validation(schema), functionHandler, []]);

This pattern aims to deliver a core set of features and a simplified interface for creating additional middleware functions tailored for Azure Functions.

Error Handling

Centralized error management is a key feature, ensuring all errors within the function's flow are intercepted and appropriately handled. Specific error responses can be defined by throwing errors in the following format:

export class ApplicationError<T> extends Error {
    status: number;
    body?: T;
}

Any error thrown in the function with this signature is getting returned to the caller with the defined status and body.

Generic Functions

The middleware supports the integration of generic functions like request validation or authorization. These functions must comply with the 'AzureFunction' type from the '@azure/functions' package. They are crucial for extending the middleware's capabilities while adhering to Azure's function signature requirements.

import { AzureFunction } from '@azure/functions';

// 'AzureFunction' type signature
export type AzureFunction = (context: Context, ...args: any[]) => Promise<any> | void;

// Configuring middleware with generic functions
export default middleware([validation(schema)], functionHandler, []);

Such generic functions are executed in sequence before the main handler function. If a post-execution function is necessary, it can be included in the postExecution array, the third argument in the middleware function. The post execution functions are also executed in sequence

Validation

The function to validate requests is based on Joi. The usage is fairly simply:

export default middleware([requestValidation(schema)], functionHandler, []);

The passed schema is a Joi ObjectSchema to check the passed request against. When the request is valid against the schema, the next middleware function gets called. In case the check of the request against the schema is invalid, the middleware function throws an error, canceling the request and returning an 400 - Bad Request with the Joi error message.

The body of the response could be customized by adding a transformer like in the following example. The passed message is the Joi error message.

export default middleware(handler, [
    requestValidation(schema, {
        transformErrorMessage: (message) => ({
            type: 'Invalid  request object',
            detail: message,
        })
    }),
])

By default, the request body is getting validated. To validate other parts of the request or context the extractValidationContentFromRequest function could be used, when initializing the middleware.

export default middleware([
    requestValidation(schema, {extractValidationContentFromRequest: (req, context) => req.query.name})],
    handler,
    []
)

In this example the name contained in the query is getting validated against the passed request.

You can also ensure the integrity of your handler's responses by utilizing the responseValidation function. However, you might prefer to avoid interruptions in the application flow caused by thrown errors, opting instead for error logging. This can be achieved with the following configuration:

export default middleware([
    responseValidation(schema, {shouldThrowOnValidationError: false})],
    handler,
    []
)

This approach validates the response against the provided schema but, instead of halting execution when encountering validation errors, it logs the issues for review without throwing an exception.

Authorization

The authorization function verifies request parameters against JWT Bearer Tokens, employing customizable extraction functions for flexible security checks.

export default middleware(functionHandler, [authorization([])]);

The passed values in the array needs to be defined based on the following structure:

export type Rule<T> = {
    parameterExtractor: (parameters: ContextBindingData) => string;
    jwtExtractor: (jwt: T) => string;
};

Header authentication

To authenticate requests against a rule, the header could be used. Therefore, the headerAuthentication pre-function is available.

export default middleware(functionHandler, [headerAuthentication()]);

When no parameter is passed to the headerAuthentication the header x-ms-client-principal-id is checked, if present or not. This header is added to a request by the Azure plattform when e.g. a JWT Token is successfully validated. The x-ms-client-principal-id and x-ms-client-principal-name header could only be set by the Azure plattform (https://learn.microsoft.com/en-us/azure/app-service/configure-authentication-user-identities).

It is also possible to pass a function to validate a specific header, like checking for basic authentication credentials. This could be done in the following manner headerAuthentication((context, request) => {...}).

Post function execution

Post-execution functions, ideal for tasks like closing database connections, can be defined to run after the main handler execution.

const postFunction = (context: Context, request: HttpRequest): Promise<void> => {
    context.log("Called after function")
    return;
}

export default middleware(functionHandler, [], [postFunction]);

Logging and Tracing with appInsights

To enhance the logging and tracing with appInsights you can wrap your function with the appInsightWrapper. Currently, this will add request parameters and workflow data into the customProperties, which will make your logs more searchable.

Use the AppInsightForHttpTrigger for your http-functions:

import {AppInsightForHttpTrigger} from "./appInsightsWrapper";

export default middleware([AppInsightForHttpTrigger.setup], handler, [AppInsightForHttpTrigger.finalizeAppInsight])

and the AppInsightForNonHttpTrigger for functions with different kinds of trigger (e.g. activityTrigger or timerTrigger).

Per default the request and response bodies of http requests are only logged if the request fails. You can customize this behavior by using AppInsightForHttpTrigger.finalizeWithConfig(...) instead of AppInsightForHttpTrigger.finalizeAppInsight. There you can also provide a function to sanitize the request and response bodies to prevent logging of sensitive data.

Support and Contact

If you encounter any issues or have questions about using this middleware, please file an issue in this repository or contact the maintainers at [email protected] or [email protected].