Sequelize not retrieving all data after insert - node.js

I have noticed that my backend is not retrieving the expected data after an insert.
In my React application, I have one function which inserts data into the database and after getting a response, a new request is sent to update the current component state with the newly fetched data.
All my functions are using await/async and in the backend, all transactions are correctly used and committed in order.
My client is calling the following endpoints:
-POST: api/ticket ( INSERT AN ITEM)
-GET: api/ticket (GET ALL ITEMS)
Here is what the backend is showing which looks correct to me, the problem is that in the 'SELECT' statement, the inserted item is not retrieved.
The transactions are started from two different routes but I don't see why it should be an issue.
In addition, I tried to change the AddItem function to output the same findAll statement which is called when using the GET method and the data returned are correct.
So why if I separate these two flows I do not get all the items? I always need to refresh the page to get the added item.
START TRANSACTION;
Executing (a9d14d5c-c0ac-4821-9b88-293b086debaa): INSERT INTO `messages` (`id`,`message`,`createdAt`,`updatedAt`,`ticketId`,`userId`) VALUES (DEFAULT,?,?,?,?,?);
Executing (a9d14d5c-c0ac-4821-9b88-293b086debaa): COMMIT;
Executing (9ee9ddaa-294e-41d1-9e03-9f02a2737030): START TRANSACTION;
Executing (9ee9ddaa-294e-41d1-9e03-9f02a2737030): SELECT `ticket`.`id`, `ticket`.`subject`, `ticket`.`status`, `ticket`.`createdAt`, `ticket`.`updatedAt`, `ticket`.`deletedAt`, `ticket`.`userId`, `messages`.`id` AS `messages.id`, `messages`.`message` AS `messages.message`, `messages`.`sender` AS `messages.sender`, `messages`.`createdAt` AS `messages.createdAt`, `messages`.`updatedAt` AS `messages.updatedAt`, `messages`.`deletedAt` AS `messages.deletedAt`, `messages`.`ticketId` AS `messages.ticketId`, `messages`.`userId` AS `messages.userId`, `messages->user`.`id` AS `messages.user.id`, `messages->user`.`firstname` AS `messages.user.firstname`, `messages->user`.`surname` AS `messages.user.surname`, `messages->user`.`email` AS `messages.user.email`, `messages->user`.`password` AS `messages.user.password`, `messages->user`.`stripeId` AS `messages.user.stripeId`, `messages->user`.`token` AS `messages.user.token`, `messages->user`.`birthDate` AS `messages.user.birthDate`, `messages->user`.`status` AS `messages.user.status`, `messages->user`.`confirmationCode` AS `messages.user.confirmationCode`, `messages->user`.`createdAt` AS `messages.user.createdAt`, `messages->user`.`updatedAt` AS `messages.user.updatedAt`, `messages->user`.`deletedAt` AS `messages.user.deletedAt` FROM `tickets` AS `ticket` LEFT OUTER JOIN `messages` AS `messages` ON `ticket`.`id` = `messages`.`ticketId` AND (`messages`.`deletedAt` IS NULL) LEFT OUTER JOIN `users` AS `messages->user` ON `messages`.`userId` = `messages->user`.`id` AND (`messages->user`.`deletedAt` IS NULL) WHERE (`ticket`.`deletedAt` IS NULL);
Executing (9ee9ddaa-294e-41d1-9e03-9f02a2737030): COMMIT;
-- POST '/api/ticket
exports.addMessage = async (req, res) => {
try {
const result = await sequelize.transaction(async (t) => {
var ticketId = req.body.ticketId;
const userId = req.body.userId;
const message = req.body.message;
const subject = req.body.subject;
// Validate input - If new ticket, a subject must be provided
if (!ticketId && !subject) {
return res
.status(400)
.send({ message: "New ticket must have a subject" });
}
// Validate input - If ticket exists, userId and message must be provided
if (!userId && !message && ticketId) {
return res
.status(400)
.send({ message: "UserID and message are required" });
}
// Create ticket is no ticketID was provided
if (!ticketId) {
const [ticket, created] = await Ticket.findOrCreate({
where: {
subject: subject,
userId: userId,
},
transaction: t,
});
ticketId = ticket.id;
}
// Create a new message object
const messageObject = await db.message.create(
{
message: message,
userId: userId,
ticketId: ticketId,
},
{ transaction: t }
);
// Output message object
return res.send(messageObject);
});
} catch (err) {
console.log(err);
return res.status(500).send({
message:
err.message || "Some error occurred while creating the ticket message.",
});
}
};
-- GET: api/ticket
exports.findAll = async (req, res) => {
try {
const result = await sequelize.transaction(async (t) => {
const tickets = await db.ticket.findAll(
{
include: [{ model: db.message, include: [db.user] }],
},
{ transaction: t }
);
tickets.forEach((ticket) => {
console.log(JSON.stringify(ticket.messages.length));
});
return res.send(tickets);
});
} catch (err) {
console.log(err);
res.status(500).send({
message: err.message || "Some error occurred while retrieving Tickets.",
});
}
};

