I am using bluebird to implementing promisify in my custom lib. I have follwing lib
user, lib-1, lib-2 etc
On user registration, from my user router
// user_router.js
var user = new User();
var lib1 = new Lib1();
var lib2 = new Lib2();
// I am expecting, all method will be async by following 3 lines!
Promise.promisifyAll(user);
Promise.promisifyAll(lib1);
Promise.promisifyAll(lib2);
user.validationAsync(payload)
.then(function(result_from_validation) {
return lib1.validationAsync(payload);
})
.then(function(result_lib1_validation)) {
return lib2.validationAsync(payload)
})
.then(function(result_lib2_validation)) {
// final save
user.registerAsync(payload);
lib1.saveAsync(payload);
lib2.saveAsync(payload);
return {'response-data'}
})
.then(function(response)) {
// send response
res.json('success', 'User successfully created', {});
})
.catch(function(error)) {
//all error will be handled here
//these error will be either throw or by promise rejected.
res.json('error', error.message, {error.stack});
})
In validation method I am throwing errors like
//user_lib.js
// This throw will be handled by router catch. ???
throw Error('Field value is invalid');
In save method mongooes modal also have some validation like user already exist so unable to create user.
function register (payload)
{
// payload data binded on user_modal here
var user_modal = getUserModal(payload);
user_modal.saveAsync()
.then(function(){})
.catch(function(error){
//How to handle error here and send this error to router
})
}
All above is just my expectation. I want to send all response from user_router.js. (My user module already implemented. now need to integrate it by bluebird)
Now Question ::
How can I do it by bluebird ? How to set resolve/reject method on
user object(explicitly) ??
If validation method throw error then it
will be captured by router catch method ???
How can I send any
error/rejection to router's catch from mongooes (see user register
method)?
I hope you will get my points. Thanks in advance!!
You can also share any example link to implement these things.
Now I am using try catch for normal statement and using promise for async error handling!! :) Thanks a lot guyz
Related
Currently I'm on a legacy application using pug.js as view engine in a node.js express-app.
I want to implement a generic way to display feedback messages. I want to be able to display messages (successes, errors), even if the handler does reply with a redirect.
This is what I want:
handlePostRequest(req, res){
// do stuff with the post request
doStuff(req.body);
//This should be done of course somewhere else.
req.session.successes=req.session.successes|[];
//save some success-message for the user
req.session.successes.push("Your post has been saved. Thank you!");
//but reply with a 302
res.redirect(req.headers.referer);
}
//a get request. maybe the handler above redirected here
handleGetRequest(req,res){
// we do NOT get the successes here. Just the 'pure' data.
const renderData=getRenderData();
res.render('fancy-pug-template', renderData);
}
fancyMiddlewareForMessages(req, res, next){
//how to implement getRenderDataByBlackMagic()????
const renderData = getRenderDataByBlackMagic();
//set the messages
renderData.successes = req.session.successes;
//empty saved messages
req.session.successes = [];
next();
}
Obviously, I do not want to polute every handler which actually renders a template with some logic which retrieves the messages and adds them to the parameter object. I would like to move this cross-cutting concern in a middleware callback or something like that.
So, the question is: Can this be achieved? How? I'm fairly new to pug.js, maybe I'm overlooking something obvious.
Ok, I found a way. This is what I did:
const requestStorage = new AsyncLocalStorage<Request>();
function patchRenderFunction(req: Request, res: Response, next: NextFunction) {
const render = res.render;
res.render = function (view: string, options?: any, callback?: (err: Error, html: string) => void) {
const messages = new MessageManager(req);
//merge errorMessages
options.errorMessages = mergeMessageArrays(options.errorMessages, messages.errors);
//same for successMessages
options.successMessages = mergeMessageArrays(options.successMessages, messages.successes);
render.bind(this)(view, options, callback);
};
requestStorage.run(req, () => {
next();
});
}
export function applyAutomaticRenderAttributes(app: Express): void {
app.use(patchRenderFunction);
}
export function successMessage(message: string, req?: Request) {
if (!req) {
req = requestStorage.getStore();
}
if (!req) {
console.error('No request found in async storage. This should not happen. Please report this issue. (successMessage)');
return;
}
new MessageManager(req).addSuccessMessage(message);
}
//export function errorMessage(...) omitted
The MessageManager uses the requests session to store messages. It also filters them in some respect. I'm using the session because the application runs clustered (thank you, pm2). Since the session is stored in the db via express-mysql-session, I avoid problems with non-sticky sessions.
I have the following timed function to periodically refetch credentials from an external API in your usual movie fetching IMDB clone app:
// This variable I pass later to Apollo Server, below all this code.
let tokenToBeUsedLater: string;
// Fetch credentials and schedule a refetch before they expire
const fetchAndRefreshToken = async () => {
try {
// fetchCredentials() sends an http request to the external API:
const { access_token, expires_in} = await fetchCredentials() as Credentials;
tokenToBeUsedLater = access_token;
// The returned token expires, so the timeout below is meant to recursively
// loop this function to refetch fresh credentials shortly before expiry.
// This timeout should not stop the app's execution, so can't await it.
// Have also tried the following with just setTimeout() and
// no `return new Promise()`but it throws a 2nd identical error.
return new Promise(resolve => setTimeout(() => {
resolve(fetchAndRefreshToken());
}, expires_in - 60000));
} catch (err) { throw new Error(err); }
};
fetchAndRefreshToken(); // <-- TypeScript error here
I have tried rewriting it in a thousand ways, but no matter what I do I get a Promises must be handled appropriately or explicitly marked as ignored with the 'void' operator error.
I can get rid the error by by:
Using .then().catch() when calling refreshToken(). Not ideal since I don't want to mix it with async and try/catch.
Putting a void operator ahead of refreshToken(). Feels like cheating.
Putting await ahead of refreshToken(). Essentially breaks my app (pauses execution of the app while waiting for the token to expire, so users in the frontend can't search for movies).
Any idea about how to solve this?
Also, any suggested resources/topics to study about this? Because I had a similar issue yesterday and I still can't figure this one out despite having already solved the other one. Cheers =)
The whole error message is the following:
Error: Error invoking remote method 'MY-IPC-CHANNEL': Error: An object
could not be cloned. at EventEmitter.o.invoke
(electron/js2c/renderer_init.js:71)
The electron/js2c/renderer_init.js:71 line is not my original line of code, but a compiled one.
I'm trying to send a POST request in order to get my Google access token, so that I can work with Google Drive's API. Currently I'm stuck trying to communicate between the renderer process and the main process by giving the main process the code I got from Google and making it send a POST request to the auth server. I have no problem establishing the connection but when I try to do it while sending an HTTP request I get the error above.
// ******* MAIN *******
function exchangeCodeForAccessToken(code: string) {
const clientID = "My Google client ID";
const clientSecret = "My Google client secret";
const body = {
code: code,
client_id: clientID,
client_secret: clientSecret,
redirect_uri: "http://localhost:4000",
grant_type: "authorization_code",
};
const body2 = `code=${code}&
client_id=${clientID}&
client_secret=${clientSecret}&
grant_type=authorization_code`;
// return fetch("https://oauth2.googleapis.com/token", {
// method: "POST",
// body: body
// });
return axios.post("https://oauth2.googleapis.com/token", body);
}
Here's the main handle:
// ******* MAIN *******
ipcMain.handle(
OAUTH2_ACCESS_TOKEN_REQUEST_CHANNEL,
async (event, code: string) => await exchangeCodeForAccessToken(code)
);
And the renderer invoke function:
// ******* RENDERER *******
function exchangeCodeForAccessToken(code: string) {
ipcRenderer.invoke(OAUTH2_ACCESS_TOKEN_REQUEST_CHANNEL, code).then((response) => {
console.log(response);
}).catch((error) => {
//TODO Improve error handling
console.log(error);
});
}
I tried sending the request through the net module from Electron. I also tried with the electron-fetch module, which is supposed to be an Electron integrated version of Node's fetch module. And finally I tried with the axios module, but it kept throwing the same error. I thought it had something to do with object serialization through IPC but then I tried just using the function without returning its promise and the same error kept popping up. Which means that the error is not only appearing when the promise is being returned but whenever the HTTP request function is being called. I also tried sending the request with both the object version of the request and its string version, hence the body and body2.
I don't know what I'm missing, and I'm so close to integrating Google login into my desktop app.
I thought it had something to do with object serialization through IPC but then I tried just using the function without returning its promise and the same error kept popping up.
It is an IPC error. You're returning the full response object, which presumably contains some properties with methods and/or other non-cloneable values. You need to make sure that the returned value can be cloned and sent to the renderer, for example:
ipcMain.handle(
OAUTH2_ACCESS_TOKEN_REQUEST_CHANNEL,
async (event, code) => {
const response = await exchangeCodeForAccessToken(code);
const {status, data} = response;
return {status, data};
}
);
I'm not sure how you called the function in your attempt to fix this, but I just ran this in Electron and it works without issues.
EDIT: Assuming response is coming from a fetch call (use response.json() if the data is JSON):
ipcMain.handle(
OAUTH2_ACCESS_TOKEN_REQUEST_CHANNEL,
async (event, code) => {
const response = await exchangeCodeForAccessToken(code);
const data = await response.text();
return data;
}
);
im trying to create a login system with Node.js, Socket.IO and MongoDB.
At one point i have to get a certain cookie of the client.
So i "send" an event to the client which should return the cookie so i can work with that data within the same function.
My code is as follows:
Server:
async function checklogin(user) {
user = user;
console.log("user:", user);
await User.find({username:user}).then(function(docs) {
servercookieid = docs[0].cookieid;
servercookiedate = docs[0].cookiedate;
});
io.emit('getCookie', function(responseData) {
console.log(responseData)
}).catch(error)
}
Client:
socket.on('getCookie', function(callback) {
console.log('getting cookie...');
var Cookie = document.cookie;
callback(Cookie)
});
I really dont know why i get this error, because as you can see i am not even broadcasting, sooo...
/shrug
If you need more information, please dont hesitate to ask.
The error message says:
Socket.IO: “UnhandledPromiseRejectionWarning: Error: Callbacks are not
supported when broadcasting”
But in fact when you do io.emit you're broadcasting.
Currently you're broadcasting when you do the command:
io.emit('getCookie', function(responseData) {
console.log(responseData)
}).catch(error)
But for broadcasting you just need set a parameter, it's not a function.
Just fix your code to fix the error.
Changing to:
io.emit('getCookie', responseData);//must set the cookie
by the code you provided responseData is not defined. But by the name it should be a cookie.
I have the following function for handling a user registration in node.js v8.9:
module.exports = async (req,res) =>{
const User = req.app.locals.User;
const user =User.build({
email: req.body.email,
displayName: req.body.display_name,
realName: req.body.real_name,
hashedPassword:req.body.password ,
role: 0
});
try{
await user.save();
res.send({message:"Usuario creado exitosamente.", success:true });
}
catch(ex)
{
res.send({message:"Información es inválida:", success:false});
}
res.end();
};
As you can see I am using async and await for handling when the user does not create successfully. However, my catch() never executes when creation fails. Why? Is there a different way to handle promise rejections with async/await? Someone suggested using thow new Error(), but I don't want to throw an error, I just want to send a message back to the client.
More background info: the app is made with Express.js, and using Sequelize.js as an ORM. I have saved my User model into req.app.locals to that I have access to it from my route handlers. The datatable, actually saving into MS SQL Server, has a UNIQUE constraint on the email column, to a User registering with an already existing e-mail would cause the user.save() promise to fail.
When testing, in the console, I see the exception from sequelize.js saying that the UNIQUE constraint did not allow the record creation. However, my catch statement does not get executed and the client (in this case the browser), remains waiting for a response until it times out. If I change the async/await to use .then() and .catch() it works correctly (no try/catch block needed, either).
I see a couple of issues (which apparently turn out to be causing your problem):
Change both instances of res.send() to res.json().
Remove the res.end().
If you want to send an object as the response, then you need to send JSON. You can either do res.send(JSON.stringify(obj)) or Express has res.json(obj) to convert the object for you.
And, res.end() is only needed with res.write(). Both res.send() and res.json() already include res.end().
try / catch. as the promise rejection in an await will throw an exception.
A correct way is to affect the returned value to variable like that:
try{
[err,resp] = await user.save();
res.send({message:"Usuario creado exitosamente.", success:true });
}
You can then check if you have error for eg