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.
Azure Functions written in TypeScript need a JSON configuration file to specify to the Azure Function runtime the details of your Function.
- Open the
functions/AddItemFunction/function.jsonfile in your editor. - Because you created the
AddItemFunction from the HTTP template, there is already some existing configuration.- The
bindingsarray 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, theAddItemFunction gets an HTTP request namedreqas input, and it will return an HTTP response namedresas output. - You will need to customize the first element in the
bindingsarray. Changeto{ "authLevel": "anonymous", "type": "httpTrigger", "direction": "in", "name": "req", "methods": [ "get", "post" ] }{ "authLevel": "anonymous", "type": "httpTrigger", "direction": "in", "name": "req", "methods": [ "post" ], "route": "AddItemFunction/{shoppingListID}" }
- The
- There are two changes in the configuration you changed above.
1. Instead of allowing this Function to be called with HTTP
GETandPOST, onlyPOSTis allowed in the updated configuration. This is because the Function will not retrieve any data, which is the meaning ofGET. Instead, it will accept data in the body of the request and store it, which is the semantic ofPOST. 2. Therouteproperty is new. It explicitly defines the URL for this Function. The route also contains a route parameter in curly braces with the nameshoppingListID. This is so the client calling theAddItemfunction can specify the shopping list to add the new item.
The next step is to implement the logic for the AddItem Function.
- Open the
functions/AddItemFunction/index.tsfile, which contains the code that is executed when theAddItemFunction is triggered through an HTTP request. - From the signature of the
httpTriggerfunction tells you that it takes acontextandreqobjects as input. Thecontextobject allows the Azure Function runtime to pass data to your Function. You defined thereqobject as part of the HTTP template for Azure Functions in the previous step. - You can delete all the code in the body of the
httpTriggerfunction. It is an example code that was generated when creating the Function from the template. - 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.
- The id of the shopping list is defined as a route parameter. The Azure Function runtime will pass it to you through the
contextobject. To get it writeconst shoppingListID = context.bindingData.shoppingListID; - You will need to get the item to add to the shopping list from the body of the HTTP request (
req). Assign it withconst itemToAdd = req.body;
- The id of the shopping list is defined as a route parameter. The Azure Function runtime will pass it to you through the
- 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.
- If the
shoppingListIDis undefined or an empty string or theitemToAddfrom the body is undefined, or theitemToAddhas 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 HTTP400(Bad Request) status code and a little error message.
if (!shoppingListID || shoppingListID === '' || !itemToAdd || !itemToAdd.itemName) {
context.res = {
status: 400,
body: {
message: 'invalid input'
}
};
return;
}
- Now that you know the input is valid, you can use the
CosmosDBServieaddItemfunctionality to store the new item in the database. If it succeeds, you can let the client know by sending it a response with HTTP status201(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 }; - Now, you will need to add the missing imports for the
CosmosDBServiceand theItemclass at the top of your file.
import { Item } from "../models/item";
import { CosmosDBService } from "../services/cosmosDBService";
- 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
CosmosDBServiceclass. So the actual code for adding an item is well encapsulated and can be easily reused in other places than just thisAddItemFunction. TheAddItemFunction itself is only concerned with its tasks of knowing how to handle an HTTP request, validate and extract the input and send a response. - Lastly, what happens if some error happens adding the item in the
CosmosDBService? YourAddItemFunction 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 atry-catchblock. You should send an HTTP400(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;