Create and deploy a TypeScript Azure Function

In this tutorial, create a secure API in Visual Studio Code, then deploy the application to the Azure cloud for hosting with a public HTTP endpoint. The API integrates with a Cosmos DB database using the MongoDB API. The MongoDB API is accessed from the mongoose npm package.

The MongoDB database functionality includes:

Action URL
Add item POST /api/category
Delete item by ID DELETE api/category?id=123
Get item by ID GET api/category?id=123
Get all items GET api/category

Full source code for this Azure Function app:

Prepare your development environment

Install the following software:

The following software is installed as part of the tutorial later:

1. Sign in to Azure in Visual Studio Code

If you already use the Azure service extensions, you should already be logged in and can skip this step.

Once you've installed an extension in Visual Studio Code, you need to sign into your Azure account.

  1. In Visual Studio Code, select the Azure explorer icon, then select Sign in to Azure, and follow the prompts.

    Sign in to Azure through VS Code

  2. After signing in, verify that the email address of your Azure account appears in the Status Bar and your subscription(s) appears in the Azure explorer:

    VS Code Azure explorer showing subscriptions

2. Create an Azure resource group

A resource group is a region-based collection of resources. By creating a resource group, then creating resources in that group, at the end of the tutorial, you can delete the resource group without having to delete each resource individually.

  1. In Visual Studio Code, select Azure explorer, then your subscription under Resource Groups.

  2. Select + to create a new resource group.

  3. Use the following table to complete the prompts:

    Prompt Value
    Enter the name of the new resource group. cosmosdb-mongodb-function-resource-group
    Select a location for your new resources. Select a geographical region close to you.

3. Create the local Functions app

