Edit

Azure Functions Node.js developer reference

This reference covers how to develop Azure Functions using JavaScript and TypeScript with the @azure/functions npm package. For a general overview of Azure Functions concepts shared across all languages, see the Azure Functions developer reference.

Resource Link
Create your first JavaScript function Visual Studio Code/CLI
Create your first TypeScript function Visual Studio Code/CLI
Scenarios and samples JavaScript/TypeScript
API reference @azure/functions API

Note

This article shows content for a specific programming model version based on the selector at the top of the page. The version you choose should match your @azure/functions npm package version. You can't mix v3 and v4 functions in the same app. If you don't have the package in your package.json, the default is v3.

Programming model

Azure Functions for Node.js supports two programming model versions. New projects should use v4.

Feature v4 (recommended) v3
Status GA GA (maintenance)
@azure/functions package 4.x 3.x
Function registration Code-centric (app.http(), app.timer()) File-based (function.json)
File structure Flexible Fixed (one folder per function)
Functions runtime version 4.25+ 4.x
Node.js versions 24.x, 22.x 24.x, 22.x

In the Node.js v4 programming model, you register functions by importing the app object from @azure/functions and calling trigger-specific methods. The functions are defined directly in your code with a flexible file structure. Every function has a single trigger that starts its execution and can also have bindings, which are declarative connections to other services for reading input data or writing output data. For more information, see Triggers and bindings.

In the v4 model, you:

  • Register functions by using trigger-specific methods like app.http(), app.timer(), and app.storageQueue().
  • Access the trigger input as the first argument to your handler (for example, HttpRequest).
  • Return the primary output directly from the handler function.
  • Use context.extraInputs.get() to read from extra input bindings like Blob Storage.
  • Use context.extraOutputs.set() to write to extra output bindings like queues.
  • Each function has exactly one trigger, but can have multiple extra inputs and outputs.
  • You can cache data in global variables for reuse across invocations, but don't rely on this state to persist. The runtime can recycle your worker at any time.

In the Node.js v3 programming model, you define each function by using a function.json configuration file and corresponding JavaScript or TypeScript code. You organize functions in separate folders with specific file structures. Every function has a single trigger that starts its execution and can also have bindings, which are declarative connections to other services for reading input data or writing output data. For more information, see Triggers and bindings.

In the v3 model, you:

  • Define triggers and bindings in a function.json file. Use direction: "in" for inputs and direction: "out" for outputs.
  • Access the trigger input as the second argument to your handler, or read it from context.bindings.
  • Set outputs by assigning values to context.bindings (for example, context.bindings.outputQueue). For HTTP, use context.res.
  • TypeScript projects require a scriptFile property in function.json that points to the compiled JavaScript file.
  • Each function has exactly one trigger, but can have multiple input and output bindings.
  • You can cache data in global variables for reuse across invocations, but don't rely on this state to persist. The runtime can recycle your worker at any time.

Examples

Here's a simple function that responds to an HTTP request:

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

