How to deploy Node.js app to Azure Functions
Azure Functions provides serverless compute service for running Node.js code triggered by HTTP requests, timers, or Azure service events without infrastructure management. As the creator of CoreUI, a widely used open-source UI library, I’ve deployed serverless Node.js applications to Azure throughout my 11 years of backend development. The most efficient approach is using Azure Functions Core Tools for local development and Azure CLI or VS Code extension for deployment. This method enables event-driven serverless architecture, seamless Azure service integration, and consumption-based pricing with automatic scaling.
Install Azure Functions Core Tools, create HTTP trigger function, and deploy using Azure CLI for serverless execution.
# Install Azure Functions Core Tools
npm install -g azure-functions-core-tools@4
# Create new function app
func init my-function-app --worker-runtime node --language javascript
cd my-function-app
# Create HTTP trigger function
func new --template "HTTP trigger" --name api
Generated function structure:
// api/index.js - HTTP trigger function
module.exports = async function (context, req) {
context.log('JavaScript 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.`
: '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,
body: responseMessage
}
}
Function configuration:
// api/function.json
{
"bindings": [
{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": ["get", "post"]
},
{
"type": "http",
"direction": "out",
"name": "res"
}
]
}
More complex API example:
// api/index.js - RESTful API
const users = [
{ id: '1', name: 'Alice', email: '[email protected]' },
{ id: '2', name: 'Bob', email: '[email protected]' }
]
module.exports = async function (context, req) {
context.log(`HTTP trigger: ${req.method} ${req.url}`)
// CORS headers
context.res = {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',
'Access-Control-Allow-Headers': 'Content-Type'
}
}
// Handle preflight
if (req.method === 'OPTIONS') {
context.res.status = 204
return
}
try {
// Route handling
const path = req.params.path || ''
const method = req.method
if (path === '' && method === 'GET') {
// GET /api
context.res.status = 200
context.res.body = { message: 'API is running' }
} else if (path === 'users' && method === 'GET') {
// GET /api/users
context.res.status = 200
context.res.body = { users }
} else if (path.startsWith('users/') && method === 'GET') {
// GET /api/users/:id
const id = path.split('/')[1]
const user = users.find(u => u.id === id)
if (user) {
context.res.status = 200
context.res.body = { user }
} else {
context.res.status = 404
context.res.body = { error: 'User not found' }
}
} else if (path === 'users' && method === 'POST') {
// POST /api/users
const { name, email } = req.body
const newUser = {
id: String(users.length + 1),
name,
email
}
users.push(newUser)
context.res.status = 201
context.res.body = { user: newUser }
} else {
context.res.status = 404
context.res.body = { error: 'Not found' }
}
} catch (error) {
context.log.error('Error:', error)
context.res.status = 500
context.res.body = { error: 'Internal server error' }
}
}
Timer trigger function:
// scheduled-task/index.js
module.exports = async function (context, myTimer) {
const timeStamp = new Date().toISOString()
if (myTimer.isPastDue) {
context.log('Timer function is running late!')
}
context.log('Timer trigger function ran at:', timeStamp)
// Perform scheduled task
try {
await performCleanup()
context.log('Cleanup completed successfully')
} catch (error) {
context.log.error('Cleanup failed:', error)
throw error
}
}
async function performCleanup() {
// Cleanup logic
console.log('Performing cleanup...')
return new Promise(resolve => setTimeout(resolve, 1000))
}
Timer configuration:
// scheduled-task/function.json
{
"bindings": [
{
"name": "myTimer",
"type": "timerTrigger",
"direction": "in",
"schedule": "0 */5 * * * *"
}
]
}
Local development:
# Start function app locally
func start
# Function will be available at:
# http://localhost:7071/api/api
# Test with curl
curl http://localhost:7071/api/api
curl http://localhost:7071/api/api?name=John
curl -X POST http://localhost:7071/api/api/users \
-H "Content-Type: application/json" \
-d '{"name":"Charlie","email":"[email protected]"}'
Application configuration:
// local.settings.json - Local development settings
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "node",
"NODE_ENV": "development",
"DATABASE_URL": "your-database-url",
"API_KEY": "your-api-key"
},
"Host": {
"CORS": "*"
}
}
Deployment with Azure CLI:
# Install Azure CLI
# Visit: https://docs.microsoft.com/en-us/cli/azure/install-azure-cli
# Login to Azure
az login
# Create resource group
az group create \
--name my-functions-rg \
--location eastus
# Create storage account
az storage account create \
--name myfunctionsstorage \
--resource-group my-functions-rg \
--location eastus \
--sku Standard_LRS
# Create function app
az functionapp create \
--name my-function-app \
--resource-group my-functions-rg \
--storage-account myfunctionsstorage \
--consumption-plan-location eastus \
--runtime node \
--runtime-version 20 \
--functions-version 4
# Deploy function app
func azure functionapp publish my-function-app
# Set environment variables
az functionapp config appsettings set \
--name my-function-app \
--resource-group my-functions-rg \
--settings \
NODE_ENV=production \
DATABASE_URL=your-production-db-url
# View logs
func azure functionapp logstream my-function-app
Using VS Code extension:
# Install Azure Functions extension for VS Code
# Extension ID: ms-azuretools.vscode-azurefunctions
# Steps:
# 1. Open VS Code
# 2. Install Azure Functions extension
# 3. Sign in to Azure
# 4. Click "Deploy to Function App" button
# 5. Select or create Function App
# 6. Extension deploys automatically
Production configuration:
// host.json - Function app configuration
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"maxTelemetryItemsPerSecond": 20
}
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.*, 5.0.0)"
},
"functionTimeout": "00:05:00"
}
Package configuration:
{
"name": "my-function-app",
"version": "1.0.0",
"scripts": {
"start": "func start",
"test": "jest"
},
"dependencies": {
"@azure/functions": "^4.0.0"
},
"devDependencies": {
"azure-functions-core-tools": "^4.0.0"
}
}
Monitoring and management:
# View function status
az functionapp show \
--name my-function-app \
--resource-group my-functions-rg
# Get function URL
az functionapp function show \
--name my-function-app \
--resource-group my-functions-rg \
--function-name api
# Restart function app
az functionapp restart \
--name my-function-app \
--resource-group my-functions-rg
# Delete function app
az functionapp delete \
--name my-function-app \
--resource-group my-functions-rg
# View Application Insights data
az monitor app-insights component show \
--app my-function-app \
--resource-group my-functions-rg
Here the Azure Functions Core Tools provide local development environment matching Azure runtime. The function.json defines trigger bindings, authentication level, and HTTP methods. The context object provides logging, input bindings, and response output. Timer triggers use cron expressions for scheduled execution. The local.settings.json stores environment variables for local development. Azure CLI commands create resource groups, storage accounts, and function apps for deployment. Application Insights integration enables production monitoring and diagnostics.
Best Practice Note:
This is the serverless deployment approach we use for CoreUI Azure-hosted microservices requiring seamless integration with Azure ecosystem services. Use Application Insights for comprehensive monitoring and distributed tracing, implement Durable Functions for complex stateful workflows, leverage Azure Key Vault for secure secret management instead of environment variables, and configure custom domains with Azure Front Door for production-grade API endpoints with global distribution.



