Watch the recording of this lesson on YouTube 🎥.
The goal of this lesson is to create your first function which can be triggered by doing an HTTP GET or POST to the function endpoint.
This lessons consists of the following exercises:
📝 Tip - If you're stuck at any point you can have a look at the source code in this repository.
📝 Tip - If you have questions or suggestions about this lesson, feel free to create a Lesson Q&A discussion here on GitHub.
Prerequisite | Exercise |
---|---|
An empty local folder / git repo | 1-5 |
Azure Functions Core Tools | 1-5 |
VS Code with Azure Functions extension | 1-5 |
Rest Client for VS Code or Postman | 1-5 |
See TypeScript prerequisites for more details.
In this exercise, you'll be creating a Function App with the default HTTPTrigger and review the generated code.
-
In VSCode, create the Function App by running
AzureFunctions: Create New Project
in the Command Palette (CTRL+SHIFT+P). -
Browse to the location where you want to save the function app (e.g. AzureFunctions.Http).
📝 Tip - Create a folder with a descriptive name since that will be used as the name for the project.
-
Select the language you will be using to code the function. In this lesson we will be using
TypeScript
. -
Select
HTTP trigger
as the template. -
Give the function a name (e.g.
HelloWorldHttpTrigger
). -
Select
Function
for the AccessRights.🔎 Observation - Now a new Azure Functions project is being generated. Once it's done, look at the files in the project. You will see the following:
File Description HelloWorldHttpTrigger\index.ts The TypeScript file containing your function code exported as an Azure Function. HelloWorldHttpTrigger\function.json The Azure Function configuration comprising the function's trigger, bindings, and other configuration settings. package.json Contains the manifest file for your project. tsconfig.json Contains the specification of the compiler options required to compile the TypeScript project. host.json Contains global configuration options for all functions in a function app. local.settings.json Contains app settings and connection strings for local development. 📝 Tip - The
function.json
file also contains the location of the transpiled .js file. Be aware to change this in case you copy & paste functions❔ Question - Review the generated HTTPTrigger function. What is it doing?
-
Install the dependencies defined in the
package.json
vianpm install
in a shell of your choice. -
Build the project by making use of the predefined script in the
package.json
file vianpm run build
in a shell of your choice.🔎 Observation - A new folder
dist
is created in your Azure Functions project directory that contains the transpiled JavaScript files as well as your source map files needed for debugging your TypeScript files. -
Start the Function App by pressing
F5
or vianpm run start
.🔎 Observation - You should see an HTTP endpoint in the output.
-
Now call the function by making a GET request to the above endpoint using a REST client:
GET http://localhost:7071/api/HelloWorldHttpTrigger?name=YourName
❔ Question - What is the result of the function? Is it what you expected?
❔ Question - What happens when you don't supply a value for the name?
Let us change the template to find out what parameters can be changed. Depending on the trigger, arguments can be added/removed. Start with only allowing GET requests.
-
Remove the
"post"
entry from the"methods"
array in thefunction.json
file. Now the function can only be triggered by a GET request. -
Switch to the file
index.ts
. Here we leave thereq
parameter unchanged. However, we will ignore thebody
parameter defined on the interfaceHttpRequest
and only take thequery
parameter into account. -
Remove the content of the function (but keep the function definition). We'll be writing a new implementation.
-
To get the name from the query string you can do the following:
const name = req.query.name
🔎 Observation - In the generated template the response object always returns an HTTP status 200. Let's make the function a bit smarter and return a response object with HTTP status 400.
-
Add an
if
statement to the function that checks if the name value isnull
, an empty string orundefined
. If this is this case we return an HTTP code 400 as response, otherwise we return an HTTP code 200.let responseMessage: string let responseStatus: number if (name) { responseStatus = 200 responseMessage = `Hello, ${name}. This HTTP triggered function executed successfully.` } else { responseStatus = 400 responseMessage = `Pass a name in the query string or in the request body for a personalized response.` } context.res = { status: responseStatus, body: responseMessage }
Now the function has proper return values for both correct and incorrect invocations.
📝 Tip - This solution hard codes the HTTP status codes. To make the handling more consistent you can either define your own enumerations for the HTTP codes or use an npm package like http-status-codes.
-
Run the function, once without name value in the query string, and once with a name value.
❔ Question - Is the outcome of both runs as expected?
Let's change the function to also allow POST requests and test it by posting a request with JSON content in the request body.
-
Add the
"post"
entry in the"methods"
array in thefunction.json
file. -
The function in the
index.ts
file currently only handles GET requests. We need to add some logic to use the query string for GET and the request body for POST requests. This can be done by checking the method property of the request parameter as follows:if (req.method === "GET"){ // Get name from query string // name = ... } else if (req.method === "POST"){ // Get name from body // name = ... }
📝 Tip - The code shows the HTTP verbs as strings. However, due to the declaration of the type in the request interface the validity is checked during design time as well as ode completion is available in the editor. This makes sure that you use valid values.
-
We will assign values to the variable
name
variable depending on the HTTP method. Therefore change the declaration of thename
variable tolet name: string
and remove the assignment of an from the request. -
Move the query string logic inside the
if
statement that handles the GET request. Leave the creation of the result object outside of theif ... else if
statement as this can be used for both branches. Theif
-branch handling GET requests should look like this:if (req.method === "GET") { name = req.query.name }
-
Now let's add the code to extract the name from the body for a POST request. The
else if
-branch handling the POST request should look like this:else if (req.method === "POST") { name = (req.body && req.body.name) }
📝 Tip - We need to check if the body exists at all before we assign the value.
-
We also adopt the message in case of an invalid request to handle the two different error situations. The construction of the response object has the following form after our changes:
if (name) { responseStatus = 200 responseMessage = `Hello, ${name}. This HTTP triggered function executed successfully.` } else { responseStatus = 400 responseMessage = `Pass a name in the query string (GET request) or a JSON body with the attribute "name" (POST request) for a personalized response.` }
-
Now run the function and do a POST request and submit JSON content with a
name
attribute. If you're using the VSCode REST client you can use this in a .http file:POST http://localhost:7071/api/HelloWorldHttpTrigger Content-Type: application/json { "name": "Your name" }
❔ Question - Is the outcome of the POST as expected?
❔ Question - What is the response when you use an empty
name
property?
In contrast to C# the typing of the request parameter of the function is restricted to string
, HttpRequest
and buffer
, so we cannot have a type-safety on this level of the function. Nevertheless we can make use of implicitly assigning the JSON body of the request to an interface which gives us consistent typing at design time.
-
Copy & paste the folder of the Azure Function from the exercise above and give it a new name e.g.
CustomGreetingHttpTrigger
.📝 Tip - Function names need to be unique within a Function App.
-
Adjust the
"scriptfile"
attribute in thefunction.json
file to the new filename to get a consistent transpilation. -
Remove the
"get"
entry from the"methods"
array in thefunction.json
file. Now the function can only be triggered by a POST request. -
Switch to the the
index.ts
file and add a new interface namedPerson
to theindex.ts
file.interface Person { name: string }
-
Remove the logic inside the function which deals GET Http verb and with the query string.
-
Rewrite the function logic that the request body is assigned to the interface
Person
.const person: Person = req.body
-
Update the logic which checks if the
name
variable is empty. You can now useperson.Name
instead. However, be aware that the request body can be empty which would result in an undefined assignment of the attributename
in theif
statement, so we must still check that the person is not undefined. The updated code should look like this:let responseMessage: string let responseStatus: number if (person && person.name) { responseMessage = `Hello, ${person.name}. This HTTP triggered function executed successfully.` responseStatus = 200 } else { responseMessage = `Pass a name in the request's JSON body with the attribute "name" (POST) for a personalized response.` responseStatus = 400 } context.res = { status: responseStatus, body: responseMessage }
-
Run the function.
🔎 Observation You should see two HTTP endpoints in the output of the console.
-
Trigger the new endpoint by making a POST request.
❔ Question Is the outcome as expected?
Ready to get hands-on? Checkout the homework assignment for this lesson.
For more info about the HTTP Trigger have a look at the official Azure Functions HTTP Trigger documentation.
We love to hear from you! Was this lesson useful to you? Is anything missing? Let us know in a Feedback discussion post here on GitHub.