app.http('httpTrigger', {
    methods: ['GET', 'POST'],
    authLevel: 'anonymous',
    handler: async (request, context) => {
        const name = request.query.get('name') || 'World';
        context.log('HTTP trigger function processed a request.');

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

The following non-HTTP example uses a timer trigger:

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

app.timer('cleanupTimer', {
  schedule: '0 */5 * * * *',
  handler: async (myTimer, context) => {
    context.log('Timer trigger function ran at', new Date().toISOString());
  }
});

The following example shows an HTTP trigger with a queue output binding:

const { app, output } = require('@azure/functions');

const queueOutput = output.storageQueue({
  queueName: 'work-items',
  connection: 'AzureWebJobsStorage'
});

app.http('submitWorkItem', {
  methods: ['POST'],
  extraOutputs: [queueOutput],
  handler: async (request, context) => {
    const body = await request.json();
    context.extraOutputs.set(queueOutput, JSON.stringify(body));
    return { status: 202, jsonBody: { accepted: true } };
  }
});

Here's a simple function that responds to an HTTP request:

{
  "bindings": [
    {
      "authLevel": "anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": ["get", "post"]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ]
}
module.exports = async function (context, req) {
    const name = (req.query.name || (req.body && req.body.name)) || 'World';
    context.log('HTTP trigger function processed a request.');

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

The following non-HTTP example uses a timer trigger:

{
  "bindings": [
    {
      "name": "myTimer",
      "type": "timerTrigger",
      "direction": "in",
      "schedule": "0 */5 * * * *"
    }
  ]
}
module.exports = async function (context, myTimer) {
    context.log('Timer trigger function ran at', new Date().toISOString());
};

The following example shows an HTTP trigger with a queue output binding:

{
  "bindings": [
    {
      "authLevel": "function",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": ["post"]
    },
    {
      "type": "queue",
      "direction": "out",
      "name": "workItems",
      "queueName": "work-items",
      "connection": "AzureWebJobsStorage"
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ]
}
module.exports = async function (context, req) {
    const payload = req.body || {};
    context.bindings.workItems = JSON.stringify(payload);
    context.res = {
        status: 202,
        body: { accepted: true }
    };
};

Building your function app

This section covers the essential components for creating and structuring your Node function app, including the @azure/functions library, project structure, and package management.

The @azure/functions library

The @azure/functions TypeScript/JavaScript library provides the core types and functions you use to interact with the Azure Functions runtime. To see all types and methods available, visit the @azure/functions API.

Your function code can use @azure/functions to:

  • Register functions and define triggers (v4 model).
  • Access strongly-typed trigger input data (for example, HttpRequest, Timer).
  • Create typed output values (such as HttpResponseInit).
  • Interact with runtime-provided context and binding data.

If you're using @azure/functions in your app, include it in your project dependencies:

{
  "dependencies": {
    "@azure/functions": "^4.0.0"
  }
}

Note

The @azure/functions library defines the programming surface for Node.js Azure Functions, but it isn't a general-purpose SDK. Use it specifically for authoring and running functions within the Azure Functions runtime.

TypeScript configuration

For the best TypeScript development experience, ensure your tsconfig.json includes the proper configuration:

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    "outDir": "dist",
    "rootDir": ".",
    "sourceMap": true,
    "strict": false,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}

Folder structure

A JavaScript project requires the folder structure shown in the following example:

<project_root>/
 | - .vscode/
 | - node_modules/
 | - myFirstFunction/
 | | - index.js
 | | - function.json
 | - mySecondFunction/
 | | - index.js
 | | - function.json
 | - .funcignore
 | - host.json
 | - local.settings.json
 | - package.json

The main project folder, <project_root>, can contain the following files:

  • .vscode/: (Optional) Contains the stored Visual Studio Code configuration. To learn more, see Visual Studio Code settings.
  • myFirstFunction/function.json: Contains configuration for the function's trigger, inputs, and outputs. The name of the directory determines the name of your function.
  • myFirstFunction/index.js: Stores your function code. To change this default file path, see using scriptFile.
  • .funcignore: (Optional) Declares files that shouldn't get published to Azure. Usually, this file contains .vscode/ to ignore your editor setting, test/ to ignore test cases, and local.settings.json to prevent local app settings from being published.
  • host.json: Contains configuration options that affect all functions in a function app instance. This file gets published to Azure. Not all options are supported when running locally. To learn more, see host.json.
  • local.settings.json: Used to store app settings and connection strings when running locally. This file doesn't get published to Azure. To learn more, see local.settings.file.
  • package.json: Contains configuration options like a list of package dependencies, the main entry point, and scripts.

A JavaScript project follows the recommended folder structure in the following example:

<project_root>/
 | - .vscode/
 | - node_modules/
 | - src/
 | | - functions/
 | | | - myFirstFunction.js
 | | | - mySecondFunction.js
 | - test/
 | | - functions/
 | | | - myFirstFunction.test.js
 | | | - mySecondFunction.test.js
 | - .funcignore
 | - host.json
 | - local.settings.json
 | - package.json

The main project folder, <project_root>, can contain the following files:

  • .vscode/: (Optional) Contains the stored Visual Studio Code configuration. To learn more, see Visual Studio Code settings.
  • src/functions/: The default location for all functions and their related triggers and bindings.
  • test/: (Optional) Contains the test cases of your function app.
  • .funcignore: (Optional) Declares files that shouldn't get published to Azure. Usually, this file contains .vscode/ to ignore your editor setting, test/ to ignore test cases, and local.settings.json to prevent local app settings from being published.
  • host.json: Contains configuration options that affect all functions in a function app instance. This file gets published to Azure. Not all options are supported when running locally. To learn more, see host.json.
  • local.settings.json: Used to store app settings and connection strings when running locally. This file doesn't get published to Azure. To learn more, see local.settings.file.
  • package.json: Contains configuration options like a list of package dependencies, the main entry point, and scripts.

Package management

Effective package management is crucial for Node.js Azure Functions projects. This section covers dependency management, package configuration, and best practices for maintaining your function app dependencies.

Managing dependencies

All Node.js Azure Functions projects use npm for package management. Your package.json file defines the project configuration, dependencies, and scripts needed to build and run your functions.

Essential package.json structure:

{
  "name": "my-functions-app",
  "version": "1.0.0",
  "description": "Azure Functions Node.js app",
  "main": "src/index.js",
  "scripts": {
    "build": "tsc",
    "watch": "tsc -w",
    "prestart": "npm run build",
    "start": "func start",
    "test": "jest"
  },
  "dependencies": {
    "@azure/functions": "^4.0.0"
  },
  "devDependencies": {
    "@azure/functions-core-tools": "^4.0.4670",
    "@types/node": "^18.0.0",
    "typescript": "^4.0.0",
    "jest": "^29.0.0"
  }
}

Runtime vs. development dependencies

Separate your dependencies appropriately:

Runtime dependencies (dependencies):

  • @azure/functions: The core Azure Functions library
  • Business logic libraries (lodash, axios, and similar packages)
  • Database drivers (mongodb, mssql, and similar packages)
  • Azure SDK packages (@azure/storage-blob, @azure/cosmos, and similar packages)

Development dependencies (devDependencies):

  • TypeScript compiler and type definitions
  • Testing frameworks (Jest, Mocha)
  • Build tools and linters
  • Azure Functions Core Tools (for local development)

TypeScript-specific packages

For TypeScript projects, include these essential development dependencies:

{
  "devDependencies": {
    "@types/node": "^18.0.0",
    "typescript": "^4.0.0",
    "@typescript-eslint/eslint-plugin": "^5.0.0",
    "@typescript-eslint/parser": "^5.0.0"
  }
}

Security and updates

Regularly update your dependencies to address security vulnerabilities:

# Check for outdated packages
npm outdated

# Update packages
npm update

# Audit for security issues
npm audit
npm audit fix

Running and debugging

This section covers local development, debugging techniques, and testing strategies for Node.js Azure Functions.

Local development setup

Prerequisites:

Setup steps:

  1. Install dependencies:

    npm install
    
  2. Build TypeScript projects:

    npm run build
    
  3. Start the local runtime:

    npm start
    # or directly:
    func start
    

Environment configuration

Configure your local development environment by using local.settings.json:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "node",
    "NODE_ENV": "development",
    "CUSTOM_ENV_VARIABLE": "local-value"
  },
  "Host": {
    "LocalHttpPort": 7071,
    "CORS": "*",
    "CORSCredentials": false
  }
}

Debugging

Visual Studio Code debugging:

Create .vscode/launch.json:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Attach to Node Functions",
      "type": "node",
      "request": "attach",
      "port": 9229,
      "preLaunchTask": "func: host start"
    }
  ]
}

