Azure Functions in Node.js and TypeScript give you a serverless way to run small pieces of code in response to events. The most surprising thing is how little you actually manage; Azure handles the servers, scaling, and patching, letting you focus purely on your business logic.

Let’s see it in action. Imagine a function that triggers when a new file is uploaded to an Azure Blob Storage container.

function.json (for a blob trigger):

{
  "bindings": [
    {
      "name": "myBlob",
      "type": "blobTrigger",
      "direction": "in",
      "path": "samples-workitems/{name}",
      "connection": "AzureWebJobsStorage"
    }
  ]
}

This function.json defines the trigger: it listens to the samples-workitems container, and myBlob will hold the content of the triggered file.

index.ts (TypeScript implementation):

import { AzureFunction, Context } from "@azure/functions";

const blobTrigger: AzureFunction = async function (context: Context, myBlob: Buffer): Promise<void> {
    context.log('TypeScript blob trigger function processed blob\n Name:', context.bindingData.name, '\n Size:', myBlob.length, 'Bytes');
    // Here you would add your logic, e.g., process the image, extract data, etc.
    // For demonstration, we'll just log.
};

export default blobTrigger;

When a file named my-image.jpg lands in the samples-workitems container, this TypeScript function will execute. context.bindingData.name will be "my-image.jpg", and myBlob will contain the binary data of the image as a Node.js Buffer.

The core problem Azure Functions solves is abstracting away the infrastructure. Instead of provisioning virtual machines, configuring web servers, and managing scaling policies for a web application that might only run once an hour, you write a function. Azure then runs that function on demand, scales it up if thousands of events arrive simultaneously, and scales it down to zero when there’s no activity. You pay only for the compute time you consume.

Internally, Azure Functions uses a "host" process that manages the execution of your functions. When an event occurs (like a blob upload), the host receives the trigger, loads your function code (if it’s not already warm), and invokes it with the event payload and context. The context object provides access to logging, binding data (like the blob name and size), and output bindings.

You control function behavior through configuration. For HTTP triggers, you define routes and methods. For queue triggers, you specify which queue to monitor. For timer triggers, you set a CRON expression.

// Example for an HTTP trigger
{
  "bindings": [
    {
      "authLevel": "function",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get",
        "post"
      ],
      "route": "greet/{name}"
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ]
}

This HTTP trigger is accessible at /api/greet/{name}. If you GET or POST to /api/greet/Alice, the req object will contain Alice in req.params.name. The res object is used to send back the HTTP response.

What most developers miss is that Azure Functions run within a specific runtime environment. While you write Node.js/TypeScript, the actual execution happens in a container managed by Azure. This means you have access to the full Node.js API and any npm packages you install, but you’re also subject to the constraints of that environment, like limited local disk space and execution timeouts (defaulting to 5 minutes for Consumption plan, up to 10 minutes for Premium/Dedicated). You can extend these timeouts by configuring the host.json file, but it’s crucial to understand that functions are designed for short-lived, event-driven tasks, not long-running processes.

The next step in mastering Azure Functions is understanding durable functions, which allow you to orchestrate complex workflows and manage state across multiple function executions.

Want structured learning?

Take the full Azure-functions course →