Skip to content

Latest commit

 

History

History
271 lines (189 loc) · 12.6 KB

File metadata and controls

271 lines (189 loc) · 12.6 KB

HTTP Trigger (TypeScript)

Watch the recording of this lesson on YouTube 🎥.

Goal 🎯

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:

Nr Exercise
0 Prerequisites
1 Creating a Function App
2 Changing the template for GET requests
3 Changing the template for POST requests
4 Adding a new function for POST requests
5 Homework
6 More info
7 Feedback

📝 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.


0. Prerequisites

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.

1. Creating a Function App

In this exercise, you'll be creating a Function App with the default HTTPTrigger and review the generated code.

Steps

  1. In VSCode, create the Function App by running AzureFunctions: Create New Project in the Command Palette (CTRL+SHIFT+P).

  2. 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.

  3. Select the language you will be using to code the function. In this lesson we will be using TypeScript.

  4. Select HTTP trigger as the template.

  5. Give the function a name (e.g. HelloWorldHttpTrigger).

  6. 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?

  7. Install the dependencies defined in the package.json via npm install in a shell of your choice.

  8. Build the project by making use of the predefined script in the package.json file via npm 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.

  9. Start the Function App by pressing F5 or via npm run start.

    🔎 Observation - You should see an HTTP endpoint in the output.

  10. 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?

2. Changing the template for GET requests

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.

Steps

  1. Remove the "post" entry from the "methods"array in the function.json file. Now the function can only be triggered by a GET request.

  2. Switch to the file index.ts. Here we leave the req parameter unchanged. However, we will ignore the body parameter defined on the interface HttpRequest and only take the query parameter into account.

  3. Remove the content of the function (but keep the function definition). We'll be writing a new implementation.

  4. 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.

  5. Add an if statement to the function that checks if the name value is null, an empty string or undefined. 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.

  6. 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?

3. Changing the template for POST requests

Let's change the function to also allow POST requests and test it by posting a request with JSON content in the request body.

Steps

  1. Add the "post" entry in the "methods"array in the function.json file.

  2. 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.

  3. We will assign values to the variable name variable depending on the HTTP method. Therefore change the declaration of the name variable to let name: string and remove the assignment of an from the request.

  4. Move the query string logic inside the if statement that handles the GET request. Leave the creation of the result object outside of the if ... else if statement as this can be used for both branches. The if-branch handling GET requests should look like this:

    if (req.method === "GET") {
        name = req.query.name
    }
  5. 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.

  6. 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.`
    }
  7. 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?

4. Adding a new function for POST requests

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.

Steps

  1. 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.

  2. Adjust the "scriptfile" attribute in the function.json file to the new filename to get a consistent transpilation.

  3. Remove the "get" entry from the "methods"array in the function.json file. Now the function can only be triggered by a POST request.

  4. Switch to the the index.ts file and add a new interface named Person to the index.ts file.

     interface Person {
         name: string
     }
  5. Remove the logic inside the function which deals GET Http verb and with the query string.

  6. Rewrite the function logic that the request body is assigned to the interface Person.

     const person: Person = req.body
  7. Update the logic which checks if the name variable is empty. You can now use person.Name instead. However, be aware that the request body can be empty which would result in an undefined assignment of the attribute name in the if 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
    }
  8. Run the function.

    🔎 Observation You should see two HTTP endpoints in the output of the console.

  9. Trigger the new endpoint by making a POST request.

    Question Is the outcome as expected?

5. Homework

Ready to get hands-on? Checkout the homework assignment for this lesson.

6. More info

For more info about the HTTP Trigger have a look at the official Azure Functions HTTP Trigger documentation.

7. Feedback

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.


🔼 Lessons Index