Create .vscode/tasks.json:

{
  "version": "2.0.0",
  "tasks": [
    {
      "type": "func",
      "label": "func: host start",
      "command": "host start",
      "problemMatcher": "$func-node-watch",
      "isBackground": true,
      "options": {
        "cwd": "${workspaceFolder}"
      }
    }
  ]
}

Command line debugging:

# Start with debugging enabled
func start --p <port>

# For TypeScript, ensure you build first
npm run build
func start --p 9229

Deployment

This section covers deployment strategies, CI/CD integration, and production best practices for Node.js Azure Functions.

Deployment methods

1. Visual Studio Code deployment:

2. Azure Functions Core Tools:

# Deploy to Azure
func azure functionapp publish <FunctionAppName>

# Deploy with custom settings
func azure functionapp publish <FunctionAppName> --build local --publish-local-settings

3. Azure CLI deployment:

# Deploy from local folder
az functionapp deployment source config-zip \
  --resource-group <ResourceGroupName> \
  --name <FunctionAppName> \
  --src <PathToZipFile>

Production configuration

Application settings in Azure:

Configure environment variables for production:

  • WEBSITE_NODE_DEFAULT_VERSION: Set to ~18 or ~20.
  • FUNCTIONS_WORKER_RUNTIME: Set to node.
  • Connection strings and API keys as secure app settings.
  • NODE_ENV: Set to production.

