Azure Functions: Node.js v4 programming model is Generally Available
Published Sep 27 2023 09:31 AM 12K Views

The Azure Functions team is thrilled to announce General Availability of version 4 of the Node.js programming model! This programming model is part of Azure Functions’ larger effort to provide a more flexible and intuitive experience for all supported languages. You may be aware that we announced General Availability of the new Python programming model for Azure Functions at MS Build this year. The new Node.js experience we ship today is a result of the valuable feedback we received from JavaScript and TypeScript developers through GitHub, surveys, user studies, as well as suggestions from internal Node.js experts working closely with customers. 

 

This blog post aims to highlight the key features of the v4 model and also shed light on the improvements we’ve made since announcing public preview last spring. 

 

What’s improved in the V4 model? 

 

In this section, we highlight several key improvements made in the V4 programming model. 

 

Flexible folder structure  

 

The existing V3 model requires that each trigger be in its own directory, with its own function.json file. This strict structure can make it hard to manage if an app has many triggers. And if you’re a Durable Functions user, having your orchestration, activity, and client functions in different directories decreases code readability, because you must switch between directories to look at the components of one logical unit. The V4 model removes the strict directory structure and gives users the flexibility to organize triggers in ways that makes sense to their Function app. For example, you can have multiple related triggers in one file or have triggers in separate files that are grouped in one directory. 

 

Furthermore, you no longer need to keep a function.json file for each trigger you have in the V4 model as bindings are configured in code! See the HTTP example in the next section and the Durable Functions example in the “More Examples” section. 

 

Define function in code 

 

The V4 model uses an app object as the entry point for registering functions instead of function.json files. For example, to register an HTTP trigger responding to a GET request, you can call app.http() or app.get() which was modeled after other Node.js frameworks like Express.js that also support app.get(). The following shows what has changed when writing an HTTP trigger in the V4 model: 

 

File Type

V3 

V4 

 JavaScript
module.exports = async function (context, req) { 
  context.log('HTTP function processed a request'); 

  const name = req.query.name 
    || req.body 
    || 'world'; 

  context.res = { 
    body: `Hello, ${name}!` 
  }; 
}; 

 

const { app } = require("@azure/functions"); 

app.http('helloWorld1', { 
  methods: ['GET', 'POST'], 
  handler: async (request, context) => { 
    context.log('Http function processed request'); 

    const name = request.query.get('name')  
      || await request.text()  
      || 'world'; 

    return { body: `Hello, ${name}!` }; 
  } 
}); 

 

JSON
{ 
  "bindings": [ 
    { 
      "authLevel": "anonymous", 
      "type": "httpTrigger", 
      "direction": "in", 
      "name": "req", 
      "methods": [ 
        "get", 
        "post" 
      ] 
    }, 
    { 
      "type": "http", 
      "direction": "out", 
      "name": "res" 
    } 
  ] 
} 
madhurabharadwaj_0-1694818755219.gif  Nothing  madhurabharadwaj_1-1694818755223.gif

 

 

 

Trigger configuration like methods and authLevel that were specified in a function.json file before are moved to the code itself in V4. We also set several defaults for you, which is why you don't see authLevel or an output binding in the V4 example. 

 

New HTTP Types 

 

In the V4 model, we’ve adjusted the HTTP request and response types to be a subset of the fetch standard instead of types unique to Azure Functions. We use Node.js's undici package, which follows the fetch standard and is currently being integrated into Node.js core. 

 

HttpRequest - body 

V3 

V4 

// returns a string, object, or Buffer 
const body = request.body; 
// returns a string 
const body = request.rawBody; 
// returns a Buffer 
const body = request.bufferBody; 
// returns an object representing a form 
const body = await request.parseFormBody();

 

 

const body = await request.text(); 
const body = await request.json(); 
const body = await request.formData(); 
const body = await request.arrayBuffer(); 
const body = await request.blob();

 

HttpResponse – status 

V3 

V4 

 

context.res.status(200); 

context.res = { status: 200} 
context.res = { statusCode: 200 }; 

return { status: 200}; 
return { statusCode: 200 }; 
return { status: 200 }; 

 

To see how other properties like header, query parameters, etc. have changed, see our developer guide. 

 

Better IntelliSense 

 

If you're not familiar with IntelliSense, it covers the features in your editor like autocomplete and documentation directly while you code. We're big fans of IntelliSense and we hope you are too because it was a priority for us from the initial design stages. The V4 model supports IntelliSense for JavaScript for the first time, and improves on the IntelliSense for TypeScript that already existed in V3. Here are a few examples: 

 

madhurabharadwaj_2-1694818755225.png

 

madhurabharadwaj_3-1694818755228.png

 

More Examples 

 

NOTE: One of the priorities of the V4 programming model is to ensure parity between JavaScript and TypeScript support. You can use either language to write all the examples in this article, but we only show one language for the sake of article length. 

 

Timer (TypeScript) 

 

A timer trigger that runs every 5 minutes: 

 

 

import { app, InvocationContext, Timer } from '@azure/functions'; 

export async function timerTrigger1(myTimer: Timer, context: InvocationContext): Promise<void> { 
    context.log('Timer function processed request.'); 
} 

app.timer('timerTrigger1', { 
    schedule: '0 */5 * * * *', 
    handler: timerTrigger1, 
}); 

 

 

Durable Functions (TypeScript) 

 

Like in the V3 model, you need the durable-functions package in addition to @azure/functions to write Durable Functions in the V4 model. The example below shows one of the common patterns Durable Functions is useful for – function chaining. In this case, we’re executing a sequence of (simple) functions in a particular order. 

 

 

import { app, HttpHandler, HttpRequest, HttpResponse, InvocationContext } from '@azure/functions'; 
import * as df from 'durable-functions'; 
import { ActivityHandler, OrchestrationContext, OrchestrationHandler } from 'durable-functions'; 

// Replace with the name of your Durable Functions Activity 
const activityName = 'hello'; 

const orchestrator: OrchestrationHandler = function* (context: OrchestrationContext) { 
    const outputs = []; 
    outputs.push(yield context.df.callActivity(activityName, 'Tokyo')); 
    outputs.push(yield context.df.callActivity(activityName, 'Seattle')); 
    outputs.push(yield context.df.callActivity(activityName, 'Cairo')); 

    return outputs; 
}; 
df.app.orchestration('durableOrchestrator1', orchestrator); 

const helloActivity: ActivityHandler = (input: string): string => { 
    return `Hello, ${input}`; 
}; 
df.app.activity(activityName, { handler: helloActivity }); 

const httpStart: HttpHandler = async (request: HttpRequest, context: InvocationContext): Promise<HttpResponse> => { 
    const client = df.getClient(context); 
    const body: unknown = await request.text(); 
    const instanceId: string = await client.startNew(request.params.orchestratorName, { input: body }); 

    context.log(`Started orchestration with ID = '${instanceId}'.`); 

    return client.createCheckStatusResponse(request, instanceId); 
}; 

app.http('durableOrchestrationStart1', { 
    route: 'orchestrators/{orchestratorName}', 
    extraInputs: [df.input.durableClient()], 
    handler: httpStart, 
}); 

 

 

In Lines 8-16, we set up and register an orchestration function. In the V4 model, instead of registering the orchestration trigger in function.json, you simply do it through the app object on the durable-functions module (here df). Similar logic applies to the activity (Lines 18-21), client (Lines 23-37), and Entity functions. This means you no longer have to manage multiple function.json files just to get a simple Durable Functions app working!  

 

Lines 23-37 set up and register a client function to start the orchestration. To do that, we pass in an input object from the durable-functions module to the extraInputs array to register the function. Like in the V3 model, we obtain the Durable Client using df.getClient() to execute orchestration management operations like starting a new orchestration. We use an HTTP trigger in this example, but you could use any trigger supported by Azure Functions such as a timer trigger or Service Bus trigger. 

 

Refer to this example to see how to write a Durable Entity with the V4 model. 

 

What’s new for GA?  

 

We made the following improvements to the v4 programming model since the announcement of Public Preview last spring. Most of these improvements were made to ensure full feature parity between the existing v3 and the new v4 programming model. 

 

  1. AzureWebJobsFeatureFlags no longer needs to be set 
    During preview, you needed to set the application setting "AzureWebJobsFeatureFlags" to "EnableWorkerIndexing" to get a v4 model app working. We removed this requirement as part of the General Availability update. This also allows you to use Azure Static Web Apps with the v4 model. You must be on runtime v4.25+ in Azure or core tools v4.0.5382+ if running locally to benefit from this change.
      
  2. Model v4 is now the default

    We're confident v4 is ready for you to use everywhere, and it's now the default version on npm, in documentation, and when creating new apps in Azure Functions Core Tools or VS Code.

      
  3. Entry point errors are now exposed via Application Insights
    In the v3 model and in the preview version of the v4 model, errors in entry point files were ignored and weren't logged in Application Insights. We changed the behavior to make entry point errors more obvious. It's a breaking change for model v4 as some errors that were previously ignored will now block your app from running. You can use the app setting "FUNCTIONS_NODE_BLOCK_ON_ENTRY_POINT_ERROR" to configure this behavior. We highly recommend setting it to "true" for all v4 apps. For more information, see the App Setting reference documentation.

  4. Support for retry policy 

    We added support for configuring retry policy when registering a function in the v4 model. The retry policy tells the runtime to rerun a failed execution until either successful completion occurs or the maximum number of retries is reached. A retry policy is evaluated when a Timer, Kafka, CosmosDB or Event Hubs-triggered function raises an uncaught exception. As a best practice, you should catch all exceptions in your code and rethrow any errors that you want to result in a retry. Learn more about Azure Functions Retry policy.
     

  5. Support for Application Insights npm package
    Add the Application Insights npm package (v2.8.0+) to your app to discover and rapidly diagnose performance and other issues. This package tracks the following out-of-the-box: incoming and outgoing HTTP requests, important system metrics such as CPU usage, unhandled exceptions, and events from many popular third-party libraries.

  6. Support for more binding types
    We added support for SQL and Table input and output bindings. We also added Cosmos DB extension v4 types. A highlight of the latest Cosmos DB extension is that it allows you to use managed identities instead of secrets. Learn how to upgrade your Cosmos DB extension here and how to configure an app to use identities here 

  7. Support for hot reload

    Hot reload ensures your app will automatically restart when a file in your app is changed. This was not working for model v4 when we announced preview, but has been fixed for GA. 

 

How to get started?

Check out our Quickstarts to get started: 

See our Developer Guide to learn more about the V4 model. We’ve also created an upgrade guide to help migrate existing V3 apps to V4. 

 

Please give the V4 model a try and let us know your thoughts by reacting or commenting on our GA discussion thread on GitHub!

2 Comments
Version history
Last update:
‎Sep 27 2023 04:09 PM
Updated by: