Nodejs promises catch "hell" then using sequelize orm - node.js

Currently I developing web app for nodejs using popular sequelize orm and typesciprt. Here is a example from my code
this.createNewGame(player.idPlayer).then((game) => {
this.getBestScore(player.idPlayer).then((bestScore) => {
defer.resolve({
idGame: game.idGame,
bestScore: bestScore
});
}).catch((error) => { defer.reject(error); });
}).catch((error) => { defer.reject(error); });
Here is one of the method
private getBestScore(idPlayer: number): Q.Promise<number> {
var defer = this.q.defer<number>();
GameModel.max<number>('score', { where: { 'idPlayer': idPlayer } }).then((result) => {
defer.resolve(result);
}).catch((error) => { defer.reject(error); });
return defer.promise;
}
I use catch in every method implementation and also in every call to method. I would like to have only one catch block in my expressjs router. I tried code like this and it works just fine, here is example:
//code in GameService class ...
getData(): Q.Promise<number> {
var defer = this.q.defer<number>();
this.method1().then((result) => {
defer.resolve(result);
});
return defer.promise;
}
private method1(): Q.Promise<number> {
var defer = this.q.defer<number>();
throw 'some error occurs here';
return defer.promise;
}
//router call GameService
router.get('/error-test', (req: express.Request, res: express.Response) => {
gameService.getData().then((result) => {
res.json(result);
}).catch((error) => { res.send(error); });
//works fine, here I get my thrown error
});
But in my previous example I need to use catch blocks everywhere otherwise If I get Unhandled rejection SequelizeDatabaseError or any other Unhandled rejection, nodejs stops working. Why I can't use only one catch block in my expressjs router when using calls to db with sequalize, like in my first example?

Sequelize operations return promises, so there is no reason to put Q into the mix. This is equivalent
private getBestScore(idPlayer: number): Q.Promise<number> {
return GameModel.max<number>('score', { where: { 'idPlayer': idPlayer } });
}
It doesn't return a Q.Promise (sequelize uses bluebird), but the implementations should be interoperable, since they are both 'thenables'.

Related

wait for result of first function before handle second function

In my html front, the user can fill in the detail product information. When click on the button to save the data, the onSubmit function will be started. Here is my example:
onSubmit() {
this.projectService.createProject().subscribe({
next: data => {
console.log(data);
},
error: err => {
console.log(err);
}
});
this.projectService.createProjectDetails().subscribe({
next: data => {
console.log(data);
},
error: err => {
console.log(err);
}
})
}
in the projectService:
createProject(): Observable<any> {
return this.http.post(PROJECT_URL + "createProject", httpOptions);
}
createProjectDetails(): Observable<any> {
return this.http.post(PROJECT_URL + "createProjectDetails", httpOptions);
}
I dont show all the code but my question is, when there is an error in the createProject, the second function still will run while I dont want this. Can I put the createProjectDetails in the next of the createProject?
Since you seem to work on an Angular-app, you could use switchMap (from RxJs) in order to chain both request. In this case the second function will not run if the first one results in an error:
onSubmit() {
this.projectService.createProject().pipe(
switchMap(() => this.projectService.createProjectDetails())
).subscribe({
next: data => {
console.log(data);
},
error: err => {
console.log(err);
}
});
}
Alternatively you could use the catchError() operator instead of the error callback.
onSubmit() {
this.projectService.createProject().pipe(
switchMap(() => this.projectService.createProjectDetails()),
catchError((err) => {
console.log(err);
return throwError(err);
})
).subscribe((data) => {
console.log(data);
});
}
please create a new function and fire that function when the response is success.
onSubmit() {
this.projectService.createProject().subscribe({
next: data => {
console.log(data);
this.createProjectDetails(); // fire function only here
},
error: err => {
console.log(err);
}
});
}
createProjectDetails(){
this.projectService.createProjectDetails().subscribe({
next: data => {
console.log(data);
},
error: err => {
console.log(err);
}
})
}
In the above example, createProjectDetails() will get fired once createProject() is complete. I hope this solves your question, let me know if there are any issues.
Try this using rxjs
import { mergeMap } from 'rxjs/operators'
When we need data from the first API request to make requests to the second API.
onSubmit() {
this.projectService.createProject().pipe(
map(project = {
console.log(project);
return project;
}),
mergeMap( project => this.projectService.createProjectDetails(project.id))
).subscribe( projDetails = {
console.log(projDetails)
});
}
subscribe is a common way to handle requests in Angular, but there are more effective methods. We will first solve our problem using subscribe and then improve on it using mergeMap.