Triggers and bindings

Azure Functions uses triggers to start function execution and bindings to connect your code to other services like storage, queues, and databases. In the Node.js programming model, you declare bindings differently depending on your model version.

Two main types of bindings exist:

  • Triggers (input that starts the function)
  • Inputs and outputs (extra data sources or destinations)

For more information about the available triggers and bindings, see Triggers and Bindings in Azure Functions.

Example: Timer Trigger with Blob Input

This function triggers every 10 minutes, reads from a Blob using extra inputs, and logs the blob content.

const { app, input } = require('@azure/functions');

let CACHED_BLOB_DATA = null;

const blobInput = input.storageBlob({
    connection: 'BLOB_CONNECTION_SETTING',
    path: 'mycontainer/myblob.txt'
});

app.timer('TimerTriggerWithBlob', {
    schedule: '0 */10 * * * *',
    extraInputs: [blobInput],
    handler: async (myTimer, context) => {
        if (CACHED_BLOB_DATA === null) {
            // Read blob content and cache it
            CACHED_BLOB_DATA = context.extraInputs.get(blobInput);
            context.log(`Blob content cached: ${CACHED_BLOB_DATA?.substring(0, 100)}...`);
        }

        context.log(`Timer function executed at: ${new Date().toISOString()}`);
        context.log(`Using cached data of length: ${CACHED_BLOB_DATA?.length || 0}`);
    }
});

This function triggers every 10 minutes, reads from a Blob by using bindings configuration, and logs the blob content.

{
  "scriptFile": "index.js",
  "bindings": [
    {
      "name": "myTimer",
      "type": "timerTrigger",
      "direction": "in",
      "schedule": "0 */10 * * * *"
    },
    {
      "name": "blobInput",
      "type": "blob",
      "direction": "in",
      "path": "mycontainer/myblob.txt",
      "connection": "AzureWebJobsStorage"
    }
  ]
}
let CACHED_BLOB_DATA = null;

module.exports = async function (context, myTimer) {
    if (CACHED_BLOB_DATA === null) {
        // Read blob content and cache it
        CACHED_BLOB_DATA = context.bindings.blobInput;
        context.log(`Blob content cached: ${CACHED_BLOB_DATA?.substring(0, 100)}...`);
    }

    context.log(`Timer function executed at: ${new Date().toISOString()}`);
    context.log(`Using cached data of length: ${CACHED_BLOB_DATA?.length || 0}`);
};

Example: HTTP Trigger with Queue Output

This function triggers on an HTTP request, writes a message to a storage queue, and returns an HTTP response.

const { app, output } = require('@azure/functions');

const queueOutput = output.storageQueue({
    connection: 'AzureWebJobsStorage',
    queueName: 'myqueue'
});

app.http('httpTriggerWithQueue', {
    methods: ['GET', 'POST'],
    extraOutputs: [queueOutput],
    handler: async (request, context) => {
        const name = request.query.get('name') || 'World';
        const message = {
            id: context.invocationId,
            name: name,
            timestamp: new Date().toISOString()
        };

        // Write to queue output
        context.extraOutputs.set(queueOutput, JSON.stringify(message));
        context.log(`Message sent to queue: ${JSON.stringify(message)}`);

        return {
            body: `Hello, ${name}! Message queued successfully.`
        };
    }
});

This function triggers on an HTTP request, writes a message to a storage queue, and returns an HTTP response.

