Azure Function TypeScript

A development team I worked with in Seattle had a Node.js API that only needed to run a few times an hour, processing uploaded invoices and writing results to a database. They were paying for an always-on App Service plan just to handle that light, unpredictable traffic. Once we rebuilt the workload as a TypeScript-based function, the same job ran on true consumption pricing and their monthly compute cost dropped by more than half.

That’s the exact situation where Azure Function TypeScript development earns its place in a modern architecture. TypeScript gives you compile-time type safety and better tooling than plain JavaScript, while Azure Functions gives you event-driven, serverless compute that only bills for actual execution time.

Together, they’re a strong fit for APIs, background processing, and integration workloads that don’t need a server running around the clock.

In this guide, I’ll walk through setting up an Azure Function using TypeScript from scratch, covering project structure, identity and secret management, deployment through CI/CD, and the monitoring and cost decisions that separate a hobby project from a production-ready service.

Azure Function TypeScript

Why TypeScript for Azure Functions

Azure Functions is Microsoft’s serverless compute service, where code runs in response to triggers like HTTP requests, timers, queue messages, or blob storage events, and you pay only for the compute consumed during execution. Writing those functions in TypeScript â€” a statically typed superset of JavaScript that compiles down to plain JavaScript — adds a layer of safety that plain JavaScript doesn’t give you.

I switched most of my Node-based function projects to TypeScript a few years ago after a production incident where an untyped function silently passed undefined into a database call and failed at 2 a.m. with a vague error.

TypeScript catches that kind of mistake at build time instead of runtime, which matters a lot when a function is triggered by an external system and nobody’s watching it execute in real time.

The trade-off is a slightly heavier build process. TypeScript functions require a compilation step before deployment, and you need to manage a tsconfig.json file and build output directory correctly, or your function app won’t find the compiled code.

It’s a small amount of added complexity in exchange for fewer runtime surprises, and in my experience, that trade is worth it for anything beyond a quick throwaway script.

Pro Tip: I’ve seen more failed function deployments caused by a misconfigured build output path than by any actual code bug. Get the tsconfig.json output directory right before you touch anything else.

Choosing the Right Hosting Plan

Before writing any code, decide how the function app will be hosted, because this choice affects cost, scaling behavior, and cold start performance. Azure Functions offers three main hosting models, and picking the wrong one is one of the most common mistakes I see teams make early on.

Hosting PlanBilling ModelBest For
ConsumptionPay per execution and GB-secondsUnpredictable or infrequent workloads
PremiumPre-warmed instances, billed hourlyWorkloads needing no cold starts or VNet integration
Dedicated (App Service Plan)Fixed hourly VM costSteady, high-frequency workloads already on App Service

The Consumption plan is the default choice for most new projects because it scales to zero when idle and charges nothing during downtime. The trade-off is cold starts â€” a delay when a function wakes up from an idle state to handle a new request. For the invoice-processing example I mentioned earlier, a few seconds of cold start didn’t matter since it wasn’t user-facing.

If your function serves a real-time API where a two-to-five-second delay is unacceptable, look at the Azure Functions Premium plan, which keeps a minimum number of instances warm and supports virtual network integration for private connectivity to backend resources.

For workloads with more predictable, constant traffic, the Azure Functions Dedicated plan runs on the same App Service Plan infrastructure you might already be using for a web app, which can simplify cost management if you’re consolidating resources on plans you already pay for.

Pro Tip: I default every new TypeScript function project to Consumption plan pricing first, then only move to Premium if cold starts actually cause a measurable problem in testing. Too many teams over-provision for a cold start issue they never confirm exists.

Setting Up the Project Structure

Start by defining the actual business requirement clearly. In this example, we’re building an HTTP-triggered function that receives a JSON payload, validates it, and writes a record to a database — a common pattern for internal line-of-business integrations.

Install the Azure Functions Core Tools and scaffold a new TypeScript project locally.

npm install -g azure-functions-core-tools@4 --unsafe-perm true
func init invoice-processor --typescript
cd invoice-processor
func new --name ProcessInvoice --template "HTTP trigger" --authlevel "function"

The func init command scaffolds a new function app project configured for TypeScript, generating the tsconfig.jsonpackage.json, and base project structure. func new adds a function named ProcessInvoice using the HTTP trigger template, with --authlevel "function" requiring a function key for access instead of allowing anonymous calls — a small but important default for anything beyond local testing.

