app service Azure Custom Handler express-validator expressJS Functions NodeJS

Express.JS middlewares on Azure Functions via custom handlers

Azure functions has a really interesting feature called ’custom handlers’ which is something I had not previously run across. Custom Handler feature allows you to run/wrap another service, such as custom API service with Azure Functions, which would not otherwise be directly supported with Azure Functions runtime

I decided to do some tests with this feature using runtime that is supported (NodeJS) but with framework Express.JS*, which is not directly supported without re-architecting the solution

*Additionally: I find using Express.js as API very often my preferred choice, and this feature seems to bridge a certain gap for me, while not being the prime target for Custom Handler feature.

Update

Since there has been some traffic to this blog, I posted an example @ https://github.com/jsa2/azureFNcustomHandlers for example deployment.

The demo is also available from https://fn.dewi.red

  • The demo was for another scenario: consumption based web app, but since it’s using Express.JS you can just simply add express middlewares there.

Why?

I would mostly use this in scenario where you have existing Express.js based API that uses Express.js Middlewares and can’t (or wont) re-architect the service, but want to have other function approaches such as various bindings (Event Hub, Storage etc) available directly to the ExpressJS based Node API, without having to first handle them on a Azure Functions before passing them to another service, such as App Service API app, that can natively support ExpressJS.

  • Since functions don’t support Express.js, or Express.js middlewares, you have three options (assuming you are combining Express.js and Azure Functions in the first place)
  1. Run the Express API in App Service behind Azure Functions
  2. Re-architect the Express API to native functions
  3. Use Express API as Custom Handler in Azure Functions (Option explored in this article)
  • Note: In many more advanced cases you might also use Azure API management, and or Function Proxies, to provide more API lifecycle and management operations. Basically all of the options presented above could also be used to fitting use cases, and are not thus necessarily mutually exclusive
  • There are many other frameworks for creating API’s in NodeJS , so you should take this only as my personal preference based on me being comfortable (or limited) to Express.js 🙂

Background

Besides just me writing here about using Express.js and Azure Functions, it is highly recommended to see Azure as holistic solution for creating and managing API architectures; This example of custom handlers just shows that Azure Functions can go to great lengths to technically support various approaches – It’s entirely another thing whether this approach is recommended in the first place. The reader should treat it as just an testament to the flexibility of Azure Functions, and available approaches

Important sources of this article

Express – Node.js web application framework (expressjs.com)

Using Express middleware (Express.js.com)

Azure Functions custom handlers | Microsoft Docs

Azure Functions custom handler overview
Picture Source: Azure Functions custom handlers | Microsoft Docs

Shifting from Express.js to Azure Functions

Microsoft docs has great article for understanding the shift from Express.js to Azure functions.

  • In the context of this article it’s beneficial to understand option 2 ”Re-architect the Express API to native functions/ based on MS article ”shift from Express.js to Azure functions ”, since we are exploring option 3, ”Use Express API as Custom Handler in Azure Functions” which is alternative pattern, that does not require (necessarily) any re-architecting at all.
Picture source Shifting from Express.js to Azure Functions | Microsoft Docs

Benefits using Express API in Azure Functions

  • Minor to no changes needed for existing Express.js based API’s (Admittedly, this point might vary a lot)
    • Express middleware patterns are available now within Azure function itself
var Router = require('express').Router()
const {check} = require('express-validator')
Router.get("*", check('text').isEmail({
    require_tld: true
}), (req, res, next) => {
    console.log(req.query)
      req['express-validator#contexts'].map((result,index) => {
        
        if (result['_errors'].length > 0) {
            res.status(401)
            return next(JSON.stringify(result['_errors']))
        } else {
            res.status(200).json({message:"it was cool"})
        }
      })
    
})
module.exports = Router
  • No need to run separate App Service API behind function (this is if you still need to have Express based API in the first place)
  • Benefits of Azure Functions in general are now closer to Express API
    • Various input and output bindings are now available to Express.js as they would be for native function
  • Compared to normal Express Router, you can also use HTTP Trigger functions, and definitions as light API baselines for the endpoint to provide certain things to be handled before the request reaches

Azure Functions HTTP trigger | Microsoft Docs