How to end process on specific error in fastify using setErrorHandler?

In my app I'm connecting to a database and then initialize it. I have two custom plugins for that. First plugin connects to DB, and then it registers the second plugin, which initializes the DB (creates tables, domains etc.)
I want to stop process if an error happens inside those two plugins, since without DB connection and initialization the app won't work.
In app.ts I have
const fastify = Fastify({ logger: true });
fastify.register<ConnectAndInitDBConfig>(ConnectAndInitDB, {
config,
initSqlPath: 'assets/init.sql',
});
Here is the first plugin ConnectAndInitDB
const ConnectAndInitDB: FastifyPluginCallback<ConnectAndInitDBConfig> = (
fastify,
{ config, initSqlPath },
done
) => {
fastify.register(fastifyPostgres, config); // this connects to the DB (fastify-postgres NPM package)
fastify.after(err => {
if (err) {
fastify.log.error(err);
return done(err);
}
console.log('DB Connected.');
});
fastify.register(InitDB, { initSqlPath }); // this initializes the DB
fastify.after(err => {
if (err) {
fastify.log.error(err);
return done(err);
}
console.log('DB Initialized.');
});
fastify.setErrorHandler(function errorHandler(error) {
console.log(`got error`); // this is never called, even if an error happens during initialization of the DB
});
done();
};
Here is the second plugin InitDB
const InitDB: FastifyPluginCallback<{ initSqlPath: string }> = async (
fastify,
{ initSqlPath }: { initSqlPath: string },
done
) => {
try {
const initSql = await (
await promisify(readFile)(join(resolve(), initSqlPath))
).toString();
await fastify.pg.pool.query(initSql);
console.log({ initSql });
} catch (err: Error | unknown) {
return done(err as Error);
}
done();
};
When the error happens inside the InitDB plugin, I see it's logged by fastify logger, but I'm not able to catch it inside the setErrorHandler.
How do I catch any error that happens exactly inside my custom plugins, and let fastify handle all other errors?
You don't need the errorHandler because it is triggered for HTTP errors, not for the fastify server startup errors.
It is quite simple to archive your needs:
// when there is an async func, the `done` arg must be removed
const ConnectAndInitDB = async function (fastify, { config, initSqlPath }) {
await fastify.register(fastifyPostgres, config)
console.log('DB Connected.')
try {
await fastify.register(InitDB, { initSqlPath })
console.log('DB Initialized.')
} catch (error) {
console.log(`got error`)
throw error // bubble up the error so fastify server will not start
}
}

Node express bad practice?

Good afternoon,
I'm trying to standardize my try {} catch() {} block across all my routes.
I created a Controller class as follows:
const { handleRouteError } = require('../handleRouteError');
class Controller {
async tryFunction(promise, onSuccess, onFail) {
try {
const data = await promise;
onSuccess(data);
} catch (error) {
handleRouteError(error);
onFail(error);
}
}
}
module.exports = Controller;
And I'm using it like this in my router:
const { getDays } = require('./controllers/getDays');
const controller = new Controller;
router.get('/days', async function getDayDays(req, res, next) {
await controller.tryFunction(
getDays(res.locals.user_id),
(data) => res.json(data),
(err) => next(err)
);
});
My questions:
Is it bad practice to pass the onSuccess and onFail function as I have done? All opinions welcome!
Will it eat up a lot of memory on the server?
I would try to stick to the built in error handling rather than inventing my own.
If your intention is to call handleRouterError with the error before passing the error to the caller you could do something like this:
class Controller {
async resolve(promise) {
try {
return await promise;
} catch (error) {
handleRouteError(error);
throw error;
}
}
}
And consume the resolve method like this:
const { getDays } = require('./controllers/getDays');
const controller = new Controller;
router.get('/days', async function getDayDays(req, res, next) {
try {
res.json(await controller.resolve(getDays(res.locals.user_id)));
} catch (err) {
next(err)
}
});

Error: Callback was already called in loopback

