user4316384
user4316384

Reputation:

Execute Sequelize queries synchronously

I am building a website using Node.js and Sequelize (with a Postgres backend). I have a query that returns many objects with a foreign key, and I want to pass to the view a list of the objects that the foreign key references.

In the example, Attendances contains Hackathon keys, and I want to return a list of hackathons. Since the code is async, the following thing of course does not work in Node:

models.Attendance.findAll({
    where: {
        UserId: req.user.id
    }
}).then(function (data) {
    var hacks = [];
    for (var d in data) {
        models.Hackathon.findOne({
            where: {
                id: data[d].id
            }
        }).then(function (data1) {
            hacks.append(data1);
        });
    }
    res.render('dashboard/index.ejs', {title: 'My Hackathons', user: req.user, hacks: hacks});
});

Is there any way to do that query in a synchronous way, meaning that I don't return the view untill I have the "hacks" list filled with all the objects?

Thanks!

Upvotes: 8

Views: 12113

Answers (3)

Immediately invoke asynchronous function expression

This is one of the techniques mentioned at: How can I use async/await at the top level? Toplevel await is likely coming soon as of 2021, which will be even better.

Minimal runnable example:

const assert = require('assert');
const { Sequelize, DataTypes } = require('sequelize');

const sequelize = new Sequelize({
  dialect: 'sqlite',
  storage: 'db.sqlite',
});
const IntegerNames = sequelize.define(
  'IntegerNames', {
  value: { type: DataTypes.INTEGER, allowNull: false },
  name: { type: DataTypes.STRING, },
}, {});

(async () => {
await IntegerNames.sync({force: true})
await IntegerNames.create({value: 2, name: 'two'});
await IntegerNames.create({value: 3, name: 'three'});
await IntegerNames.create({value: 5, name: 'five'});

// Fill array.
let integerNames = [];
integerNames.push(await IntegerNames.findOne({
  where: {value: 2}
}));
integerNames.push(await IntegerNames.findOne({
  where: {value: 3}
}));

// Use array.
assert(integerNames[0].name === 'two');
assert(integerNames[1].name === 'three');

await sequelize.close();
})();

Tested on Node v14.16.0, sequelize 6.6.2, seqlite3 5.0.2, Ubuntu 20.10.

Upvotes: 0

buycanna.io
buycanna.io

Reputation: 1204

The Sequelize library has the include parameter which merges models in one call. Adjust your where statement to bring the Hackathons model into Attendance. If this does not work, take the necessary time to setup Sequelize correctly, their documentation is constantly being improved. In the end, you'll save loads of time by reducing error and making your code readable for other programmers.

Look how much cleaner this can be...

models.Attendance.findAll({
    include: [{
        model: Hackathon,
        as: 'hackathon'
    },
    where: {
        UserId: req.user.id
    }
}).then(function (data) {
    // hackathon id
    console.log(data.hackathon.id)

    // attendance id
    console.log(data.id)
})

Also..

Hackathon.belongsTo(Attendance)
Attendance.hasMany(Hackathon)
sequelize.sync().then(() => {
  // this is where we continue ...
})

Learn more about Sequelize includes here: http://docs.sequelizejs.com/en/latest/docs/models-usage/

Upvotes: 3

synthet1c
synthet1c

Reputation: 6282

Use Promise.all to execute all of your queries then call the next function.

models.Attendance.findAll({
    where: {
        UserId: req.user.id
    }
}).then(function (data) {
    // get an array of the data keys, (not sure if you need to do this)
    // it is unclear whether data is an object of users or an array. I assume
    // it's an object as you used a `for in` loop
    const keys = Object.keys(data)
    // map the data keys to [Promise(query), Promise(query), {...}]
    const hacks = keys.map((d) => {
      return models.Hackathon.findOne({
        where: {
          id: data[d].id
        }
      })
    })
    // user Promise.all to resolve all of the promises asynchronously
    Promise.all(hacks)
      // this will be called once all promises have resolved so
      // you can modify your data. it will be an array of the returned values
      .then((users) => {
        const [user1, user2, {...}] = users
        res.render('dashboard/index.ejs', {
          title: 'My Hackathons', 
          user: req.user, 
          hacks: users
        });
      })
});

Upvotes: 6

Related Questions