Azure Functions Implement Add Item - microsoft-campus-community/workshop-shopping-list-bot GitHub Wiki

Prerequisite: This page assumes that you have created an Azure Function with the name AddItem in Visual Studio Code. If you do not have it please create an Azure Functions in VS Code first.

When you write a shopping list, you add items to it. The same goes for the shared digital shopping list. This page will guide you through implementing the AddItem Azure Function, which provides the functionality to add a single item to a given shopping list.

Configuration for the AddItem Function

Azure Functions written in TypeScript need a JSON configuration file to specify to the Azure Function runtime the details of your Function.

  1. Open the functions/AddItemFunction/function.json file in your editor.
  2. Because you created the AddItem Function from the HTTP template, there is already some existing configuration.
    • The bindings array defines the trigger/input for your Function and the output the Azure Function runtime can expect from your function. Because you used the HTTP template, the AddItem Function gets an HTTP request named req as input, and it will return an HTTP response named res as output.
    • You will need to customize the first element in the bindings array. Change
      {
         "authLevel": "anonymous",
         "type": "httpTrigger",
         "direction": "in",
         "name": "req",
         "methods": [
                      "get",
                      "post"
         ]
      }
      
      to
      {
         "authLevel": "anonymous",
         "type": "httpTrigger",
         "direction": "in",
         "name": "req",
         "methods": [
             "post"
         ],
         "route": "AddItemFunction/{shoppingListID}"
      }
      
  3. There are two changes in the configuration you changed above. 1. Instead of allowing this Function to be called with HTTP GET and POST, only POST is allowed in the updated configuration. This is because the Function will not retrieve any data, which is the meaning of GET. Instead, it will accept data in the body of the request and store it, which is the semantic of POST. 2. The route property is new. It explicitly defines the URL for this Function. The route also contains a route parameter in curly braces with the name shoppingListID. This is so the client calling the AddItem function can specify the shopping list to add the new item.

Write the AddItem Function

The next step is to implement the logic for the AddItem Function.

  1. Open the functions/AddItemFunction/index.ts file, which contains the code that is executed when the AddItem Function is triggered through an HTTP request.
  2. From the signature of the httpTrigger function tells you that it takes a context and req objects as input. The context object allows the Azure Function runtime to pass data to your Function. You defined the req object as part of the HTTP template for Azure Functions in the previous step.
  3. You can delete all the code in the body of the httpTrigger function. It is an example code that was generated when creating the Function from the template.
  4. First up, you should assign the input your Function expects to local variables. There are two pieces of data the Function needs to add a new item.
    1. The id of the shopping list is defined as a route parameter. The Azure Function runtime will pass it to you through the context object. To get it write const shoppingListID = context.bindingData.shoppingListID;
    2. You will need to get the item to add to the shopping list from the body of the HTTP request (req). Assign it with const itemToAdd = req.body;
  5. Now that you have the item to add to the shopping list and the id of the shopping list, you should verify if the input is correct or if the client's calling your Function made a mistake.
  6. If the shoppingListID is undefined or an empty string or the itemToAdd from the body is undefined, or the itemToAdd has no name, then the input is invalid. You cannot add an item to a shopping list with no id, and likewise, you do not want to add an empty item to a shopping list to keep your data integrity. If the input is invalid, you want to let the client know by sending it an HTTP 400 (Bad Request) status code and a little error message.
if (!shoppingListID || shoppingListID === '' || !itemToAdd || !itemToAdd.itemName) {
      context.res = {
          status: 400,
          body: {
              message: 'invalid input'
          }
      };
      return;
  }
  1. Now that you know the input is valid, you can use the CosmosDBServie addItem functionality to store the new item in the database. If it succeeds, you can let the client know by sending it a response with HTTP status 201 (Created) and the new item in the response body. To do this, add the following code below the input validation. const cosmosService: CosmosDBService = new CosmosDBService(shoppingListID); const itemAdded: Item = await cosmosService.addItem(itemToAdd); context.res = { status: 201, body: itemAdded };
  2. Now, you will need to add the missing imports for the CosmosDBService and the Item class at the top of your file.
import { Item } from "../models/item";
import { CosmosDBService } from "../services/cosmosDBService";
  1. Also, note that this is a fairly nice code. The functionality of the Function is to add an item to the shopping list. However, to do so, it is only a matter of calling one function on the CosmosDBService class. So the actual code for adding an item is well encapsulated and can be easily reused in other places than just this AddItem Function. The AddItem Function itself is only concerned with its tasks of knowing how to handle an HTTP request, validate and extract the input and send a response.
  2. Lastly, what happens if some error happens adding the item in the CosmosDBService? Your AddItem Function should handle it and let the client know through an appropriate response that something went wrong. So you will need to wrap the code written in 7. in a try-catch block. You should send an HTTP 400 (Bad Request) response to the client in the' catch' block.

The code for the completed AddItem Function now looks like this.

import { AzureFunction, Context, HttpRequest } from "@azure/functions"
import { Item } from "../models/item";
import { CosmosDBService } from "../services/cosmosDBService";

const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
    const shoppingListID = context.bindingData.shoppingListID;
    const itemToAdd = req.body;
    if (!shoppingListID || shoppingListID === '' || !itemToAdd || !itemToAdd.itemName) {
        context.res = {
            status: 400,
            body: {
                message: 'invalid input'
            }
        };
        return;
    }
    try {
        const cosmosService: CosmosDBService = new CosmosDBService(shoppingListID);
        const itemAdded: Item = await cosmosService.addItem(itemToAdd);
        context.res = {
            status: 201,
            body: itemAdded
        };
    } catch (error) {
        context.res = {
            status: 400,
            body: {
                message: error.message
            }
        };
    }
};

export default httpTrigger;
⚠️ **GitHub.com Fallback** ⚠️