-1

I am developing the backend of an application using Node JS, Sequelize and Postgres database.

When the course is registered, the user must inform which organizations, companies and teachers will be linked to it.

The organization IDs are passed through an array to the backend, I am trying to do a check to make sure that the passed IDs exist.

What I've done so far is this:

const { organizations } = req.body;
const organizationsArray = organizations.map(async (organization) => {
  const organizationExists = await Organization.findByPk(organization);
  if (!organizationExists) {
    return res
      .status(400)
      .json({ error: `Organization ${organization} does not exists!` });
  }
  return {
    course_id: id,
    organization_id: organization,
  };
});
await CoursesOrganizations.bulkCreate(organizationsArray);

This link has the complete controller code, I believe it will facilitate understanding.

When !OrganizationExists is true, I am getting the return that the organization does not exist. The problem is when the organization exists, I am getting the following message error.

Fred
  • 182
  • 1
  • 22
  • 2
    Take a look at https://stackoverflow.com/questions/40140149/use-async-await-with-array-map or any other question about `.map` and async functions. – Aioros Oct 16 '20 at 14:08

2 Answers2

1

The Array.map() is returning an array of promises that you can resolve to an array using Promise.all(). Inside the map you should use throw new Error() to break out of the map - this error will be raised by Promise.all() and you can then catch it and return an error to the client (or swallow it, etc).

This is a corrected version of your pattern, resolving the Promise results.

const { organizations } = req.body;
try {
  // use Promise.all to resolve the promises returned by the async callback function
  const organizationsArray = await Promise.all(
    // this will return an array of promises
    organizations.map(async (organization) => {
      const organizationExists = await Organization.findByPk(organization, { 
        attributes: ['id'], // we only need the ID
        raw: true, // don't need Instances
      });
      if (!organizationExists) {
        // don't send response inside the map, throw an Error to break out
        throw new Error(`Organization ${organization} does not exists!`);
      }
      // it does exist so return/resolve the value for the promise
      return {
        course_id: id,
        organization_id: organization,
      };
    })
  );

  // if we get here there were no errors, create the records
  await CoursesOrganizations.bulkCreate(organizationsArray);

  // return a success to the client
  return res.json({ success: true });
} catch (err) {
  // there was an error, return it to the client
  return res.status(400).json({ error: err.message }); 
}

This is a refactored version that will be a bit faster by fetching all the Organizations in one query and then doing the checks/creating the Course inserts.

const { Op } = Sequelize;
const { organizations } = req.body;
try {
  // get all Organization matches for the IDs
  const organizationsArray = await Organization.findAll({
    attributes: ['id'], // we only need the ID
    where: {
      id: {
        [Op.in]: organizations, // WHERE id IN (organizations) 
      }
    },
    raw: true, // no need to create Instances
  });

  // create an array of the IDs we found
  const foundIds = organizationsArray.map((org) => org.id);

  // check to see if any of the IDs are missing from the results
  if (foundIds.length !== organizations.length) {
    // Use Array.reduce() to figure out which IDs are missing from the results
    const missingIds = organizations.reduce((missingIds, orgId) => {
      if (!foundIds.includes(orgId)){
        missingIds.push(orgId);
      }
      return missingIds;
    }, []); // initialized to empty array

    throw new Error(`Unable to find Organization for: ${missingIds.join(', ')}`);
  }

  // now create an array of courses to create using the foundIds
  const courses = foundIds.map((orgId) => {
    return {
      course_id: id,
      organization_id: orgId,
    }; 
  });

  // if we get here there were no errors, create the records
  await CoursesOrganizations.bulkCreate(courses);

  // return a success to the client
  return res.json({ success: true });
} catch (err) {
  // there was an error, return it to the client
  return res.status(400).json({ error: err.message }); 
}
doublesharp
  • 26,888
  • 6
  • 52
  • 73
-1

If you have an array of Ids and you want to check if they exist you should you use the (in) operator, this makes it so that you are hitting the DB only once and getting all the records in one hit (instead of getting them one by one in a loop), after you get these records you can check their lengths to determine if they all exist or not.

const { Op } = require("sequelize");
let foundOrgs = await Organization.findAll({
  where: {
    id: {
      [Op.in]: organizationsArray,
    }
  }
});