{
  "bindings": [
    {
      "authLevel": "function",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
  ]
}

  • Also Function Proxies could be used to do some pre-handling for the requests (I have not tested this scenario with custom handler, but expect them to work also

Read more about function Proxies: You can use these proxies to break a large API into multiple function apps (as in a microservice architecture), while still presenting a single API surface for clients)https://docs.microsoft.com/en-us/azure/azure-functions/functions-proxies

Considerations using Express API in Azure Functions

  • Using the Express API might shift you to more ”monolithic” approach, since you are now handling each functions inside Express.js route, or (any other applicable Express.js pattern)
    • Building all logic inside the Express.js might feel good idea, but might create a lot of ”clutter”, whereas using separate API management, or multiple different functions for

Test table

Input binding tests done Local function Runtime Linux Consumption PlanLinux App Service Plan P1V2
Event Hub xx**x
Storage Blob xxx*
HTTP Triggerxxx

* Test actually not done, but since it works on consumption and local function runtime, it should work

** Had generally problems getting EH triggers working regardless the function scenario (custom handlers / Native Functions), wrote some test notes how I got them working

Further test notes

To get Event Hub working on Cloud runtime, I did the following things (Since the Event Hub was not firing)

  • Enabled Always On from the function
  • Copied EventHub Connection String Key & Value pair to App Settings
  • Manually enforced the triggers to be sync
curl "https://functiondedicated.azurewebsites.net/admin/host/synctriggers?code=" -Method Post -Body ""

Deployment technologies in Azure Functions | Microsoft Docs

Example solution for testing

I built quickly a solution to test few interesting things in local Function Runtime

Scenarios tested

  • Having attribute/input validation with Express Patterns, using Express-Validate NPM package helps especially with functions, as they don’t natively provide additional data validation (there obviously differences using strongly typed, or dynamic languages, but even strongly typed languages benefit from granular data validation and input sanitation.
Read more on docs@msft Securing Azure Functions | Microsoft Docs

  • Testing input bindings (Storage and Event Hub) with Express.js as custom handler, I chose to use non-interactive sign-in logs, provided by Azure AD, and handle the within a express route, that matches the function name

HOST.json

In order to use custom handler, and to use standard express response, instead of default functions host expecting response I opted for following setup

  • Important changes are bolded in the below host.json example
{
  "version": "2.0",
  "logging": {
    "applicationInsights": {
      "samplingSettings": {
        "isEnabled": true,
        "excludedTypes": "Request"
      }
    },
    "logLevel": {
      "default": "Information"
    }
  },
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[1.*, 2.0.0)"
  },
  "customHandler": {
    "description": {
      "defaultExecutablePath": "node",
      "defaultWorkerPath": "azexpress-1/app.js"
    },
    "enableForwardingHttpRequest": true
  },
  "extensions": {"http": {"routePrefix": ""}}
}


HTTP Binding and attribute validation via ’express-validator’

  • Input validation test
  • Depending on your API implementation you might need to remove the static route prefix ’API’ by modifying the host.json on Azure Functions HTTP Triggers

Event Hub Binding

  • Responds to function host from Express.js
  • writes logins.json from EH data to Express.js App Root

Performance

Performance was very good judging by stats provided by Live Metrics, and Application Map Feature from Application Insights

  • Since the function host has three different input bindings, I am thinking that maybe its ”kept warm” all the time, thus the results might vary based on bindings

Other references

Upon searching for Express.js and Azure Functions I found following blog Azure Functions Custom Handlers – Bram.us, which was helpful providing answer, that somebody has tested Express.js previously as custom handler (Given way custom handlers work they should work in most scenarios, as they are invoked as separate process inside the function host)

Notes on Security

While I created a separate functions for each Express Route/Function, there is an existing issue open for another use case of using wildcard directives. You can read more about the issue here.

Based on the approach of creating separate function for Each Express handler, I was not able to access the handler for internally callable event based triggers such as storage.

  • Its worth still noting that attacker could gain access to the internal handler route; for example if you have flaw in the middleware design, or path structure – this is akin to ”path traversal” type of attack, but on different attack vector

BR Joosua!

0 comments on “Express.JS middlewares on Azure Functions via custom handlers

Jätä kommentti