In NodeJS, can I create a promise and catch its failure later?

Viewed 23

I'm writing some code that grabs some result asynchronously and stores it until later, when another function needs it, sort of like eager loading it. (In my specific case I'm grabbing a secret from AWS Secrets manager, but whatever.) My code looks like this:

const secretPromise = new Promise((resolve, reject) => {
  try {
    const credentialsJSON = await new AWSController().getSecret("my-credentials");
    resolve(JSON.parse(credentialsJSON));
  } catch (e) {
    reject(new Error(`Error getting credentials: ${e.message}`));
  }
});

// in an expressJS route handler:
router.get("/groups", async (request, response) => {
  try {
    const credentials = await secretPromise;
    const result = await doSomething(credentials);
    response.send(JSON.stringify(result));
  } catch (error){
    response.status(503).send(`Error: ${error.message)`);
  }
});

What I didn't realize was that if there's no handler for this promise, it will throw an error and crash my nodeJS code immediately. Is there a way to store the result or error and handle it later? I don't want my server to crash just because one API function won't work.

2 Answers

You create the secretPromise early, outside of the route handler. It may therefore be rejected before the route handler is called, when there is no try-catch block that would catch that rejection.

A rejected secretPromise causes every subsequent GET /groups request to fail, you could handle this without a rejection:

const secretPromise = new Promise((resolve, reject) => {
  try {
    const credentialsJSON = await new AWSController().getSecret("my-credentials");
    resolve(JSON.parse(credentialsJSON));
  } catch (e) {
    resolve({error: new Error(`Error getting credentials: ${e.message}`)});
  }
});

// in an expressJS route handler:
router.get("/groups", async (request, response) => {
  try {
    const credentials = await secretPromise;
    if (secretPromise.error) throw secretPromise.error;
    const result = await doSomething(credentials);
    response.send(JSON.stringify(result));
  } catch (error){
    response.status(503).send(`Error: ${error.message)`);
  }
});

The await inside secretPromise is not within an async function therefore the code isn't valid. Also there's no need to wrap it in a Promise.

To eagerly fetch the credentials before the API is called, I would simply write it like this:

let credentials;
try {
  const credentialsJSON = await new AWSController().getSecret("my-credentials");
  credentials = JSON.parse(credentialsJSON);
} catch (e) {
  credentials = new Error(`Error getting credentials: ${e.message}`);
 }
};


// in an expressJS route handler:
router.get("/groups", async (request, response) => {
  try {
    if (credentials instanceof Error) {
      throw credentials;
    }

    const result = await doSomething(credentials);
    response.send(JSON.stringify(result));
  } catch (error){
    response.status(503).send(`Error: ${error.message)`);
  }
});
Related