Back to Blog
3 min read

Azure Functions HTTP APIs: Building Serverless Backends

Azure Functions provides a simple way to build HTTP APIs without managing infrastructure. Combined with Azure API Management or used standalone, you can create powerful serverless backends.

Project Structure

my-api/
├── GetProducts/
│   ├── function.json
│   └── index.js
├── CreateOrder/
│   ├── function.json
│   └── index.js
├── host.json
├── local.settings.json
└── package.json

HTTP Trigger Function

// GetProducts/index.js
module.exports = async function (context, req) {
    const products = [
        { id: 1, name: "Widget", price: 9.99 },
        { id: 2, name: "Gadget", price: 19.99 }
    ];

    context.res = {
        status: 200,
        headers: { "Content-Type": "application/json" },
        body: products
    };
};
// GetProducts/function.json
{
    "bindings": [
        {
            "authLevel": "function",
            "type": "httpTrigger",
            "direction": "in",
            "name": "req",
            "methods": ["get"],
            "route": "products"
        },
        {
            "type": "http",
            "direction": "out",
            "name": "res"
        }
    ]
}

Host Configuration

// host.json
{
    "version": "2.0",
    "extensions": {
        "http": {
            "routePrefix": "api",
            "maxOutstandingRequests": 200,
            "maxConcurrentRequests": 100
        }
    },
    "logging": {
        "applicationInsights": {
            "samplingSettings": {
                "isEnabled": true,
                "maxTelemetryItemsPerSecond": 20
            }
        }
    }
}

Authentication Options

Function Keys

// Calling with function key
const response = await fetch('https://myapi.azurewebsites.net/api/products?code=<function-key>');

Azure AD Authentication

// Configure in Azure Portal
// Authentication / Authorization settings

// Client code
const token = await getAccessToken();
const response = await fetch('https://myapi.azurewebsites.net/api/products', {
    headers: {
        'Authorization': `Bearer ${token}`
    }
});

Proxies for API Routing

// proxies.json
{
    "$schema": "http://json.schemastore.org/proxies",
    "proxies": {
        "products": {
            "matchCondition": {
                "route": "/v1/products/{*restOfPath}"
            },
            "backendUri": "https://%BACKEND_HOST%/api/products/{restOfPath}"
        },
        "legacy-redirect": {
            "matchCondition": {
                "route": "/old-api/{*path}"
            },
            "responseOverrides": {
                "response.statusCode": "301",
                "response.headers.Location": "/api/{path}"
            }
        }
    }
}

CORS Configuration

// local.settings.json
{
    "IsEncrypted": false,
    "Values": {
        "AzureWebJobsStorage": "UseDevelopmentStorage=true",
        "FUNCTIONS_WORKER_RUNTIME": "node"
    },
    "Host": {
        "CORS": "*",
        "CORSCredentials": false
    }
}

Integration with API Management

# Import Functions into API Management
az apim api import \
    --path /myapi \
    --api-id my-functions-api \
    --resource-group myRG \
    --service-name my-apim \
    --specification-format OpenApiJson \
    --specification-path ./openapi.json

Environment Configuration

# Set application settings
az functionapp config appsettings set \
    --name my-functions-app \
    --resource-group myRG \
    --settings "DATABASE_CONNECTION=<connection-string>"

# Access in function
const connectionString = process.env.DATABASE_CONNECTION;

Local Development

# Install Azure Functions Core Tools
npm install -g azure-functions-core-tools@3

# Create new function
func new --name GetProducts --template "HTTP trigger"

# Run locally
func start

# Test locally
curl http://localhost:7071/api/products

Deployment Options

Azure CLI

# Deploy to Azure
func azure functionapp publish my-functions-app

GitHub Actions

# .github/workflows/deploy.yml
name: Deploy Functions

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - name: Setup Node.js
        uses: actions/setup-node@v2
        with:
          node-version: '14'

      - name: Install dependencies
        run: npm ci

      - name: Deploy to Azure Functions
        uses: Azure/functions-action@v1
        with:
          app-name: my-functions-app
          package: .
          publish-profile: ${{ secrets.AZURE_FUNCTIONAPP_PUBLISH_PROFILE }}

Best Practices

AspectRecommendation
Cold startsUse Premium plan for critical APIs
ScalingConfigure host.json limits
SecurityUse managed identity for Azure resources
MonitoringEnable Application Insights
VersioningUse route prefixes or API Management

Azure Functions HTTP triggers: the simplest path to serverless APIs.

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.