You sent a response to a client before the transaction actually was committed. You just need to move res.send(messageObject); outside the transaction call.
You can try to look what's going on in the current version of your code if you add several console.log with messages to see what the actual order of actions is (I mean a couple of messages in POST (the last statement inside transaction and after transaction before res.send) and at least one at the beginning of GET).
Actually if the transaction was rolled back you'd send an uncommited and already removed object/record that I suppose is not your goal.

Related

Query was already executed, MoongoseError

I am making a transaction controller in NodeJS but when I send data through postman I get this error:
MongooseError: Query was already executed: Customer.updateOne({ name: 'Axel' }, { '$set': { balance: 98...
at model.Query._wrappedThunk [as _updateOne] (C:\Users\m4afy\Desktop\the spark foundation\Banking system\node_modules\mongoose\lib\helpers\query\wrapThunk.js:23:19)
at C:\Users\m4afy\Desktop\the spark foundation\Banking system\node_modules\kareem\index.js:494:25
at process.processTicksAndRejections (node:internal/process/task_queues:77:11) {
originalStack: 'Error\n' +
' at model.Query._wrappedThunk [as _updateOne] (C:\\Users\\m4afy\\Desktop\\the spark foundation\\Banking system\\node_modules\\mongoose\\lib\\helpers\\query\\wrapThunk.js:27:28)\n' +
' at C:\\Users\\m4afy\\Desktop\\the spark foundation\\Banking system\\node_modules\\kareem\\index.js:494:25\n' +
' at process.processTicksAndRejections (node:internal/process/task_queues:77:11)'
}
my Transaction code goes as follow:
const Transaction = require('../models/transaction')
const Customer = require('../models/customers')
const cashTransaction = async (req, res, next) => {
const {from, to, amount} = req.body
try {
let sender = await Customer.findOne({'name' : `${from}`})
let senderBalance = Number(sender.balance) - Number(amount)
await Customer.updateOne({name : from}, {balance : senderBalance}, err =>{
if (err){
console.log(err)
res.status(500).send('Could not update sender information')
} else {
console.log('Sender information updated');
}
})
let receiver = await Customer.findOne({name : to})
let receiverBalance = Number(receiver.balance) + Number(amount)
await Customer.updateOne({name : to}, {balance : receiverBalance}, err =>{
if (err){
console.log(err);
res.status(500).send('Could not update receive ver information')
} else{
console.log('receiver information updated');
}
})
const transaction = new Transaction({
from,
to,
amount
})
await transaction.save()
res.status(200).json({transaction : {transaction} , message : 'transaction saved'})
} catch (error) {
console.log(error);
res.status(500).send('An Error occured');
}
}
how can I update it multiple times?
It worked one time but then am getting this error, any help?
Using await and a callback simultaneously will result in the query executing twice.
The Model.updateOne method returns a query object. Passing a callback function causes the query to be immediately executed and then the callback is called. Await will likewise cause the query to be executed, and will return the result.
When you use both at the same time, both try to execute the query, but a specific instance of a query can only be executed once, hence the error.
You might try using await inside of a try/catch instead of a callback.
Each call to updateOne instantiates a new query object, so you should be able to do both updates

deleteMany only returns 1 value deleted in change streams

I have a deleteMany request but I am having a hard time in filtering my context of the deleteMany returned value. It only returns 1 value deleted from pusherjs.
Here is my change stream code and pusher code in server side;
if (schedules.operationType === 'delete') {
const scheduleDetails = schedules.documentKey;
pusher.trigger('schedules', 'deleted', {
_id: scheduleDetails._id,
teamOne: scheduleDetails.teamOne,
teamTwo: scheduleDetails.teamTwo,
user: scheduleDetails.user,
isDone: scheduleDetails.isDone,
isStarted: scheduleDetails.isStarted,
date: scheduleDetails.date,
gameEvent: scheduleDetails.gameEvent,
});
}
Here is my pusher code in client side. I am using React by the way. It is stored in my context api;
ScheduleChannel.bind('deleted', ({ deletedSchedule }) => {
console.log(deletedSchedule);
setScheduleList(
scheduleList.filter((schedule) => schedule._id !== deletedSchedule._id)
);
});
here is my code on request;
exports.deleteallmatch = async (req, res) => {
try {
const { sub } = req.user;
const deletedMatches = await Schedule.deleteMany({ user: sub });
return res.status(201).json({
message: 'All of your schedule is successfully deleted!',
deletedMatches,
});
} catch (err) {
return res.status(400).json({
message: 'Something went wrong.',
});
}
};
The delete request is fine but I want to have realtime in my app. Cuz it happened that only one data is being send instead of many. How can I solve this?
The deleteMany() method returns an object that contains three fields:
n – number of matched documents
ok – 1 if the operation was successful
deletedCount – number of deleted documents
What you can do is:
First find all elements that match your query
Store them in some variable
Perform deleting
Return the stored variable
let deleted_items = await Schedule.find({ user: sub });
await Schedule.deleteMany({ user: sub });
return res.status(201).json({
message: 'All of your schedule is successfully deleted!',
deleted_items,
});

Transaction error using node-mssql library

Hello I'm using the mssql library to insert some data into an SQL Server DB, the call is being executed inside a queue so if it fails it will retry in 1 min * retryAttemp up until 5 attempts
however I keep getting this weird behavior where I get this error message:
ERROR: Error inserting into BASIS OLAP TransactionError: Can't acquire connection for the request. There is another request in progress.
and I don't know why this is happening... this is my code:
static async insertPresale(req, res) {
logger.info('Inserting new Pre-Sale');
const { CAB, DET } = req.body.basisOlapStructure;
const { country } = req.body;
if (country.toLowerCase() !== 'cl') return res.status(404).json({ success: false, message: 'Other countries are not currently supported' });
let pool;
let transaction;
try {
pool = await Databases.ClSQLServerPreventa;
if (!pool) throw new errors.UNEXPECTED_ERROR({ message: 'Error inserting pre-sale in BASIS OLAP. Could not get a connection pool' });
transaction = new mssql.Transaction(pool);
await new Promise((resolve, reject) => transaction.begin(err => (err ? reject(err) : resolve())));
const request = new mssql.Request(transaction);
logger.info('Inserting data into PVRCabecera');
let queryStr = msql.insert().into(bTables.PVRCabecera.toString()).setFields(CAB);
logger.info(queryStr.toString());
let result = await request.query(queryStr.toString());
if (result.rowsAffected > 0) logger.info('Data successfully inserted into PVRCabecera');
logger.info('Inserting data into PVRDetalle');
const queries = DET.map((d) => {
queryStr = msql.insert().into(bTables.PVRDetalle.toString()).setFields(d);
logger.info(queryStr.toString());
return request.query(queryStr.toString());
});
result = await Promise.all(queries);
if (result[0].rowsAffected > 0) logger.info('Data successfully inserted into PVRDetalle');
await transaction.commit();
logger.info('BASIS_OLAP Transaction completed successfully');
return res.status(201).json({ success: true });
} catch (error) {
logger.error(`Error inserting into BASIS OLAP ${error}`);
if (transaction) {
await transaction.rollback();
}
logger.info('Transaction has been rolled back');
throw new errors.UNEXPECTED_ERROR({ message: `Error inserting pre-sale in BASIS OLAP. ${error.toString()}` });
}
}
what can I do to solve this problem?

Express application cannot get certain item from my database (Sqlite)

I am creating an application in which users can create posts and comment on these. Creating, updating and deleting posts works as intended, and so does creating comments.
When the user creates a comment, its accountId is passed to the database.
When deleting a specific comment, the accountId is passed to verify that the user is allowed to delete it.
The problem is, it seems like the accountId isn't fetched from the database, though the query asks for all details from the database table called "comments".
The app is divided into two files, db.js, and app.js.
I have tried modifying the request. In order to troubleshoot, I added a line of code checking if the comment.accountId was fetched, but that is where I get the error.
/* in db.js: */
//get comment by comment id
exports.getCommentById = (id, callback) => {
const query = 'SELECT * FROM comments WHERE id = ?'
const values = [ id ]
db.all(query, values, (error, comment) => {
if (error) {
console.log(error)
callback(['databaseError'])
return
} else if (!comment) {
console.log(error)
callback(['notFound'])
return
} else {
callback([], comment)
}
})
}
/* in app.js */
app.delete('/comments/:commentId', (req, res, next) => {
const commentId = req.params.commentId
db.getCommentById(commentId, (errors, comment) => {
if (errors.length > 0) {
res.status(500).json({
message: 'serverError'
}).end()
return
} else if (!comment) {
res.status(404).json({
message: 'notFound'
}).end()
return
}
const accountId = req.accountId //from my auth middleware
const commAccId = comment.accountId
if(!commAccId) {
console.log(accountId)
console.log(commAccId)
res.status(404).json({
message: 'AccIdNotFound'
}).end()
return
}
- - - - - ^ this is the error checking I inserted, and this is where the error is thrown, so it seems like the id is just not found.
if(!accountId) {
res.status(401).json({
message: 'notAuthenticated'
}).end()
return
} else if (comment.accountId != accountId) {
res.status(401).json({
message: 'notAuthorized'
}).end()
return
}
//plus code for deletion (will insert if it seems relevant, just ask)
})
})
The error message is "AccIdNotFound"
console.log returns 5 (same as the logged in user) and undefined
db.all delivers an array of rows, not just one row. You are assuming the result is a single comment only.
You should check result.length, then pull out result[0].

Perform side effects for mongoose/mongodb query

I need to query my database for users based on an array of emails and then execute a function for each result, I do this with eachAsync:
mongoose.model('User')
.find({email: {$in: ['foo#bar.com', 'bar#foo.com']}})
/* -- Run side effects before continuing -- */
.cursor()
.eachAsync((doc) => {
// do stuff
});
The problem I'm having is that I need to return a 404 status if any of the users with the given emails do not exist.
I've been looking through the mongoose docs but I can't seem to find a way of running "side effects" when working with queries. Simply "resolving" the DocumentQuery with .then doesn't work since you can't turn it into a cursor afterwards.
How can I achieve this?
You could try implementing it as shown below. I hope it helps.
// Function using async/await
getCursor: async (_, res) => {
try {
const result = []; // To hold result of cursor
const searchArray = ['foo#bar.com', 'bar#foo.com'];
let hasError = false; // to track error when email from find isn't in the array
const cursor = await mongoose.model('User').find({ email: { $in: searchArray } }).cursor();
// NOTE: Use cursor.on('data') to read the stream of data passed
cursor.on('data', (cursorChunk) => {
// NOTE: Run your side effect before continuing
if (searchArray.indexOf(cursorChunk.email) === -1) {
hasError = true;
res.status(404).json({ message: 'Resource not found!' });
} else {
// Note: Push chunk to result array if you need it
result.push(cursorChunk);
}
});
// NOTE: listen to the cursor.on('end')
cursor.on('end', () => {
// Do stuff or return result to client
if (!hasError) {
res.status(200).json({ result, success: true });
}
});
} catch (error) {
// Do error log and/or return to client
res.status(404).json({ error, message: 'Resource not found!' });
}
}

Resources