Create a local Azure Functions (serverless) application that contains an HTTP trigger function.

  1. In Visual Studio Code, select the Azure explorer, then select Create New Project in the Functions section:

    Create a local Function app in VS Code

  2. Use the following table to finish creating the local Azure Function project:

    Prompt Value Notes
    Select the folder that will contain your function project. Select the current folder, which is the default value.
    Select a language TypeScript
    Select a template for your project's first function HTTP Trigger API is invoked with an HTTP request.
    Provide a function name category API route is /api/category
    Authorization Level Function This locks the remote API to requests that pass the function key with the request. While developing locally, you won't need the function key.

    This process doesn't create cloud-based Azure Function resource. That step will come later.

  3. When Visual Studio Code completes creation of the project, you have a folder named for the function, category with three files:

    Filename Description
    index.ts The source code that responds to the HTTP request.
    function.json The binding configuration for the HTTP trigger.
    sample.dat A placeholder data file to demonstrate that you can have other files in the folder. You can delete this file, if desired, as it's not used in this tutorial.
  4. In Visual Studio Code, open an integrated bash terminal, Ctrl + ` and install the Azure Function app dependencies:

    npm install
    
  5. Add the Azure Function core tools package, required to run the Azure Function app locally:

    npm install --global azure-functions-core-tools@3
    
  6. In the ./category/index.ts file, add a new context.log message to print the name to the function's log, highlighted in the following 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));
        context.log(`*** HTTPExample name: ${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;
    

4. Run the local serverless function

Run the Azure Functions project locally to test it before deploying to Azure.

  1. In Visual Studio Code, ./category/index.ts file, set a break point on the final context.res block, at the end of the function.

  2. In Visual Studio Code, press F5 to launch the debugger and attach to the Azure Functions host.

    You could also use the Debug > Start Debugging menu command.

  3. Output from the Functions Core tools appears in the Terminal panel.

    Partial screenshot of VSCode output terminal panel when debugging locally

  4. To copy the URL of the local function, use the Azure explorer, right-click the function name, category, then select Copy Function Url.

    Partial screenshot of Visual Studio Code, with the Azure Function's button named Copy Function URL highlighted.

  5. In your browser, paste the URL, then add ?name=YOUR-NAME to the end of URL, replacing YOUR-NAME with your name:

    Screenshot of web browser displaying results of HTTP trigger function parsing URL parameters.

    Because the function is running locally, your local API doesn't need the function key to work successfully.

  6. To see the entire HTTP response, use the following cURL command in the terminal:

    curl http://localhost:7071/api/category?name=john --verbose
    
  7. The response is:

    *   Trying ::1:7071...
    *   Trying 127.0.0.1:7071...
    * Connected to localhost (127.0.0.1) port 7071 (#0)
    > GET /api/category?name=john HTTP/1.1
    > Host: localhost:7071
    > User-Agent: curl/7.75.0
    > Accept: */*
    >
    * Mark bundle as not supporting multiuse
    < HTTP/1.1 200 OK
    < Date: Tue, 21 Sep 2021 17:35:05 GMT
    < Content-Type: text/plain; charset=utf-8
    < Server: Kestrel
    < Transfer-Encoding: chunked
    < Request-Context: appId=cid-v1:e981b763-c455-4e32-852c-73765b048a0f
    <
    Hello, john. This HTTP triggered function executed successfully.* Connection #0 to host localhost left intact
    

5. Set and stop at break point in serverless app

With your function running locally, set breakpoints on different parts of the code.

  1. Open index.ts, then click in the margin to the left of last context.res, in the editor window.

  2. A small red dot appears to indicate a breakpoint.

  3. Change the ?name= value for the URL in the integrated bash terminal and resubmit the request to the function.

  4. When the browser makes that request, VS Code stops the function code on that breakpoint:

    Screenshot of Visual Studio Code with breakpoint activated and Closure (httpTrigger) variables displaying request values.

    Expand the Variables element named Closure (httpTrigger) to see the request properties. You can view all the properties passed into the function.

  5. Stop the debugger in Visual Studio Code, Shift + F5.

6. Create the Function app and deploy to Azure

  1. In Visual Studio Code, select the Azure explorer, then under Functions, select the Deploy to Function app icon to deploy your app:

    Deploy to Azure Functions command

    Alternately, you can deploy by opening the Command Palette (F1), entering 'deploy to function app' to filter the commands, then select the Azure Functions: Deploy to Function App command.

  2. Use the following table to complete the prompts to create a new Azure Function resource.

    Prompt Value Notes
    Select Function App in Azure Create new Function app in Azure (Advanced) Create a cloud-based resource for your function.
    Enter a globally unique name for the new Function App The name becomes part of the API's URL. API is invoked with an HTTP request. Valid characters for a function app name are 'a-z', '0-9', and '-'. An example is cosmosdb-mongodb-function-app.
    Select a runtime stack Select a Node.js stack with the LTS descriptor. Select Node.js 14 LTS
    Select an OS. Windows
    Select a resource group for new resources. cosmosdb-mongodb-function-resource-group Select the resource group you created in the first article of this series.
    Select a location for new resources. Select the recommended region.
    Select a hosting plan. Consumption
    Select a storage account. + Create new storage account named cosmosdbmongodbstorage.
    Select an Application Insights resource for your app. Create a new Application Insights resource named cosmosdb-mongodb-function-app-insights.
  3. The Visual Studio Code Output panel for Azure Functions shows progress:

    12:26:48 PM: Creating new function app "Visual Studio Codecosmosdb-mongodb-function-app"...
    12:27:09 PM: Successfully created function app "Visual Studio Codecosmosdb-mongodb-function-app": https://Visual Studio Codecosmosdb-mongodb-function-app.azurewebsites.net
    12:27:38 PM Visual Studio Codecosmosdb-mongodb-function-app: Starting deployment...
    12:27:40 PM Visual Studio Codecosmosdb-mongodb-function-app: Creating zip package...
    12:27:41 PM Visual Studio Codecosmosdb-mongodb-function-app: Uploading zip package to storage container...
    12:27:41 PM Visual Studio Codecosmosdb-mongodb-function-app: Zip package size: 2.73 kB
    12:27:44 PM Visual Studio Codecosmosdb-mongodb-function-app: Deployment successful.
    12:27:44 PM Visual Studio Codecosmosdb-mongodb-function-app: Started postDeployTask "npm install (functions)".
    12:27:55 PM Visual Studio Codecosmosdb-mongodb-function-app: Syncing triggers...
    12:27:57 PM Visual Studio Codecosmosdb-mongodb-function-app: Querying triggers...
    12:28:01 PM Visual Studio Codecosmosdb-mongodb-function-app: WARNING: Some http trigger urls cannot be displayed in the output window because they require an authentication token. Instead, you may copy them from the Azure Functions explorer.
    

    When deploying, the entire Functions application is deployed, any changes to individual APIs are deployed at once.

  4. When the resource is created, a notification pops up, usually in the lower left corner of Visual Studio Code.

  5. In the notification, select Stream logs and keep the view open while you make a request to the API in the next section.

7. Run the remote serverless function

  1. Once deployment is completed, go to the Azure explorer, expand the node for your Azure subscription, expand the node for your Functions app, then expand Functions (read only). Right-click the function name, category and select Copy Function Url:

    Copy function URL command

  2. Paste the URL into a browser. The URL includes the function key, code, as a query parameter.

  3. Append a querystring name/value pair,&name=YOUR-NAME, to the URL. The browser shows the successful function running in the cloud.

    Screenshot of a browser showing the result of the API returns successfully.

  4. Now remove the code= querystring parameter from the URL and submit the URL in the browser again. This simulates an unauthorized request to your secured API.

    Screenshot of a browser showing the result of the API returns an HTTP error code of 401.

  5. Review the streaming log in Visual Studio Code to find your context.log output.

8. Add Cosmos DB for MongoDB API integration

Cosmos DB provides a MongoDB API to provide a familiar integration point.

  1. In Visual Studio Code, select the Azure Explorer, then under Databases, select + to begin the creation process.

    Use the following table to complete the prompts to create a new Azure Cosmos DB resource.

    Prompt Value Notes
    Select an Azure Database Server Azure Cosmos DB for MongoDB API
    Provide a Cosmos DB account name. cosmosdb-mongodb-database The name becomes part of the API's URL.
    Select a capacity model. Provisioned Throughput
    Select a resource group for new resources. cosmosdb-mongodb-function-resource-group Select the resource group you created in the first article of this series.
    Select a location for new resources. Select the recommended region.
  2. In a Visual Studio Code terminal, install the npm package:

    npm install mongoose
    
  3. In Visual Studio Code, create a subdirectory named lib, create a file named ./azure-cosmosdb-mongodb.ts and copy the following code into it.

    import { Schema, model, connect } from "mongoose";
    
    let db=null;
    
    const CategorySchema = new Schema(
      { categoryName: String },
      { timestamps: true }
    );
    const CategoryModel = model("Category", CategorySchema, "Bookstore");
    
    export const init = async () => {
      if(!db) {
        db = await connect(process.env["CosmosDbConnectionString"]);
      }
    };
    export const addItem = async (doc) => {
      const modelToInsert = new CategoryModel();
      modelToInsert["categoryName"] = doc.name;
    
      return await modelToInsert.save();
    };
    export const findItemById = async (id) => {
      return await CategoryModel.findById(id);
    };
    export const findItems = async (query = {}) => {
      return await CategoryModel.find({});
    };
    export const deleteItemById = async (id) => {
      return await CategoryModel.findByIdAndDelete(id);
    };
    

    This file contains a simple mongoose schema for a Category container.

  4. In Visual Studio Code, open the ./category/index.ts file and replace the entire file's code with the following:

    import { AzureFunction, Context, HttpRequest } from "@azure/functions";
    import * as db from "../lib/azure-cosmosdb-mongodb";
    
    const httpTrigger: AzureFunction = async function (
      context: Context,
      req: HttpRequest
    ): Promise<void> {
      try {
        let response = null;
    
        // create 1 db connection for all functions
        await db.init();
    
        switch (req.method) {
          case "GET":
            if (req?.query.id || (req?.body && req?.body?.id)) {
              response = {
                documentResponse: await db.findItemById(req?.body?.id),
              };
            } else {
              // allows empty query to return all items
              const dbQuery =
                req?.query?.dbQuery || (req?.body && req?.body?.dbQuery);
              response = {
                documentResponse: await db.findItems(dbQuery),
              };
            }
            break;
          case "POST":
            if (req?.body?.document) {
              const insertOneResponse = await db.addItem(req?.body?.document);
              response = {
                documentResponse: insertOneResponse,
              };
            } else {
              throw Error("No document found");
            }
    
            break;
          case "DELETE":
            if (req?.query?.id || (req?.body && req?.body?.id)) {
              response = {
                documentResponse: await db.deleteItemById(req?.body?.id),
              };
            } else {
              throw Error("No id found");
            }
    
            break;
          default:
            throw Error(`${req.method} not allowed`)
        }
    
        context.res = {
          body: response,
        };
      } catch (err) {
        context.log(`*** Error throw: ${JSON.stringify(err)}`);
    
        context.res = {
          status: 500,
          body: err,
        };
      }
    };
    
    export default httpTrigger;
    
  5. In Visual Studio Code, open the ./category/function.json file and change the methods property to include delete.

    {
      "bindings": [
        {
          "authLevel": "function",
          "type": "httpTrigger",
          "direction": "in",
          "name": "req",
          "methods": [
            "get",
            "post",
            "delete"
          ]
        },
        {
          "type": "http",
          "direction": "out",
          "name": "res"
        }
      ],
      "scriptFile": "../dist/category/index.js"
    }
    

9. Add database connection string to local project

  1. In Visual Studio Code, select the Azure explorer, then under Databases, right-click your database and select Copy Connection String.

    Partial screenshot of Visual Studio Code, showing the Azure explorer with a database selected and the right-click menu highlighting Copy Connection String.

  2. Open the ./local.settings.json file and add a new property CosmosDbConnectionString and paste in the database connection string in as the value.

    {
      "IsEncrypted": false,
      "Values": {
        
        "FUNCTIONS_WORKER_RUNTIME": "node",
        "CosmosDbConnectionString": ""
      }
    }
    

10. Use the Azure Function APIs

Add items to database with API

  1. In Visual Studio Code, press F5 to launch the debugger and attach to the Azure Functions host.

    You could also use the Debug > Start Debugging menu command.

  2. Use the following curl command in the integrated bash terminal to add John to your database:

    curl -X POST http://localhost:7071/api/category \
       -H 'Content-Type: application/json' \
       -d '{"document":{"name":"John"}}' --verbose
    
  3. The response includes the new item's ID:

    {
      "documentResponse": {
        "_id": "614a45d97ccca62acd742550",
        "categoryName": "John",
        "createdAt": "2021-09-21T20:51:37.669Z",
        "updatedAt": "2021-09-21T20:51:37.669Z",
        "__v": 0
      }
    }
    
  4. Use the following curl command in the integrated bash terminal to add Sally to your database:

    curl -X POST http://localhost:7071/api/category \
       -H 'Content-Type: application/json' \
       -d '{"document":{"name":"Sally"}}' --verbose
    
  5. The response includes the new item's ID:

    {
      "documentResponse": {
        "_id": "614a45d97bbba62acd742550",
        "categoryName": "Sally",
        "createdAt": "2021-09-21T20:51:37.669Z",
        "updatedAt": "2021-09-21T20:51:37.669Z",
        "__v": 0
      }
    }
    

Get all items from database with API

  1. Use the following curl command to get all items from the database:

    curl -X GET http://localhost:7071/api/category \
       -H 'Content-Type: application/json' --verbose
    
  2. The response includes the new item's ID:

    {
      "documentResponse": [
        {
          "_id": "614a45d97ccca62acd742550",
          "categoryName": "John",
          "createdAt": "2021-09-21T20:51:25.288Z",
          "updatedAt": "2021-09-21T20:51:25.288Z",
          "__v": 0
        },
        {
          "_id": "614a45d97bbba62acd742550",
          "categoryName": "Sally",
          "createdAt": "2021-09-21T20:51:37.669Z",
          "updatedAt": "2021-09-21T20:51:37.669Z",
          "__v": 0
        }
      ]
    }
    

View all data with Visual Studio Code extension

  1. In Visual Studio Code, select the Azure Explorer, then under Databases, right-click your Cosmos DB resource, then the Test database, then the Bookstore collection.

  2. Select one of the items listed to view the data in the Cosmos DB.

    Partial screenshot of Visual Studio Code, showing the Azure explorer with the Databases with a selected item displayed in the reading pane.

Get one item from the database with API

  1. Use the following curl command to get one item from the database. Replace DOCUMENT_ID with one of the IDs from a previous step's response:

    curl -X GET http://localhost:7071/api/category \
       -H 'Content-Type: application/json' \
       -d '{"id":"DOCUMENT_ID"}' --verbose
    
  2. The response includes the new item's ID:

    {
      "documentResponse": {
        "_id": "614a45cd7ccca62acd74254e",
        "categoryName": "John",
        "createdAt": "2021-09-21T20:51:25.288Z",
        "updatedAt": "2021-09-21T20:51:25.288Z",
        "__v": 0
      }
    }
    
  3. Return to the Database section of the Azure Explorer in Visual Studio Code, right-click on your Cosmos DB and select Refresh to verify that the item was removed from your cloud resource.

Delete one item from the database with API

  1. Use the following curl command to delete one item from the database. Replace DOCUMENT_ID with one of the IDs from a previous step's response:

    curl -X DELETE http://localhost:7071/api/category \
       -H 'Content-Type: application/json' \
       -d '{"id":"DOCUMENT_ID"}' --verbose
    
  2. The response includes the new item's ID:

    {
      "documentResponse": {
        "_id": "614a45cd7ccca62acd74254e",
        "categoryName": "John",
        "createdAt": "2021-09-21T20:51:25.288Z",
        "updatedAt": "2021-09-21T20:51:25.288Z",
        "__v": 0
      }
    }
    

11. Redeploy the function app to include database code

  1. In Visual Studio Code, select the Azure Explorer, then under Functions, select the deploy icon to begin the deployment process.

    Partial screenshot of Visual Studio Code, showing the Azure explorer with the Functions deploy icon highlighted.

  2. In the pop-up window, select the same function app, cosmosdb-mongodb-function-app to begin the deployment.

  3. Wait until deployment completes before continuing.

12. Add database connection string to remote function

  1. In Visual Studio Code, in the Azure Explorer, under Functions, select and expand your Function app.

  2. Right-click on Application Settings and select Add New Setting.

    Partial screenshot of Visual Studio Code, showing the Azure explorer with the Functions Application Settings, with the Add new setting menu item highlighted.

  3. Enter the app setting name, CosmosDbConnectionString and press enter. Leave the next pop window up.

  4. To get the connection string, select the Azure Explorer, then under Databases, right-click your database and select Copy Connection String.

  5. Paste the value into the still-open pop-up window.

13. Use cloud-based Azure Function

  1. Still in the Azure Explorer, in the Functions area, select and expand your function then the Functions node, which lists the API, category.

  2. Right-click on the category item and select Copy Function Url.

  3. Use the following cURL commands, and replace YOUR-FUNCTION-URL. Run each command in a terminal in order.

    curl -X POST YOUR-FUNCTION-URL \
       -H 'Content-Type: application/json' \
       -d '{"document":{"name":"John"}}' --verbose
    
    curl -X POST YOUR-FUNCTION-URL \
       -H 'Content-Type: application/json' \
       -d '{"document":{"name":"Sally"}}' --verbose
    
    curl -X GET YOUR-FUNCTION-URL \
       -H 'Content-Type: application/json' --verbose
    
  4. Use the following cURL commands, and replace YOUR-FUNCTION-URL and DOCUMENT_ID with an ID from the previous command. Run each command in a terminal in order.

    curl -X GET YOUR-FUNCTION-URL \
       -H 'Content-Type: application/json' \
       -d '{"id":"DOCUMENT_ID"}' --verbose
    
    curl -X DELETE YOUR-FUNCTION-URL \
       -H 'Content-Type: application/json' \
       -d '{"id":"DOCUMENT_ID"}' --verbose
    

14. Query your Azure Function logs

To search the logs, use the Azure portal.

  1. In Visual Studio Code, select the Azure Explorer, then under Functions, right-click on your function app, then select Open in Portal.

    This opens the Azure portal to your Azure Function.

  2. From Settings, select Application Insights, then select View Application Insights data.

    Browser screenshot showing menu choices. Select **Application Insights** from the Settings, then select **View Application Insights data**.

    This link takes you to your separate metrics resource created for you when you created your Azure Function with Visual Studio Code.

  3. From the Monitoring section, select Logs. If a Queries pop-up window appears, select the X in the top-right corner of the pop-up to close it.

  4. In the New Query 1 pane, on the Tables tab, double-click the traces table.

    This enters the Kusto query, traces into the query window.

  5. Edit the query to search for the custom logs:

    traces 
    | where message startswith "***"
    
  6. Select Run.

    If the log doesn't display any results, it may be because there is a few minute delay between the HTTP request to the Azure Function and the log availability in Kusto. Wait a few minutes and run the query again.

    Browser screenshot showing Azure portal Kusto query result for Trace table.

    You didn't need to do anything extra to get this logging information:

    • The code used the context.log function provided by the Function framework. By using context, instead of console, your logging can be filtered to the specific individual function. This is useful if your Function app has many functions.
    • The Function app added Application Insights for you.
    • The Kusto Query tool is included in the Azure portal.
    • You can click on traces instead of having to learn to write a Kusto query to get even the minimum information from your logs.

15. Clean up resources

The Functions App you created includes resources that can incur minimal costs (refer to Functions Pricing). Remove the resource group to remove all the resources in a single action.

  1. In Visual Studio Code, from the Azure explorer, find the resource group in the Resource Groups section, cosmosdb-mongodb-function-resource-group.

  2. Right-click the resource group name and select Delete.

    Use the Visual Studio Code extension, Azure Resource Groups, to delete the resource group and all resources within the group.

Next steps

Learn more about Azure Functions:

Check out the other Azure extensions:

To learn more about working with Azure using Node.js, visit the resources below: