I have an async function which inserts some data into a database (using mariadb). This insert may fail due to a duplicate unique key, so it'll throw an error (and it actually does), but when I try to throw it again to catch it through the Promise, it doesn't work; it always seems to end in a successful case, even if it has thrown the error.
I tried changing the then/catch order, and I used reject(err); instead of throw err; but none of that works.
Here's the POST declaration:
router.post('/', function (req, res) {
var user = req.body || {};
createUser(user).then(() => {
res.status(201);
res.send('Created!'); // This is ALWAYS sent, with the error thrown or not
}).catch(err => {
console.log('thrown'); // This is never printed
res.status(500);
res.send('Failed');
});
});
And this is the create user function:
async function createUser(user) {
let conn;
try {
conn = await db.getConnection();
const res = await conn.query('INSERT INTO users VALUES (NULL, ?, ?)', [user.name, user.password]); // Shorter example
return res;
} catch (err) {
console.log('catched'); // This is printed
throw err; // This is run but nothing thrown
} finally {
if (conn) {
return conn.end(); // This is run after catching
}
}
}
The idea would be to get that exception caught by the Promise so I can send an error message instead of a success.
The problem is with your return statement inside your finally. In an async function after throwing an exception if you catch it throw finally and return something, instead of throwing it resolves the promise into your returned value. From what I see you do not need the ended connection's object as the return value which means all you have to do is to change your function to this:
async function createUser(user) {
let conn;
try {
conn = await db.getConnection();
const res = await conn.query('INSERT INTO users VALUES (NULL, ?, ?)', [user.name, user.password]); // Shorter example
return res;
} catch (err) {
console.log('catched'); // This is printed
throw err; // This is run but nothing thrown
} finally {
if (conn) {
conn.end(); // This is run after catching
}
}
}
Related
I'm trying to use database transaction to create a Page record however I'm getting Variable 'createdPage' is used before being assigned even though this.pagesService.create() only returns Page and it will throw error if something goes wrong so program can be sure that createdPage is set if no exception is thrown. So why I'm getting this error?
#Post('')
async create(
#Body() body: PageCreateDto,
): Promise<Page> {
let createdPage: Page;
try {
await this.database.transaction(async trx => {
createdPage = await this.pagesService.create(body, trx);
});
} catch (error) {
throw new InternalServerErrorException('unable to create page');
}
return createdPage;
}
The problem is that the function you pass into the transaction call doesn't get run synchronously and so you can't be sure that createdPage is actually assigned when you return it. You could solve this by creating a promise.
#Post('')
async create(#Body() body: PageCreateDto): Promise<Page> {
return new Promise<Page>((resolve, reject) => {
try {
await this.database.transaction(trx => this.pagesService
.create(body, trx)
.then(resolve));
} catch (error) {
reject(new InternalServerErrorException('unable to create page'));
}
});
}
Returning it inside arrow function solved the issue:
#Post('')
async create(
#Body() body: PageCreateDto,
): Promise<Page> {
let createdPage: Page;
try {
createdPage = await this.database.transaction(async trx => {
return this.pagesService.create(body, trx);
});
} catch (error) {
throw new InternalServerErrorException('unable to create page');
}
return createdPage;
}
let { errors } = otherValdations(data);
withDB(async (db) => {
return Promise.all([
..code...
]).then(() => {
return {
errors,
isValid: isEmpty(errors),
}
})
}, res).then((result) => {
console.log(result);
})
How can I get 'result' variable to be the value of the object returned in promise.all? This is the code for withDB function:
const withDB = async (operations, res) => {
try {
const client = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true });
const db = client.db('app');
await operations(db);
client.close();
} catch (error) {
res.status(500).json({ message: 'Error connecting to db', error});
}
};
You need to modify withDB() so that it returns the value you want:
const withDB = async (operations, res) => {
try {
const client = await MongoClient.connect('mongodb://localhost:27017', { useNewUrlParser: true });
const db = client.db('app');
let result = await operations(db);
client.close();
return result;
} catch (error) {
res.status(500).json({ message: 'Error connecting to db', error});
throw error;
}
}
In your catch() handler, you also need to do something so that your calling code can distinguish the error path where you've already sent an error response from the case where you're resolved with the value. I don't know exactly how you want that to work, but I put in a throw error so that it will reject the returned promise and the caller can see that.
I notice from your error handling that you are assuming all possible errors are causing by an error connecting to the DB. That is not the case here. If operations(db) rejects, that will also hit your catch.
Promise.all returns an array with the results. So you either have to loop over the results or access them directly by supplying an index.
Hi guys I was working on a Async Function and I want it to return a name that I get from some other API calls.
My code are as below:
apiAccessor.ts
async registerName(): Promise<any>{
try{
await client.register((error, result) => {
if(error){
console.error(`Error register name: ${error}`);
}
else{
console.log('Registered Successfully!');
this.assignedPlace = result.assignedPlace;
console.log(this.assignedPlace);
}
});
return this.assignedPlace;
}
catch (error) {
this.logger.error(error.name, error.message);
}
main.ts
async function nameAssigner() {
let place = await client.registerName();
console.log(place);
}
nameAssigner();
The problem is, the console.log in main.ts will always run first (even if I put the console.log inside then promise callback) and giving me undefined value while after some time the code in apiAccessor.ts had finished, the console.log in apiAccessor.ts gave me the correct value I want. However this does not seems to be returned to the main.ts as expected.
How can I return the value or let the main.ts wait for the function to run complete first?
Thanks for the reads and appreciates if anyone could help!
Your problem is that client.register does not return a promise, it takes a call back function that is run internally but does not return a promise. You need to create a promise and resolve it in your callback function and then await the result from the promise.
async registerName(): Promise<any>{
try{
let promise = new Promise(function(resolve, reject) {
client.register((error, result) => {
if(error){
reject(`Error register name: ${error}`);
}
else{
console.log('Registered Successfully!');
resolve(result.assignedPlace);
console.log(this.assignedPlace);
}
});
});
this.assignedPlace = await promise;
return this.assignedPlace;
}
catch (error) {
this.logger.error(error.name, error.message);
}
From your code it looks like client.register((error, result) => { uses a callback and is not returning a Promise. Therefore your await in front of it doesn't do what you think. You can wrap your call to client.resolve with a Promise and only resolve/reject it once the callback gives you the result.
return new Promise((resolve,reject) => {
try {
client.register((error, result) => {
if(error){
console.error(`Error register name: ${error}`);
return reject(error);
}
else{
console.log('Registered Successfully!');
return resolve(result.assignedPlace);
}
});
}
catch (error) {
this.logger.error(error.name, error.message);
return reject(error);
}
});
So when running a query using Objection.js, the query will return data based on success or failure of said query and this data is passed to the then() block as a 0 or 1. Meaning to error handle, I'm having to check falsey values rather than send a response in the catch block. Am I doing something wrong?
const editIndustry = async (req, res, next) => {
const industry = await Industry.query().findById(req.params.industryId);
if (!industry) {
return res.status(404).json({
error: 'NotFoundError',
message: `industry not found`,
});
}
await industry
.$query()
.patch({ ...req.body })
.then(result => console.log(result, 'then() block'))
// never runs
.catch(err => {
console.log(err);
next(err);
});
};
App is listening on port 3000.
1 then() block ran
Your code is working as expected. The reason it's not going into the catch block is because there isn't an error. patch does not return the row. It returns the number of rows changed (see docs).
The function I think you're really looking for is patchAndFetchById (see docs). If you're concerned about generating a 404 error, you can append throwIfNotFound. Obviously, this will throw if it's not found in the database, which will let you catch. You can catch an instance of this error so you can send a proper 404 response. Otherwise, you want to return a 500. You'd need to require NotFoundError from objection.
const { NotFoundError } = require('objection');
const Industry = require('<myIndustryModelLocation>');
const editIndustry = (req, res) => {
try {
return Industry
.query()
.patchAndFetchById(req.params.industryId, { ...req.body })
.throwIfNotFound();
} catch (err) {
if(err instanceof NotFoundError) {
return res.status(404).json({
error: 'NotFoundError',
message: `industry not found`,
});
}
return res.status(500);
}
};
I have a mysql statement that creates an entry, it has a .then function and a .catch function, but when the following error occurs:
TypeError('Bind parameters must not contain undefined. To pass SQL NULL specify JS null');
the server crashes instead of answering a 500 like defined in the .catch function
Note: I'm using the mysql2 library from npm with promises (require('mysql2/promise');)
Here's the code that calls it (req.params.account_name is undefined):
const CREATE_ACCOUNT_STATEMENT =
'INSERT INTO `Accounts` (`account_token`, `account_name`) VALUES (?, ?)'
try {
mysqlConnectionPool.execute(CREATE_ACCOUNT_STATEMENT, [
account_token, account_name
])
.then(() => {
res.end(JSON.stringify({ token: account_token }))
})
.catch((e) => {
debug(1, "error while trying to create account:", e)
res.status(500).end("Internal Server Error")
})
} catch(e) {
debug(1, "error while trying to create account:", e)
res.status(500).end("Internal Server Error")
}
Actually, #Quentine was close to the right thing...
It is "sort of" a bug in mysql2,
i use sort-of because https://github.com/sidorares/node-mysql2/issues/902 suggests the development team of mysql2 is o.k. with it.
it is an issue with the way mysql2.pool passes the call to the created connection, which does not pass the exception to the wrapping promise.
I ended up making my own wrapping function to create the connection + call execute wrapped in proper promise handling.
import mysql = require('mysql2');
private async queryDB(query:string, useExecute: boolean = false, ...args:any[]) : Promise<any[]>
{
return new Promise<any[]>((resolve, reject)=>{
for(var i = 0; i < args.length; ++i)
{
if(args[i]===undefined)
args[i] = null;
}
this.dbPool.getConnection((err, conn)=>{
if(err){
reject(err);
return;
}
let cb = function(err: mysql.QueryError, results: any[], fields: mysql.FieldPacket[]) {
conn.release();
if(err)
{
reject(err);
return;
}
resolve(results);
}
if(useExecute)
conn.execute(query, args, cb);
else
conn.query(query, args, cb);
});
});
}
mysqlConnectionPool.execute is throwing the exception before creating a promise.
i.e. the exception is not thrown from within the promise.
To catch it you would need to try {} catch (e) {} around the call to mysqlConnectionPool.execute.
Well,
I'm guessing that you are using the standard mysql package which it seems not supporting Promises, instead, it accepts a standard node callback function(err, results, fields) {} as an argument of the execute method.
So since you haven't defined a valid callback the script will just throw an exception.