Mastering the art of crafting and managing NodeJS promises with finesse

Currently, I am utilizing ExpressJS for handling APIs and heavily rely on promises for managing asynchronous requests such as external API calls and database queries. While this approach works well in most cases, it tends to clutter the code with numerous .then -> .catch statements when dealing with a large number of calls. To illustrate, consider the following snippet of a login API that involves API calls and database queries:

const loginUser = (req, res) => {
  const { body } = req;

  console.log('Login');

  if (!body) {
    return res
      .status(400)
      .json({ success: false, error: 'request body is empty' });
  }

  if (!body.authCode) {
    return res
      .status(400)
      .json({ success: false, error: 'authCode missing in the body' });
  }

  getLinkedInProfile(body.authCode)
    .then((linkedInProfile) => {
      User.findOne({ linkedInId: linkedInProfile.linkedInId })
        .then((user) => {
          if (user) {
            Token.findOne({ userId: user._id })
              .then((token) => {
                // Handle token logic here
              })
              .catch((err) => {
                // Error handling for finding token
              });
          } else {
            // User does not exist response
          }
        })
        .catch((err) => {
          // Database query error response
        });
    })
    .catch((err) => {
      // LinkedIn profile authentication error response
    });
};

If you have any suggestions or improvements to streamline this code, please feel free to share your insights!

Answer №1

When dealing with promises, creating custom error handling can involve some additional effort at each step of the process. To streamline this and reduce redundancy, a few strategies can be employed:

  1. Centralize error handling by sending all errors from one designated location.
  2. Allow errors to propagate to the top-level try/catch block for easier management.
  3. Customize properties on the Error object to define the desired final error response, facilitating easy interception and modification in a centralized manner.
  4. Develop a helper function dedicated to constructing these customized error messages.
  5. Log any actual exceptions or rejections for visibility into the original errors, particularly useful during server-side debugging.

To ensure a custom error is generated at every stage, thorough error catching mechanisms need to be put in place. Despite using await in certain instances, utilizing .catch() for custom error handling significantly simplifies the code structure. While some developers may not prefer combining await and .catch(), omitting .catch() will highlight the advantages of its usage and showcase its effectiveness in simplifying the process.

The employment of async and await further streamlines the code flow, making error propagation towards the main catch block more straightforward. By leveraging these functionalities and throwing errors as needed, consolidating error handling at the top level becomes effortless.

Below is an example of the aforementioned principles being implemented:

// Define a function to create and throw an error object with specific properties
function throwError(status, msg, e) {
    if (e) {
        // Log the original error before modifying it
        console.log(e);
    }
    const err = new Error(msg);
    err.json = { success: false, error: msg };
    err.status = status;
    throw err;
}

const loginUser = async (req, res) => {
    try {
        const { body } = req;

        console.log('Login');

        if (!body) {
            throwError(400, 'request body is empty');
        }

        if (!body.authCode) {
            throwError(400, 'authCode missing in the body');
        }

        const linkedInProfile = await getLinkedInProfile(body.authCode)
            .catch(err => throwError(500, 'Unable to query database', err));

        const user = await User.findOne({ linkedInId: linkedInProfile.linkedInId })
            .catch(err => throwError(500, 'Unable to authenticate linkedIn profile', err));

        if (!user) {
            throwError(200, 'User does not exist');
        }
        const token = await Token.findOne({ userId: user._id })
            .catch(err => throwError(500, 'Error in find token', err));

        // Store token if not in DB
        if (!token) {
            const t = util.refreshToken(user._id); // Encrypt information
            Token.create({ refreshToken: t, userId: user._id });
        }

        // Send access token
        const accessToken = util.accessToken(user._id);
        res.json({
            success: true,
            message: 'Login Successful',
            token: accessToken,
            user: {
                _id: user._id,
                userType: user.userType,
            },
        });

    } catch (e) {
        // Send aggregated error response incorporating prior errors
        let status = e.status | 500;
        let json = e.json | { success: false, error: e.message };
        res.status(status).json(json);
    }
}

Answer №2

Employ async/await within a try/catch block

const loginUser = async (req, res) => {
const { body } = req;

console.log('Login');

if (!body) {
  return res
    .status(400)
    .json({ success: false, error: 'request body is empty' });
}

if (!body.authCode) {
  return res
    .status(400)
    .json({ success: false, error: 'authCode missing in the body' });
}
try{
const linkedInProfile = await getLinkedInProfile(body.authCode)
.
.
.
}catch(err){
  console.log(err);
  return res.status(500).json({
    success: false,
    error: 'Unable to authenticate linkedIn profile',
  });

}

Explore more about Async/await here: https://javascript.info/async-await

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Encountered an error while executing findByIdAndRemove operation

Could someone please assist in identifying the issue with the mongoose findByIdAndRemove function in the delete route provided below? //DELETE Route app.delete("/blogs/:id", function(req, res){ //Delete blog Blog.findByIdAndRemove(req.params.id, funct ...

Tips on invoking a scope function depending on an attribute's value

In my application, there are multiple 'save and close' links, each with a unique function triggered when clicked, specified by the directive ng-really-click. This directive confirms closure before executing the designated function. For example: ...

What is the best way to retrieve a png image using a Node.js Express app by sending a request to an API?

I am trying to retrieve a png image from an API endpoint using a nodejs express app. When attempting to fetch and display an image/svg file, everything works as expected. However, when I try the same with a png file, I receive poorly encoded text like thi ...

What is the best way to alternate between displaying HTML content with v-html and plain text in Vue.js?

I need a way to switch between v-html and plain text in Vue.js v2. Here's what I have so far: HTML <div id="app"> <h2 v-html="html ? text : undefined">{{html ? '' : text}}</h2> <button @click=&qu ...

Submitting data from a dropdown menu using Ajax in Struts 2: A step-by-step guide

I have a select tag with the s:select element inside a form. My goal is to send a post request to the specified action using Struts 2 json plugin. I do not have much knowledge in javascript or jquery. <s:form action="selectFileType" method="post" ...

How can a "Loading" message be displayed while external JavaScript is loading?

I have mastered the art of using JS or jQuery to showcase a "Loading" message during the loading process. Currently, I am working on a sizeable web application that relies on various JS dependencies, and I am seeking a way to exhibit a "loading" message wh ...

Troubleshooting tips for resolving issues when launching Nx React + Express

[webpack-dev-server] [HPM] Encountered an issue while forwarding request localhost:4200/api to http://localhost:3333/ [ECONNREFUSED] (https://nodejs.org/api/errors.html#errors_common_system_errors) While setting up my nx app with react and express, I face ...

Obtain and install NPM packages in an offline environment

As a newcomer to npm, I am facing the challenge of working with NPM packages like express, express-generator, ejs, mysql, and more on a server that does not have Internet access. This means that the simple command npm install express will not be effective ...

Angular with Firebase: How to ignore a field in a query

I am curious to see if my current structure is compatible with Firebase, or if I need to make adjustments. Let's take a look at an example using the "/rooms" endpoint, which contains an array of Room objects: export class Room { id: number; p ...

Deliver a personalized error notification using serverless-http (express) framework

I am trying to send a custom error message in JSON format from my express app, which is served in a lambda function using serverless-http To achieve this, it seems I need to utilize LAMBDA_PROXY APIG integration to enable sending custom error messages dir ...

Express Node is struggling to update user information

When a user logs in, their information is stored in req.user. They should be able to edit their details (first name, last name, email) using a form displayed with the /profile route as shown below: app.get('/profile', checkAuthenticated, (req ...

What is the method for populating a dropdown using ajax in the Jade template engine?

Looking to dynamically populate a dropdown based on the selection of another dropdown. Here's the code snippet: script. var b_name = []; function jsFunction() { var client = document.getElementById('drop_client'); var c_name = cli ...

Input a value to request in the Mssql node

I am using angular2 and mssql to establish a connection with SQL Server. This is my code snippet: var express = require('express'); var app = express(); var sql = require("mssql"); // database configuration var config = { user: 'sa&ap ...

The strict-origin-when-cross-origin policy is enforced when submitting a POST request through a form to a specific route

Currently, I am diving into the world of Node.js, express, and MongoDB by reading Greg Lims's book. However, I've hit a roadblock when trying to utilize a form to submit data to a route that should then output the body.title of the form in the co ...

Tips for interacting with a custom web component using Selenium WebDriver

As a newcomer to writing selenium tests, I am attempting to create an automated test for a carousel feature on our homepage. The objective is to click on one of the carousel navigation buttons and then confirm that a specific inline style has been applied ...

Troubleshooting: Issue with binding nested data property using bracket access in Vue3 v-model

Having an issue in Vue3 where I am unable to bind a nested property to v-model correctly. Check out the source code below: Template: <div id="app"> <span>{{level1.level2.level3}}</span> <br/> <span>{{level1[&ap ...

Customizing React-Data-Grid styles using Material-UI in a React application

Imagine a scenario where we have a file containing themes: themes.js: import {createMuiTheme} from "@material-ui/core/styles"; export const myTheme = createMuiTheme({ palette: { text: { color: "#545F66", }, }, }); In ...

Tips on increasing the height of an element that is overflowing

When populating my timeline component with dynamically mapped data from an array, I encountered an issue where if I added more data causing the maximum height to be reached, the overflow-y element didn't display all content. Despite trying various sol ...

Share a status on Facebook with an earlier date

How to Modify Facebook Post Date using Graph API facebook facebook-graph-api I am able to publish on my wall using the Graph API. By utilizing FB nod and graph, I now need to post on Facebook with a date from the past Afterwards, I can adjust the date man ...

The socket.emit() function I am using is malfunctioning

Recently, I've encountered an issue with the socket emit function in my React and node.js application. When a button is pressed, the goal is to save data on the backend and send it to another party in real-time for a chat feature. Below is the React ...