Your generated tsconfig.json should point the compiled output to a dist folder, and your package.json needs a build script:

json{
  "scripts": {
    "build": "tsc",
    "prestart": "npm run build",
    "start": "func start"
  }
}

This ensures the TypeScript compiler runs before the function host starts locally, so you’re always testing against the latest compiled code rather than a stale build. If you’re new to the broader Functions platform, this guide on what Azure Functions are used for is a good primer before diving deeper into TypeScript-specific patterns.

Pro Tip: I always add dist/ to .gitignore and let the CI/CD pipeline handle the build step. Committing compiled output to source control creates merge conflicts that have nothing to do with actual code changes.

Managing Identity and Secrets Correctly

This is where I see the most security mistakes in function projects, especially ones built quickly under deadline pressure. A function that needs to connect to a database, storage account, or external API needs credentials, and the wrong place to store those credentials is directly in code or in an unprotected local.settings.json file that accidentally gets committed to a repository.

The right approach depends on what the function is authenticating to. If it’s calling another Azure resource — say, writing to a Cosmos DB container or reading from Blob Storage — use a managed identity instead of a connection string.

A managed identity is an identity Azure manages for you automatically, letting your function authenticate to other Azure services without you ever storing a secret.

az functionapp identity assign \
--name func-invoice-processor-prod \
--resource-group rg-invoice-app-prod

This command enables a system-assigned managed identity on the function app func-invoice-processor-prod. Once enabled, you grant that identity permission to the target resource through RBAC instead of copying a connection string into app settings.

az role assignment create \
--assignee <managed-identity-object-id> \
--role "Storage Blob Data Contributor" \
--scope "/subscriptions/<subscription-id>/resourceGroups/rg-invoice-app-prod/providers/Microsoft.Storage/storageAccounts/stinvoicedocs001"

This grants the function’s managed identity the Storage Blob Data Contributor role scoped only to the specific storage account it needs, following the principle of least privilege rather than granting subscription-wide access.

For secrets that genuinely can’t be replaced by a managed identity — like a third-party API key — store them in Azure Key Vault rather than app settings.

az keyvault secret set \
--vault-name kv-invoice-app-prod \
--name ThirdPartyApiKey \
--value "<secret-value>"

Then reference that secret from your function app configuration using a Key Vault reference, so the actual value never lives in your app settings in plain text. If you’re unfamiliar with how Key Vault fits into a broader security design, review this explanation of how Azure Key Vault works and this guide on creating a secret in Azure Key Vault before wiring up your function’s connections.

Pro Tip: I test every new function’s permissions using a non-admin test account before calling it done. It’s the fastest way to catch an RBAC role that’s too broad or too narrow before it reaches production.

Deploying Through Azure DevOps

Manual deployment through the Azure Portal or CLI is fine for a quick proof of concept, but any real project needs a repeatable CI/CD pipeline so deployments aren’t dependent on one person’s laptop having the right tools installed.

Azure DevOps Pipelines handle this well for TypeScript function projects since the build step and deployment step map cleanly onto pipeline stages.

texttrigger:
  branches:
    include:
      - main

pool:
  vmImage: 'ubuntu-latest'

variables:
  azureSubscription: 'sc-invoice-app-prod'
  functionAppName: 'func-invoice-processor-prod'

steps:
  - task: NodeTool@0
    inputs:
      versionSpec: '20.x'
    displayName: 'Install Node.js'

  - script: |
      npm install
      npm run build
    displayName: 'Install dependencies and compile TypeScript'

  - task: ArchiveFiles@2
    inputs:
      rootFolderOrFile: '$(System.DefaultWorkingDirectory)'
      includeRootFolder: false
      archiveType: 'zip'
      archiveFile: '$(Build.ArtifactStagingDirectory)/function.zip'
    displayName: 'Package function app'

  - task: AzureFunctionApp@2
    inputs:
      azureSubscription: '$(azureSubscription)'
      appType: 'functionAppLinux'
      appName: '$(functionAppName)'
      package: '$(Build.ArtifactStagingDirectory)/function.zip'
    displayName: 'Deploy to Azure Function App'

This pipeline triggers on commits to main, installs Node.js 20, compiles the TypeScript source, packages the compiled output into a zip archive, and deploys it to the target function app using a service connection rather than embedded credentials.

The azureSubscription variable references a pre-configured service principal connection in Azure DevOps, which keeps deployment credentials out of the YAML file entirely. If you’re setting this up for the first time, this step-by-step CI/CD pipeline guide walks through configuring the service connection itself.

Pro Tip: I always add a separate pipeline stage for a staging slot before production. Even a simple deployment slot swap has saved me from pushing a broken build straight to a live endpoint more than once.

Monitoring and Logging in Production

A function running unattended in production without monitoring is a liability, not a convenience. Application Insights gives you telemetry on every function execution — duration, success rate, exceptions, and dependency calls to databases or external APIs — and it integrates directly with Azure Functions with minimal setup.

az monitor app-insights component create \
--app appi-invoice-processor \
--location eastus \
--resource-group rg-invoice-app-prod \
--application-type web

This creates an Application Insights resource scoped to rg-invoice-app-prod, which you then link to your function app through its configuration settings. Once connected, you get automatic tracking of function execution times and failure rates without writing custom logging code for the basics.

For TypeScript functions specifically, I recommend adding structured logging inside the function itself so custom business context — like an invoice ID or customer account — shows up alongside the automatic telemetry:

typescriptimport { app, HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions";

export async function ProcessInvoice(
  request: HttpRequest,
  context: InvocationContext
): Promise<HttpResponseInit> {
  context.log(`Processing invoice request: ${request.url}`);

  try {
    const body = await request.json();
    context.log(`Invoice ID received: ${(body as { invoiceId: string }).invoiceId}`);
    return { status: 200, jsonBody: { received: true } };
  } catch (error) {
    context.error(`Failed to process invoice: ${error}`);
    return { status: 500, jsonBody: { error: "Processing failed" } };
  }
}

app.http("ProcessInvoice", {
  methods: ["POST"],
  authLevel: "function",
  handler: ProcessInvoice,
});

This handler logs the incoming request, attempts to parse and process the invoice payload, and logs both success and failure paths clearly with context.log and context.error. That context-aware logging is what actually makes Application Insights useful during an incident — raw execution metrics tell you something failed, but structured logs tell you why.

Set up alert rules in Azure Monitor on failure rate thresholds so your team gets notified before a customer does.

Pro Tip: I’ve resolved more production issues by searching Application Insights for a specific invoice ID in the logs than by staring at dashboards. Structured logging with business context pays for itself the first time you need to trace one specific failed request.

Cost and Performance Considerations

Consumption-plan billing is based on execution count, execution duration, and memory consumption, which means inefficient code directly affects your bill, not just performance. A function that takes three seconds to run when it could run in 300 milliseconds is billing you ten times more per invocation.

A few practical habits keep TypeScript function costs predictable:

  • Keep dependencies lean; a bloated node_modules folder increases cold start time on the Consumption plan
  • Avoid unnecessary synchronous operations that block the event loop, since Node.js functions perform best with proper async/await usage
  • Set appropriate timeout values so a hung function doesn’t run indefinitely and rack up execution time
  • Separate development and production function apps entirely, since testing against production data or triggering production alerts during development wastes both time and budget

Tag your function app and its associated resource group so costs are traceable in Cost Analysis, especially if multiple teams share a subscription. Reviewing Azure cost optimization best practices periodically helps catch drift before it becomes a surprise on the monthly invoice.

Pro Tip: I benchmark cold start time and average execution duration in staging before every major dependency upgrade. A single new npm package with a heavy import chain has added over a second to cold start time in projects I’ve worked on.

How to write Azure function in TypeScript?: Step By Step

Let’s discuss how to create a typescript Azure Function using Visual Studio Code. Before starting the actual development, we should know what the Prerequisites are needed to create the Azure Function using typescript.

Prerequisites

Below are the prerequisites needed to start with the development

  • Make sure you have an Azure subscription or Azure Account. If you don’t have an Azure account as of now, Create a free Azure Account now.
  • Next thing is, you must have Visual Studio Code installed on your machine. If you have not yet installed it, you can install the Visual Studio Code now.
  • Don’t forget to install the Azure Function Extensions for Visual Studio Code.

Assuming you have all the prerequisites needed here, let’s start with creating the Azure Function Project.

Create the Azure Function Project

  • Open the Visual Studio code IDE, click on the Azure button from the left side, and then click on the Create New Project button as highlighted below.
azure function typescript
  • Browse a location where you want to save your Azure Function project.
  • Make sure to choose the language as Typescript, as highlighted below.
Create a function in Azure with TypeScript
  • On the next window, select the trigger as per your requirement. Here, for the demo perspective, I am choosing the simple and basic trigger, which is the HTTP trigger option, as highlighted below.
typescript azure functions
  • Provide a unique name for your Azure Function Project in the next window, and then press Enter.
  • In the next window, choose the Authorization level based on your requirement, as highlighted below. The available options are Function, Anonymous, and Admin.
How to create a typescript Azure Function using Visual Studio Code

Now, it will take a few seconds to create the Azure Function; you can see the project was created successfully, and below is the index.ts file code.

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

const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
    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."
        : "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
    };

};

