Skip to content

Commit

Permalink
Merge pull request #346 from senacor/feature/improve-documentation-in…
Browse files Browse the repository at this point in the history
…-the-readme

[2.1.1]
~ Enhanced clarity and detail in the README.md documentation. Improvements include:
  - Refined explanations of middleware functionalities and usage.
  - Fixed the usage of the middleware api in the examples
  - Improved grammatical structure and readability throughout the document.
  - Restructured sections for better logical flow and understanding.
  • Loading branch information
maaaNu authored Nov 1, 2023
2 parents 305a8c1 + 9b66d4e commit be1cf80
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 26 deletions.
65 changes: 44 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
# Azure Function Middleware

Azure Function Middleware provides a simple way to use the middleware pattern for Azure Functions with NodeJS in order to define cross-cutting functionality such as schema validation, authorization and error handling.
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:


```bash
npm install @senacor/azure-function-middleware
```

## Usage
The interface is simple to use and could be easily expanded:
The middleware interface is intuitive, designed for expansion, and integrates seamlessly with Azure Functions. Here's a quick example to get you started:

```typescript
const schema = Joi.object().keys({
Expand All @@ -14,14 +26,15 @@ const functionHandler = async (context: Context, req: HttpRequest): Promise<void
context.res = { status: 201 };
};

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

The goal is to provide a minimal feature set with generic functionality and an easy interface to create other middleware functions, which could be used for an Azure function.
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

The middleware provides a central way to handle errors occurring in the control flow of the function. Every error thrown within the function gets caught by the middleware and processed. To define the correct response to a specific error the following structure can be thrown:
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:

```typescript
export class ApplicationError<T> extends Error {
Expand All @@ -34,26 +47,29 @@ Any error thrown in the function with this signature is getting returned to the

## Generic Functions

For additional generic functionality like request validation or authorization functions could be defined. The functions need to have the following structure:
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.

```typescript
export type MiddlewareFunction = (context: Context, request: HttpRequest) => Promise<void>;
```
import { AzureFunction } from '@azure/functions';

The function receives the Azure Function context and request to operate. The function needs to be passed when the middleware is configured.
// 'AzureFunction' type signature
export type AzureFunction = (context: Context, ...args: any[]) => Promise<any> | void;

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

In the above example a `validation` function is passed with a schema. All passed functions are called before the in the defined order before the handler function containing the logic for the request is called.
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.

### Validation

The function to validate requests is based on [Joi](https://www.npmjs.com/package/joi). The usage is fairly simply:

```typescript
export default middleware(functionHandler, [validation(schema)]);
export default middleware([validation(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 aan `400 - Bad Request` with the Joi error message.
Expand All @@ -72,16 +88,19 @@ export default middleware(handler, [
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.

```typescript
export default middleware(handler, [
validation(schema, undefined, (req, context) => req.query.name)),
])
export default middleware([
validation(schema, undefined, (req, context) => req.query.name)],
handler,
[]
)

```

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

### Authorization

To authorize a request the middleware function `authorization` could be used. The function is verifying a request parameter against a JWT Bearer Token. The information get extracted using two functions for the correct parameter and token counterpart.
The authorization function verifies request parameters against JWT Bearer Tokens, employing customizable extraction functions for flexible security checks.

```typescript
export default middleware(functionHandler, [authorization([])]);
Expand Down Expand Up @@ -112,15 +131,15 @@ This could be done in the following manner `headerAuthentication((context, reque

### Post function execution

To execute a function after the handler is called, a post function execution could be defined. The post function could be used to close for example a database connection or something similar.
Post-execution functions, ideal for tasks like closing database connections, can be defined to run after the main handler execution.

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

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

### Logging and Tracing with appInsights
Expand All @@ -135,4 +154,8 @@ 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`).
and the `AppInsightForNonHttpTrigger` for functions with different kinds of trigger (e.g. `activityTrigger` or `timerTrigger`).

## 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]>.
4 changes: 2 additions & 2 deletions example/test-validation-function/test-validation-function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ const functionHandler = async (context: Context, req: HttpRequest): Promise<void
context.res = { status: 200, body: { text: `Hallo ${req.body.name}` } };
};

const afterFunction = (context: Context): Promise<void> => {
const postFunction = (context: Context): Promise<void> => {
context.log('Called after function');
return;
};

export default middleware([validation(schema)], functionHandler, [afterFunction]);
export default middleware([validation(schema)], functionHandler, [postFunction]);
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@senacor/azure-function-middleware",
"version": "2.1.0",
"version": "2.1.1",
"description": "Middleware for azure functions to handle authentication, authorization, error handling and logging",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down

0 comments on commit be1cf80

Please sign in to comment.