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.json
file in your editor. - 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, theAddItem
Function gets an HTTP request namedreq
as input, and it will return an HTTP response namedres
as output. - You will need to customize the first element in the
bindings
array. 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
GET
andPOST
, onlyPOST
is 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. Theroute
property 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 theAddItem
function 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.ts
file, which contains the code that is executed when theAddItem
Function is triggered through an HTTP request. - From the signature of the
httpTrigger
function tells you that it takes acontext
andreq
objects as input. Thecontext
object allows the Azure Function runtime to pass data to your Function. You defined thereq
object as part of the HTTP template for Azure Functions in the previous step. - 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. - 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
context
object. 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
shoppingListID
is undefined or an empty string or theitemToAdd
from the body is undefined, or theitemToAdd
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 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
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 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
CosmosDBService
and theItem
class 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
CosmosDBService
class. So the actual code for adding an item is well encapsulated and can be easily reused in other places than just thisAddItem
Function. TheAddItem
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. - Lastly, what happens if some error happens adding the item in the
CosmosDBService
? YourAddItem
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 atry-catch
block. 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;