{
  "scriptFile": "index.js",
  "bindings": [
    {
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": ["get", "post"]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    },
    {
      "type": "queue",
      "direction": "out",
      "name": "outputQueue",
      "queueName": "myqueue",
      "connection": "AzureWebJobsStorage"
    }
  ]
}
module.exports = async function (context, req) {
    const name = (req.query.name || (req.body && req.body.name)) || 'World';
    const message = {
        id: context.invocationId,
        name: name,
        timestamp: new Date().toISOString()
    };

    // Write to queue output
    context.bindings.outputQueue = JSON.stringify(message);
    context.log(`Message sent to queue: ${JSON.stringify(message)}`);

    return {
        status: 200,
        body: `Hello, ${name}! Message queued successfully.`
    };
};

The app, trigger, input, and output objects exported by the @azure/functions module provide type-specific methods for most types. For all the types that aren't supported, a generic method is provided to allow you to manually specify the configuration. The generic method can also be used if you want to change the default settings provided by a type-specific method.

The following example is a simple HTTP triggered function using generic methods instead of type-specific methods.

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

app.generic("helloWorld1", {
  trigger: trigger.generic({
    type: "httpTrigger",
    methods: ["GET", "POST"],
  }),
  return: output.generic({
    type: "http",
  }),
  handler: async (request, context) => {
    context.log(`Http function processed request for url "${request.url}"`);

    return { body: `Hello, world!` };
  },
});

::: zone-end

Invocation context

Each invocation of your function receives an invocation context object. Use this object to read inputs, set outputs, write to logs, and access various metadata. In the v3 model, you always pass the context object as the first argument to your handler.

The context object includes the following properties:

Property Description
invocationId The ID of the current function invocation.
executionContext See execution context.
bindings See bindings.
bindingData Metadata about the trigger input for this invocation, excluding the value itself. For example, an event hub trigger has an enqueuedTimeUtc property.
traceContext The context for distributed tracing. For more information, see Trace Context.
bindingDefinitions The configuration of your inputs and outputs, as defined in function.json.
req See HTTP request.
res See HTTP response.

context.executionContext

The context.executionContext object has the following properties:

Property Description
invocationId The ID of the current function invocation.
functionName The name of the function that you're invoking. The name of the folder containing the function.json file determines the name of the function.
functionDirectory The folder containing the function.json file.
retryContext See retry context.

context.executionContext.retryContext

The context.executionContext.retryContext object has the following properties:

Property Description
retryCount A number representing the current retry attempt.
maxRetryCount Maximum number of times an execution is retried. A value of -1 means to retry indefinitely.
exception Exception that caused the retry.

context.bindings

Use the context.bindings object to read inputs or set outputs. The following example is a storage queue trigger that uses context.bindings to copy a storage blob input to a storage blob output. The queue message's content replaces {queueTrigger} as the file name to be copied, with the help of a binding expression.

{
    "name": "myQueueItem",
    "type": "queueTrigger",
    "direction": "in",
    "connection": "storage_APPSETTING",
    "queueName": "helloworldqueue"
},
{
    "name": "myInput",
    "type": "blob",
    "direction": "in",
    "connection": "storage_APPSETTING",
    "path": "helloworld/{queueTrigger}"
},
{
    "name": "myOutput",
    "type": "blob",
    "direction": "out",
    "connection": "storage_APPSETTING",
    "path": "helloworld/{queueTrigger}-copy"
}
module.exports = async function (context, myQueueItem) {
  const blobValue = context.bindings.myInput;
  context.bindings.myOutput = blobValue;
};

context.done

The context.done method is deprecated. Before Azure Functions supported async functions, you signaled your function was done by calling context.done():

module.exports = function (context, request) {
  context.log("this pattern is now deprecated");
  context.done();
};

Remove the call to context.done(). Mark your function as async so that it returns a promise (even if you don't await anything). As soon as your function finishes (in other words, the returned promise resolves), the v3 model knows your function is done.

module.exports = async function (context, request) {
  context.log("you don't need context.done or an awaited call");
};

Each invocation of your function receives an invocation context object. This object contains information about your invocation and methods for logging. In the v4 model, you typically pass the context object as the second argument to your handler.

The InvocationContext class includes the following properties:

Property Description
invocationId The ID of the current function invocation.
functionName The name of the function.
extraInputs Used to get the values of extra inputs. For more information, see extra inputs and outputs.
extraOutputs Used to set the values of extra outputs. For more information, see extra inputs and outputs.
retryContext See retry context.
traceContext The context for distributed tracing. For more information, see Trace Context.
triggerMetadata Metadata about the trigger input for this invocation, not including the value itself. For example, an event hub trigger has an enqueuedTimeUtc property.
options The options used when registering the function, after they're validated and defaults are explicitly specified.

Retry context

The retryContext object has the following properties:

Property Description