export default httpTrigger;

Now, if you press F5 to run the Azure function project, you can see that it ran successfully and provided us with the Azure Function URL as highlighted below.

Creating a function in Azure with TypeScript using Visual Studio Code

Test Typescript Azure Function Locally

Now, the Azure Function is created. To make sure the function is working as expected. We can test it locally. Press F5 to run the function. Now you can see the Azure Function project ran successfully and provided us with the below Azure Function URL.

http://localhost:7071/api/myazuretypescriptfunction

Now Open Your Favorite browser and paste the above URL, you can able to see, we got the expected output.

Azure functions typescript example

We tried executing the URL below with the name value as a query string parameter. You can able to see we got the expected output.

How To Test Typescript Azure Function Locally

Deploy Typescript Azure Function To Azure from Visual Studio Code

Since the typescript Azure Function is working properly, let’s deploy the typescript Azure Function to the Azure Portal. Follow the below steps to deploy the typescript Azure Function.

You can click on the Deploy To function App button as highlighted below.

How to Deploy Node js Azure Function

Or else, for the same option, right-click on the Function name â€”-> Click on the Deploy to Function App option as highlighted below.

Deploy Typescript Azure Function To Azure from Visual Studio Code

Now select the Azure Function App that you have created in the Azure Portal. You can search with your Azure Function App name and then select that.

Note: Make sure to create an Azure Function App in the Azure Portal. You can refer to the above section to create the Azure Function App in the Azure Portal.

How To Deploy Typescript Azure Function To Azure from Visual Studio Code

Now, it will take a few seconds to deploy the Azure Function to the selected Azure Function App successfully.

Frequently Asked Questions

Can I use TypeScript with Azure Functions?

Yes. Azure Functions has first-class support for TypeScript through the Node.js runtime, using func init --typescript to scaffold a project with the correct build configuration. The TypeScript code compiles to JavaScript before deployment, so the function host always runs the compiled output.

Should I use the Consumption plan or Premium plan for a TypeScript function?

Start with the Consumption plan unless you’ve confirmed cold starts cause a real problem for your use case. The Premium plan eliminates cold starts and supports virtual network integration, but it bills hourly regardless of actual usage, which costs more for infrequent workloads.

How do I secure secrets in an Azure Function?

Use a managed identity combined with RBAC for anything connecting to another Azure resource, and store any remaining secrets, like third-party API keys, in Azure Key Vault rather than app settings or source code. Reference Key Vault secrets through app configuration instead of hardcoding values anywhere in your project.

Why is my Azure Function not triggering?

Common causes include an incorrect trigger binding configuration, a missing or expired function key for HTTP triggers, or a build output path mismatch in TypeScript projects. Check the Azure Function not triggering troubleshooting steps to work through the most frequent causes systematically.

How do I monitor a TypeScript Azure Function in production?

Connect the function app to Application Insights to automatically capture execution duration, failure rates, and dependency calls. Add structured logging inside your function code using context.log and context.error so you can trace specific requests during an incident instead of relying on aggregate metrics alone.

Building an Azure Function in TypeScript gives you serverless scalability and compile-time safety, but only pays off when the project structure, identity model, and monitoring are set up correctly from the start.

The most important principle to carry forward is replacing stored secrets with managed identities and RBAC wherever possible, since that single decision prevents most of the security incidents I’ve seen in production function apps. I hope you found this article helpful.

Now, you can easily create Typescript Azure Functions. Azure functions now support Typescript. Typescript is nothing but a superset of JavaScript that helps you with static typing, different interfaces, and classes that make the development process much easier.

You May Also Like