Call a web API in a Node.js web application

In this article, you learn how to call a web API from your Node.js client web app using the access token you acquire in Acquire access token. The web API is protected by Microsoft Entra External ID. This article is the fourth and last part of a four-part guide series.

Prerequisite

Update code

  1. In your code editor, open routes/todos.js file, then add the following code:

        const express = require('express');
        const router = express.Router();
    
        const toDoListController = require('../controller/todolistController');
        const authProvider = require('../auth/AuthProvider');
        const { protectedResources } = require('../authConfig');
    
        // custom middleware to check auth state
        function isAuthenticated(req, res, next) {
            if (!req.session.isAuthenticated) {
                return res.redirect('/auth/signin'); // redirect to sign-in route
            }
    
            next();
        }        
        // isAuthenticated checks if user is authenticated
        router.get('/',isAuthenticated, authProvider.getToken(protectedResources.toDoListAPI.scopes.read),toDoListController.getToDos);
    
        router.delete('/', isAuthenticated,authProvider.getToken(protectedResources.toDoListAPI.scopes.write),toDoListController.deleteToDo);
    
        router.post('/',isAuthenticated,authProvider.getToken(protectedResources.toDoListAPI.scopes.write),toDoListController.postToDo);
    
        module.exports = router;
    

    This file contains express routes for create, read and delete resource in the protected API. Each route uses three middleware functions, which execute in that sequence:

    • isAuthenticated checks whether the user is authenticated.

    • getToken requests an access token. You defined this function earlier in Acquire access token. For example, the create resource route (POST request) requests an access token with read and write permissions.

    • Finally, the postToDo or deleteToDo getToDos methods handles the actual logic for manipulating the resource. These functions are defined in controller/todolistController.js file.

  2. In your code editor, open controller/todolistController.js file, then add the following code:

        const { callEndpointWithToken } = require('../fetch');
        const { protectedResources } = require('../authConfig');
    
        exports.getToDos = async (req, res, next) => {
            try {
                const todoResponse = await callEndpointWithToken(
                    protectedResources.toDoListAPI.endpoint,
                    req.session.accessToken,
                    'GET'
                );
                res.render('todos', { isAuthenticated: req.session.isAuthenticated, todos: todoResponse.data });
            } catch (error) {
                next(error);
            }
        };
    
        exports.postToDo = async (req, res, next) => {
            try {
                if (!!req.body.description) {
                    let todoItem = {
                        description: req.body.description,
                    };
    
                    await callEndpointWithToken(
                        protectedResources.toDoListAPI.endpoint,
                        req.session.accessToken,
                        'POST',
                        todoItem
                    );
                    res.redirect('todos');
                } else {
                    throw { error: 'empty request' };
                }
            } catch (error) {
                next(error);
            }
        };
    
        exports.deleteToDo = async (req, res, next) => {
            try {
                await callEndpointWithToken(
                    protectedResources.toDoListAPI.endpoint,
                    req.session.accessToken,
                    'DELETE',
                    req.body._id
                );
                res.redirect('todos');
            } catch (error) {
                next(error);
            }
        };
    

    Each of these functions collects all the information required to call an API. It then delegates the work to the callEndpointWithToken function and waits for a response. The callEndpointWithToken function is defined in the fetch.js file. For example, to create a resource in the API, the postToDo function passes an endpoint, an access token, an HTTP method and a request body to the callEndpointWithToken function and waits for a response. It then redirects the user to the todo.hbs view to show all tasks.

  3. In your code editor, open fetch.js file, then add the following code:

        const axios = require('axios');
    
        /**
         * Makes an Authorization "Bearer" request with the given accessToken to the given endpoint.
         * @param endpoint
         * @param accessToken
         * @param method
         */
        const callEndpointWithToken = async (endpoint, accessToken, method, data = null) => {
            const options = {
                headers: {
                    Authorization: `Bearer ${accessToken}`,
                },
            };
    
            switch (method) {
                case 'GET':
                    return await axios.get(endpoint, options);
                case 'POST':
                    return await axios.post(endpoint, data, options);
                case 'DELETE':
                    return await axios.delete(endpoint + `/${data}`, options);
                default:
                    return null;
            }
        };
    
        module.exports = {
            callEndpointWithToken,
        };
    

    This function makes the actual API call. Notice how you include the access token as the value of bearer token in the HTTP request header:

        //...        
        headers: {
            Authorization: `Bearer ${accessToken}`,
        }        
        //...
    
  4. In your code editor, open .env file, then add the following configuration:

        # Use this variable only in the development environment. 
        # Please remove the variable when you move the app to the production environment.
        NODE_TLS_REJECT_UNAUTHORIZED='0'
    

    The NODE_TLS_REJECT_UNAUTHORIZED='0' setting in your .env file instructs Node.js to ignore any SSL certificate errors, such as the self-signed certificate error.

  5. In your code editor, open the app.js file, then:

    1. Add the todo router by using the following code:

          var todosRouter = require('./routes/todos');
      
    2. Use the todo router by using the following code:

          app.use('/todos', todosRouter); 
      

Run and test web app and API

At this point, you're ready to call the web API from the client web app:

  1. Use the steps in Secure an ASP.NET web API article to start your web API app. Your web API is now ready to serve client requests.

  2. In your terminal, make sure you're in the project folder that contains your client web app such as ciam-sign-in-call-api-node-express-web-app, then run the following command:

    npm start
    

    Your client web app starts.

  3. Use the steps in Run and test sample web app and API to demonstrate how the client app calls the web API.

Next steps

You may want to: