Goal is to set the variable auth correctly for further use, hence i want to refactor the function loginUser:
function loginUser(user, request, auth) {
return function(done) {
request
.post('/users/login')
.send(credentials)
.expect(200)
.end(onResponse);
function onResponse(err, res) {
auth.token = res.body.token;
return done();
}
};
}
loginUser(user, request, auth)(function() {
request.get(testUrl)
.set('Authorization', `bearer ${auth.token}`)
.expect(200, done);
});
to use async / await like this (without the callback):
auth = await loginUser(user, request);
request.get(testUrl)
.set('Authorization', `bearer ${auth.token}`)
.expect(200, done);
But i am struggling of returning / setting auth correctly (it would not matter if i pass auth as parameter or as return value).
What i tried was stuff like this:
async function loginUser(user, request) {
let auth;
await request
.post('/users/login')
.send(credentials)
.expect(200)
.end(onResponse);
function onResponse(err, res) {
auth.token = res.body.token;
}
return auth;
}
But auth was never set correctly.
Don't use 'end' syntax, that's for callbacks:
const response = await request.post(...)
.expect(200)
const {body: {token}} = response
return token
Basically it should look like sync code
The problem is that the onResponse method is being executed later than you the return of the function because of the event loop in Nodejs. So you will have to do resolve the promise exactly when you receive the data
The method loginUserInternal could be like this:
function loginUserInternal(user, request) {
return new Promise((resolve,reject) => {
let auth = {};
request
.post('/users/login')
.send({
username: user.username,
password: user.password_decoded,
})
.expect(200)
.end(onResponse);
function onResponse(err, res) {
if(err) return reject(err)
auth.id = res.body.id;
auth.token = res.body.token;
auth.tokenExpires = res.body.tokenExpires;
resolve(auth)
}
})
}
And call it like you were doing with async await.
Related
Having this script:
const got = require("got");
const getStream = require("get-stream");
const app = require("express")();
async function httpGet() {
console.log("getting response");
let targetUrl = "http://localhost:3000/api";
let gotOptions = {
method: "get",
headers: { "content-type": "application/json" },
body: undefined,
retries: 0
};
let response = await new Promise(async (resolve, reject) => {
let stream = got.stream(targetUrl, gotOptions);
stream.on("error", async error => {
try {
resolve(stream);
} catch (err) {
reject(err);
}
});
stream.on("response", async res => {
try {
resolve(await getStream(res));
} catch (err) {
reject(err);
}
})
});
return response;
}
async function apiMiddleware(req, res, next) {
try {
const response = await httpGet();
console.log(response);
} catch (e) {
throw new Error("some error");
}
next();
}
app.use("/", [apiMiddleware]);
app.get("/api", (req, res) => {
res.json({ data: "some output" });
});
app.get("/someapp", (req, res) => {
res.end();
});
app.listen(3000, () => {
console.log("listening on port 3000");
});
Will (upon visiting "localhost:3000/someapp") console.log
getting response
getting response
getting response
getting response
getting response
getting response
getting response
getting response
getting response
getting response
getting response
...
When debugging, the promise will always throw me at the start of httpGet(), so what is going on?
what is going on?
You've registered the apiMiddleware for all routes (/), that includes the /api route. So whenever you are requesting anything from the server, apiMiddleware() is getting called, which will - wait for it - make a http request to http://localhost:3000/api! This leads to an infinite recursion. And, since you're waiting for the response before sending a response, probably also to quickly running out of memory.
There is a lint error, that basically says that we cannot return Promises in places where a void is expected, the message is clear, but how to fix it without lying to the linter by using any or (as any), both functions [validateJWT, getUser] are async functions It looks pretty basic, but I do not know how to fix it in an easy way. thanks!
import { Router } from 'express';
import { getUser } from '../controllers/userController';
import { validateJWT } from '../middlewares/validateJWT';
const router = Router();
router.get('/user', validateJWT, getUser);
export default router;
const getUser = async (req: Request, res: Response, next:
NextFunction): Promise<any> => {
try {
const { id } = req.params;
if (!id) {
let response = formatErrorResponse({ error: true, statusCode: 400, errorMessage: 'Missing id in params' });
return res.status(400).json(response);
}
let user = await User.findById({_id: id});
let objRes = { error: false, statusCode: 200, message: 'User was found', data: user };
return res.status(200).json(objRes);
} catch (error) {
console.log('error in controller /user', error)
next(error);
}
}
export {
getUser
}
const validateJWT = async (req: Request, res: Response, next: NextFunction): Promise<any> => {
const token = req.header('x-token');
console.log(req)
if (!token) {
const err = formatErrorResponse({ error: true, statusCode: 401, errorMessage: 'Missing header x-token' });
return res.status(401).json(err);
}
try {
await verifyToken(token);
next();
} catch (error) {
const err = formatErrorResponse({error: true, statusCode: 401, errorMessage: 'Invalid token, athorization denied', errorContent: error});
return res.status(400).json(err);
}
}
You need to change your implementation of the function to work like this:
router.get('/user', (req, res) => {
validateJWT(req, res);
getUser(req, res);
});
Since express.Router instances expect the route as a string for the first parameter which you have as "/user". And a second parameter which is a callback.
Inside that callback you can call your functions.
Assuming you need validateJWT to finish running before getUser you could do something like this:
validateJWT(...).then((...) => getUser(...));
I suggest something like the below.
router.use(validateJWT)
router.get('/users/:id', (req, res, next) => {
getUser(req.params.id)
.then(res.json)
.catch(next);
});
In the docs, I could see an async middleware example http://expressjs.com/en/guide/writing-middleware.html#mw-fig.
I could not find an async handler example. I recall that back in the day, express didn't support async handler. Hence, I used .then inside the handler. You need to double-check if you can use them nowadays.
In the express docs is also an example like the below. It makes working with async handler more convenient in some regard. As you can wrap all your async handler with this wrapper.
const wrap = fn => (...args) => fn(...args).catch(args[2]);
router.get('/users/:id', wrap(getUser));
I have a utils layer with some functions inside like getUserInfo(userToken). I call this getUserInfo function to get userId. I get Node js works asynchronously, But is there any way I can achieve the below functionality. I am using npm request library to make rest calls.
routes/controller.js receives call with userToken. Make a call to getUserInfo() in utils/users.js to get UserId. getUserInfo function makes a request.get('url', function (e, r, b)) call to external service.
I am trying to implement the following, But Not interested in using callbacks and having nested stuff.
//Controller Layer Part
router.get('/api', check, async function (req, res) {
const userId = await users.getUserInfo(req.headers.authorization);
const result = await assignmentsService.getAssignments(userId);
res.send(result);
});
//Utils Layer Part
async function getUserInfo(accessToken, callback){
const userId = await request.get({
url: 'someUrl',
headers: {
'Authorization': accessToken
}
},
await function (error, response, body){
const userId = JSON.parse(body).nickname;
});
return userId;
}
Per your request. I would recommend axios for you.
Example below:
const axios = require("axios");
//Controller Layer Part
router.get("/api", check, async function (req, res) {
const userId = await users.getUserInfo(req.headers.authorization);
const result = await assignmentsService.getAssignments(userId);
res.send(result);
});
//Utils Layer Part
async function getUserInfo(accessToken) {
const response = await axios.get("someUrl", {
headers: {
Authorization: accessToken,
},
});
console.log(response);
const userId = JSON.parse(response.data).nickname; //it may not be response.data but you can change.
return userId;
}
loginTest() gives me resolve(value) so it goes to .then , my problem is since promise is async code, console.log(token) gets printed with promise-pending before promise fulfills.I want to display the value only after promise is fulfilled.Can any one help ?
const token = loginTest().then(res => res).catch(err => err);
console.log(token);
Try this:
loginTest().then(res => {
console.log(res.token);
}).catch(err => err);
This presumes that the token is provided as a field of res. I don't know the structure of the response so you need to check that. Token will not be returned directly from loginTest if it is async.
Use ES6's Async / Await.
const token = await loginTest();
But please note that this line of code needs to be wrapped in a async function. Otherwise, await will not work. And note that await cannot be used in global scope.
For example:
async function getToken() {
const token = await loginTest();
// this next line will execute after the result of of loginTest() returns
console.log(token);
// do something with token after this line
const mutateToken = token + '123456';
}
Documentation of Async / Await found here: Async / Await
You could leverage Async / Await functionality:
const token = await loginTest()
console.log(token)
For Example:
async function getToken () {
const token = await loginTest()
console.log(token)
}
getToken()
You could also do the following for a "synchronous" promises way:
loginTest()
.then(res => res) // Retained for example sake of chaining promises
.then(res => console.log(res))
.catch(err => console.log(err))
This is assuming that token is res, adjust accordingly if it is an object and you need a child property :)!
Try this promise :
var loginTest = new Promise(function
(resolve, reject) {
if (isLogin) {
var login = {
username: 'admin'
//something that related with the loginTest..
};
resolve(login);
} else {
var reason = new Error('some errors..');
reject(reason);
}
})
loginTest
.then((fulfilled) => {
console.log(fulfilled);
})
.catch((error) => {
console.log(error.message);
})
so, loginTest will be printed after fulfilled, then catch error if there are some errors.
You can't access token by doing like below as it is an asyn method.
const token = loginTest().then(res => res).catch(err => err);
console.log(token);
Instead use below
loginTest().then(res => {
const token = res.token;
// your further code goes here.
}).catch(err => err);
I'm using promises inside express middleware. I want to use the async/await methods.
app.get('/data1',async function(req,res) {
data = await getData1(); // This line throw an error,
res.send(data)
})
app.get('/data2',async function(req,res) {
data = await getData2(); // This line throw an error
res.send(data)
})
This makes the browser wait forever.
On the server I see
(node:251960) UnhandledPromiseRejectionWarning: Unhandled promise rejection
Now, to fix it for one middleware I'm doing:
app.get('/data1',async function (req,res){
return (async function(){
data = await getData1()
})().catch(() => {
res.send("You have an error")
}
})
app.get('/data2',async function (req,res){
return (async function(){
data = await getData2()
})().catch(() => {
res.send("You have an error")
}
})
I don't like this repetion. How can I set default error? I have tried for example:
app.use(function(error,req,res,next)){
res.send('You have an error')
}
But it didn't work.
In other words: How to set default function to be called when Express middlewares returning a rejected promise?
Now I found a way how to do it, I'm still keep the question open for more suggestions
app.get("/data1",
wrap_middleware(async (req, res) => {
data1=await getData1()
res.send(data1)
})
}
app.get("/data1",
wrap_middleware(async (req, res) => {
data2=await getData2()
})
}
function wrap_middleware(func) {
return async (req, res, next) => {
func(req, res, next).catch(err => {
console.log(err.message);
res.send("Error");
});
};
}
I don't understand the use of sending the same error for different function but I think the handling error code could be write in more readable way (just catch the error and do with them what you want the same way you catch errors in any route middleware):
function getData1(){
return new Promise( (resolve,reject) => {
setTimeout(() =>{
reject(new Error('error has occur!'));
},2000);
});
}
router.get('/data1', async (req,res, next) => {
try{
const data = await getData1();
res.send(data);
}
catch(ex){
res.send(ex.message);
// next(ex); => sending it to express to handle it
}
});
If you want a global error handling then its not any different from any code you want catch errors globally - you can set a function that take as param , the response object and the async code and create general catch for every async call comes from middleware (which has response object)