Introduction to Azure Functions with Node.js
Azure Functions is a serverless compute service that lets you run event-triggered code without explicitly provisioning or managing infrastructure. With Node.js, you can build powerful, scalable applications that respond to a wide range of events, from HTTP requests to database changes and scheduled timers.
This guide will walk you through the essentials of developing Azure Functions using Node.js, covering everything from setup to deployment and best practices.
Getting Started
Prerequisites
Before you begin, ensure you have the following installed on your development machine:
- Node.js: Download and install the latest LTS version from nodejs.org.
- Azure Functions Core Tools: Install globally using npm:
npm install -g azure-functions-core-tools@3 --unsafe-perm true
- Azure CLI (Optional but Recommended): For easier deployment and management of Azure resources. Install from Microsoft Docs.
Installation and Setup
Once you have the prerequisites, you can create your first Azure Functions project.
- Create a new directory for your project and navigate into it:
mkdir MyAzureFunctionsProject cd MyAzureFunctionsProject
- Initialize a new Azure Functions project:
This command creates a basic project structure with essential configuration files likefunc init --worker-runtime node
local.settings.json
andhost.json
.
Creating Your First Functions
Functions are triggered by specific events. The Azure Functions runtime manages these triggers for you.
HTTP Triggers
HTTP triggers allow your functions to be invoked via HTTP requests. They are commonly used for building APIs.
To create an HTTP-triggered function:
func new --template "HTTP trigger" --name MyHttpTrigger
This creates a new folder MyHttpTrigger
containing index.js
and function.json
. The index.js
file will contain your Node.js code:
// MyHttpTrigger/index.js
module.exports = async function (context, req) {
context.log('HTTP trigger function processed a request.');
const name = (req.query.name || (req.body && req.body.name));
const responseMessage = name
? 'Hello, ' + name + '!'
: 'This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.';
context.res = {
// status: 200, /* Defaults to 200 */
body: responseMessage
};
};
The function.json
file defines the trigger and its configuration:
// MyHttpTrigger/function.json
{
"bindings": [
{
"authLevel": "function",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [
"get",
"post"
]
},
{
"type": "http",
"direction": "out",
"name": "res"
}
]
}
func start
. This will provide you with a local URL to test your HTTP trigger.
Timer Triggers
Timer triggers execute your function on a schedule defined by a CRON expression. This is useful for background tasks.
Create a timer trigger:
func new --template "Timer trigger" --name MyTimerTrigger
Example index.js
:
// MyTimerTrigger/index.js
module.exports = async function (context, myTimer) {
const timeStamp = new Date().toISOString();
if (myTimer.isPastDue) {
context.log('JavaScript is running late!');
}
context.log('JavaScript timer trigger function executed at', timeStamp);
};
The CRON schedule is defined in function.json
:
// MyTimerTrigger/function.json
{
"scriptFile": "index.js",
"bindings": [
{
"name": "myTimer",
"type": "timerTrigger",
"direction": "in",
"schedule": "0 */5 * * * *" // Runs every 5 minutes
}
]
}
Queue Triggers
Queue triggers execute your function when a message is added to a specified Azure Storage Queue.
Create a queue trigger:
func new --template "Azure Queue Storage trigger" --name MyQueueTrigger
Example index.js
:
// MyQueueTrigger/index.js
module.exports = async function (context, myQueueItem) {
context.log('JavaScript queue trigger function processed work item', myQueueItem);
context.log('Queue item content: ', myQueueItem);
};
The queue connection string and name are configured in function.json
and local.settings.json
:
// MyQueueTrigger/function.json
{
"scriptFile": "index.js",
"bindings": [
{
"name": "myQueueItem",
"type": "queueTrigger",
"direction": "in",
"queueName": "myqueue-items",
"connection": "AzureWebJobsStorage"
}
]
}
AzureWebJobsStorage
connection string in local.settings.json
points to a valid Azure Storage account connection string.
Bindings: Connecting to Other Services
Bindings simplify integration with other Azure services and external data sources by abstracting away the details of the SDKs.
Input Bindings
Input bindings allow you to read data from a service and make it available as parameters in your function.
Example: Retrieving a document from a Cosmos DB collection.
// In function.json
{
"bindings": [
// ... other bindings
{
"name": "inputDocument",
"type": "cosmosDB",
"direction": "in",
"databaseName": "Tasks",
"collectionName": "Items",
"id": "{queueTrigger}", // Example: get ID from a queue trigger
"connectionStringSetting": "CosmosDbConnectionString"
}
]
}
// In index.js
module.exports = async function (context) {
const document = context.bindings.inputDocument;
context.log('Retrieved document:', document);
};
Output Bindings
Output bindings enable your function to write data to a service.
Example: Sending a message to an Azure Service Bus queue.
// In function.json
{
"bindings": [
// ... other bindings
{
"name": "outputSbMessage",
"type": "serviceBus",
"direction": "out",
"queueName": "output-queue",
"connection": "ServiceBusConnection"
}
]
}
// In index.js
module.exports = async function (context) {
context.bindings.outputSbMessage = { body: "Hello from Azure Functions!" };
context.log('Message sent to Service Bus.');
};
Development Tools
Leverage these tools to streamline your Node.js Azure Functions development:
- Azure Functions Core Tools: Essential for local development, debugging, and testing.
- Visual Studio Code: With the Azure Functions extension, you get IntelliSense, debugging capabilities, and easy deployment directly from VS Code.
- NPM: Use npm to manage your project's dependencies.
Deployment
Deploying your Azure Functions can be done through several methods:
- Azure Functions Core Tools: Use the
func azure functionapp publish <YourFunctionAppName>
command for direct deployment. - Azure CLI: The
az functionapp create
andaz functionapp deployment source config-zip
commands offer robust deployment options. - CI/CD Pipelines: Integrate Azure Functions deployment into Azure DevOps, GitHub Actions, or other CI/CD platforms for automated deployments.
- VS Code Extension: Deploy directly from the Azure Functions extension in Visual Studio Code.
Monitoring and Debugging
Effective monitoring and debugging are crucial for maintaining healthy functions.
- Local Debugging: Set breakpoints in your code using VS Code and step through execution while running locally with
func start
. - Application Insights: Integrate Application Insights with your Azure Functions to monitor performance, track exceptions, and analyze logs.
- Log Streaming: Use the Azure Portal or Azure CLI to stream logs in real-time from your deployed functions.
Best Practices
- Keep Functions Small and Focused: Each function should perform a single task.
- Manage Dependencies Efficiently: Use
package.json
to manage your Node.js dependencies. - Handle Errors Gracefully: Implement robust error handling and logging.
- Optimize for Cold Starts: Minimize the size of your deployment package and reduce external dependencies to improve startup performance.
- Secure Your Functions: Use appropriate authorization levels (e.g., function, admin) for HTTP triggers.
- Leverage Bindings: Use bindings to simplify integration and reduce boilerplate code.