I have the following code:
"use strict";
const Raven = require("raven");
Raven.config(
"test"
).install();
module.exports = function(Reservation) {
function dateValidator(err) {
if (this.startDate >= this.endDate) {
err();
}
}
function sendEmail(campground) {
return new Promise((resolve, reject) => {
Reservation.app.models.Email.send(formEmailObject(campground),
function(
err,
mail
) {
if (err) {
console.log(err);
Raven.captureException(err);
reject(err);
} else {
console.log(mail);
console.log("email sent!");
resolve(mail);
}
});
});
}
function formEmailObject(campground) {
return {
to: "loopbackintern#yopmail.com",
from: "noreply#optis.be",
subject: "Thank you for your reservation at " + campground.name,
html:
"<p>We confirm your reservation for <strong>" +
campground.name +
"</strong></p>"
};
}
Reservation.validate("startDate", dateValidator, {
message: "endDate should be after startDate"
});
Reservation.observe("after save", async function(ctx, next) {
try {
const campground = await Reservation.app.models.Campground.findById(
ctx.instance.campgroundId
);
const mail = await sendEmail(campground);
next();
} catch (e) {
Raven.captureException(e);
next(e);
}
});
};
Sorry for the poor formatting. When the flow is done I get this error:
(node:3907) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Callback was already called.
I am calling the next() callback in two places, one in the try code and one in the catch code. I assume that when it all goes right, next callback is called only once, and the same when it goes wrong. But it seems that it is called twice and I don't know why.
I also tried to call next outside the try/catch code but it results in the same error. If I left only the next that is called inside the catch code it doesn't throw the error.
Any idea? Thanks!
if you are using async function you shouldn't explicitly call next, it gets automatically called.
check out this github issue for loopback async/await
so your hook can be like the following.
Reservation.observe("after save", async ctx => {
try {
const campground = await Reservation.app.models.Campground.findById(
ctx.instance.campgroundId
);
const mail = await sendEmail(campground);
} catch (e) {
Raven.captureException(e);
throw e;
}
});
NB: you don't need to wrap it in try catch unless you want to modify/work with the error.
You should declare your sendEmail method as async as it returns a promise.
async function sendEmail(campground) {
...
}
After reading this article, I created a await-handler.js file which include following code.
module.exports = (promise) =>
promise
.then(data => ({
ok: true,
data
}))
.catch(error =>
Promise.resolve({
ok: false,
error
})
);
Then in MyModel.js file, I created a async function to get a value from database as follow.
const awaitHandler = require("./../await-handler.js")
const getMaxNumber = async (MyModel) => {
let result = await awaitHandler(MyModel.find());
if (result.ok) {
if (result.data.length) {
return result.data.reduce((max, b) => Math.max(max, b.propertyName), result.data[0] && result.data[0].propertyName);
} else {
return 0;
}
} else {
return result.error;
}
}
As per #Mehari's answer, I've commented call to next() method as follow:-
module.exports = function(MyModel) {
MyModel.observe('before save', async(ctx, next) => {
const maxNumber = await getMaxNumber (MyModel);
if(ctx.instance) {
...
set the required property using ctx.instance.*
like createdAt, createdBy properties
...
// return next();
} else {
...
code for patch
...
// return next();
}
})
}
This solves the warning issue whenever saving endpoint is triggered.
But the warning issue still appear when I run the endpoint to load the resource.Like
http://localhost:3000/api/MyModel
Previously, the issue appear only when the before save operation hook gets triggered.
After encountering this issue, I checked adding access and loaded operation hooks and I found that the the warnings are issued after loaded operation hook.
MyModel.observe('access', (ctx, next) => {
return next();
})
MyModel.observe('loaded', (ctx, next) => {
return next();
})
What could have caused this issue and how can it gets resolved?

Using async await properly in node js

To overcome callback hell in javascript, I'm trying to use async await from legacy code written in SQLServer procedure.
But I'm not sure my code might be write properly.
My first confusing point is when async function returns, should it return resolve() as boolean, or just return reject and handle with try-catch?
Here is my code snippets.
Please correct me to right direction.
apiRoutes.js
app.route('/api/dansok/cancelDansok')
.post(dansokCancelHandler.cancelDansok);
dansokCancelController.js
const sequelize = models.Sequelize;
const jwt = require('jsonwebtoken');
async function jwtAccessAuthCheck(accessToken) {
if (!accessToken) {
return Promise.reject('Empty access token');
}
jwt.verify(accessToken,"dipa",function(err){
if(err) {
return Promise.reject('TokenExpiredError.');
} else {
return Promise.resolve();
}
});
}
async function checkFeeHist(dansokSeqNo) {
let feeHist = await models.FeeHist.findOne({
where: { DansokSeqNo: dansokSeqNo}
});
return !!feeHist;
}
async function getNextDansokHistSerialNo(dansokSeqNo) {
....
}
async function getDansokFee(dansokSeqNo) {
....
}
async function doCancel(dansokSeqNo) {
try {
if (await !checkFeeHist(dansokSeqNo)) {
log.error("doCancel() invalid dansokSeqNo for cancel, ", dansokSeqNo);
return;
}
let nextDansokSerialNo = await getNextDansokHistSerialNo(dansokSeqNo);
await insertNewDansokHist(dansokSeqNo, nextDansokSerialNo);
await updateDansokHist(dansokSeqNo);
await updateVBankList(dansokSeqNo, danokFee.VBankSeqNo);
await getVBankList(dansokSeqNo);
} catch (e) {
log.error("doCancel() exception:", e);
}
}
exports.cancelDansok = function (req, res) {
res.setHeader("Content-Type", "application/json; charset=utf-8");
const dansokSeqNo = req.body.DANSOKSEQNO;
const discKindCode = req.body.HISTKIND;
const worker = req.body.PROCWORKER;
const workerIp = req.body.CREATEIP;
const accessToken = req.headers.accesstoken;
//check input parameter
if (!dansokSeqNo || !discKindCode || !worker || !workerIp) {
let e = {status:400, message:'params are empty.'};
return res.status(e.status).json(e);
}
try {
jwtAccessAuthCheck(accessToken)
.then(() => {
log.info("jwt success");
doCancel(dansokSeqNo).then(() => {
log.info("cancelDansok() finish");
res.status(200).json({ message: 'cancelDansok success.' });
});
});
} catch(e) {
return res.status(e.status).json(e);
}
};
You'll need to rewrite jwtAccessAuthCheck(accessToken) so that it keeps track of the outcome of its nested tasks. In the code you've written:
// Code that needs fixes!
async function jwtAccessAuthCheck(accessToken) {
// This part is fine. We are in the main async flow.
if (!accessToken) {
return Promise.reject('Empty access token');
}
// This needs to be rewritten, as the async function itself doesn't know anything about
// the outcome of `jwt.verify`...
jwt.verify(accessToken,"dipa",function(err){
if(err) {
// This is wrapped in a `function(err)` callback, so the return value is irrelevant
// to the async function itself
return Promise.reject('TokenExpiredError.');
} else {
// Same problem here.
return Promise.resolve();
}
});
// Since the main async scope didn't handle anything related to `jwt.verify`, the content
// below will print even before `jwt.verify()` completes! And the async call will be
// considered complete right away.
console.log('Completed before jwt.verify() outcome');
}
A better rewrite would be:
// Fixed code. The outcome of `jwt.verify` is explicitly delegated back to a new Promise's
// `resolve` and `reject` handlers, Promise which we await for.
async function jwtAccessAuthCheck(accessToken) {
await new Promise((resolve, reject) => {
if (!accessToken) {
reject('Empty access token');
return;
}
jwt.verify(accessToken,"dipa",function(err){
if(err) {
reject('TokenExpiredError.');
} else {
resolve();
}
});
});
// We won't consider this async call done until the Promise above completes.
console.log('Completed');
}
An alternate signature that would also work in this specific use case:
// Also works this way without the `async` type:
function jwtAccessAuthCheck(accessToken) {
return new Promise((resolve, reject) => {
...
});
}
Regarding your cancelDansok(req, res) middleware, since jwtAccessAuthCheck is guaranteed to return a Promise (you made it an async function), you'll also need to handle its returned Promise directly. No try / catch can handle the outcome of this asynchronous task.
exports.cancelDansok = function (req, res) {
...
jwtAccessAuthCheck(accessToken)
.then(() => {
log.info("jwt success");
return doCancel(dansokSeqNo);
})
.then(() => {
log.info("cancelDansok() finish");
res.status(200).json({ message: 'cancelDansok success.' });
})
.catch(e => {
res.status(e.status).json(e);
});
};
I strongly suggest reading a few Promise-related articles to get the hang of it. They're very handy and powerful, but also bring a little pain when mixed with other JS patterns (async callbacks, try / catch...).
https://www.promisejs.org/
Node.js util.promisify

Resources