I am working on a Nuxt server side rendered application with the express framework. For authentication I am using the openid-client package. Now I want to store my retrieved token in the express session but the request model (req) is always undefined in the callback promise. To do this I want to use req.session.token = tokenSet.access_token. I am a Newbie in JavaScript so I think I am missing something obvious.
I tried various options on how to pass variables into a JavaScript promise but all of these required that you define the Promise by yourself which is not my case. I also tried waiting on the promise and using it outside of the callback promise but had no success either.
router.get('/api/oidc-callback', (req, res, params) => {
Issuer.discover('http://localhost:5000') // => Promise
.then(function(identityIssuer) {
const client = new identityIssuer.Client({
...
})
// HERE IT IS DEFINED
console.log(req)
client
.callback('http://localhost:3000/api/oidc-callback', req.query, {
code_verifier
})
// => Promise
.then(function(tokenSet) {
// HERE IT IS UNDEFINED
console.log(req)
req.session.token = tokenSet.access_token
}, req)
.catch(error => {
console.log(error)
})
//Also tried using outside
res.redirect('/oidc-callback')
})
})
Thanks in advance for your help!
You have two nested asynchronous operations (shown simplified here) and then you try to do something last in the first .then() handler:
Issuer.discover().then(function() {
client.callback().then(function() {
// ...
});
res.redirect('/oidc-callback');
});
This causes res.redirect() to get called before client.callback() is done as there is nothing in your code that make it wait for the completion of client.callback(). That sends the response and triggers a redirect before you modify the session which is not what you want to do. You can fix that in one of two ways:
1) Put the res.redirect() inside the inner .then() like this:
Issuer.discover().then(function() {
client.callback().then(function() {
// ...
res.redirect('/oidc-callback');
});
});
2) Add a return before client.callback() so that it will chain the inner promise to the outer one. Then, the outer one won't finish until the inner one is done and you can add another .then() handler to put the res.redirect() into:
Issuer.discover().then(function() {
return client.callback().then(function() {
// ...
});
}).then(function() {
// gets called when both asynchronous operations are done
res.redirect('/oidc-callback');
});
I'd recommend option #2 because it makes error handling simpler as you can do all your error handling in one place at the top level. Putting all that together, you'd end up with this:
router.get('/api/oidc-callback', (req, res, params) => {
Issuer.discover('http://localhost:5000').then(function(identityIssuer) {
const client = new identityIssuer.Client({
...
})
return client.callback('http://localhost:3000/api/oidc-callback', req.query, {
code_verifier
}).then(function(tokenSet) {
console.log(req);
req.session.token = tokenSet.access_token
}, req);
}).then(() => {
res.redirect('/oidc-callback');
}).catch(err => {
console.log(err);
res.sendStatus(500);
});
});
Note, I also added proper error handling at the end. This makes sure that no response is sent until both your async operations are done and if either one fails, it sends a proper error response.
I believe the reason why req is undefined because the promise was resolved after the execution of the middleware (res, req, next) =>{...}. Try returning the top level promise i.e. (res, req, next) =>{ return Issuer.discover(...)}, also add a return statement before client.callback(...).
router.get('/api/oidc-callback', (req, res, params) => {
return Issuer.discover('http://localhost:5000') // <-- added return here
.then(function(identityIssuer) {
const client = new identityIssuer.Client({
...
})
// HERE IT IS DEFINED
console.log(req)
return client // <-- added return here
.callback('http://localhost:3000/api/oidc-callback', req.query, {
code_verifier
})
// => Promise
.then(function(tokenSet) {
// HERE IT IS UNDEFINED
console.log(req)
req.session.token = tokenSet.access_token
res.redirect('/oidc-callback')
}) // removed , req here, it is not needed
.catch(error => {
console.log(error)
})
})
})
By adding the return statement, it tells express you are running an async function, thus express it going to wait until your middleware is resolved before moving on to the next middleware.
Related
I am creating a rest api. My get method will return the result according to the total supply value of the contract or it will not respond, but the request I made to the contract returns a promise. How can I use this value?
const NameContract = new web3.eth.Contract(abi, '0xE3A2beCa..........1D901F8');
NameContract.methods.totalSupply().call().then(value => console.log(value))
app.get('/:id', (req, res) => {
let id = parseInt(req.params.id);
//I want to use an if here.
//I want to throw the query according to the value returned from above,
// but it returns a promise, how can I use it value?
nft.findOne({ id: id }, (err, doc) => {
if (doc != null) {
res.json(doc)
}
else {
res.status(404).json(err)
}
});
});
Try saving the promise whose value will end up being the total value
const pTotalSupply = NameContract.methods.totalSupply().call();
(assuming this is valid - the .call method looks a little strange). Options then are to
Not start the server until pTotalSupply above has been fulfilled, provided total supply is a static value and there to be only one name contract the server has to deal with,
Wait for the result to be obtained within the app.get handler, either by using an await operator inside an asynchronous function body, or wrapping request processing in a promise then argument function, or
Using a promise then call to save the supply value and call next in a two part app.get handler. This is more an express oriented solution using a pattern along the lines of:
app.get('/:id', (req, res, next) => {
pTotalSupply.then( value => {
res.locals.value = value;
next();
}
.catch( err => res.status(500).send("server error")); // or whatever
}
, (req, res) => {
const value = res.locals.value;
let id = parseInt(req.params.id);
// process request with value and id:
...
});
The second option is covered in answers to How to return the response from an asynchronous call
I am using Node.js and have a database call being performed with a promise. I setup a '.then/.catch' to handle the result of the promise. That part all seems to work without any issue. After I do some processing of the data returned from the database I am attempting to 'redirect' to another route within the '.then' statement. This re-direction seems to be causing a problem with the '.catch' statement. I have no error issues if I remove the 'res.redirect'...is there a workaround? Any suggestions appreciated.
code:
const db = require('./routes/queries');
//**Query PostGres database...
db.getMembers()
.then(function(value) {
console.log('Async success!', value);
//do some processing on the data returned from the promise call here...
//if I insert a redirect here...such as "res.redirect('/_members');"...
//I get a "Caught an error! ReferenceError: res is not defined" message...?
//evidently the redirect fires the '.catch' statement below...why?
})
.catch(function(err) {
console.log('Caught an error!', err);
});
I recommend providing a reusable promise in case you need to find your members list elsewhere in your Express app
const db = require('./routes/queries');
const findMembers = () => {
return db.getMembers()
.then(members => {
return members
})
.catch(err => {
console.log('Caught an error!', err);
});
}
//inside your express app
app.get("/some_route", (req, res) => {
return findMembers()
.then(members => {
//do something with your members list
res.redirect("/redirect_route")
})
})
Hello friends! I hope you are well.
So in this snippet of code, I have a list of ten servers I want to query. These ten servers are represented by the ten ports I have defined up top in the const "ports".
The idea is to use forEach() on "ports", and run the query on each server. An object is returned by each run of the query and added into the initially empty array "data".
THEN! After "data" has been loaded with the ten objects containing the status of my servers, I want it dished back to the client side!
However..... this is not what happens. Port.forEach() runs the query just fine, and adds into the array "data"... but not before res.json jumps the gun and sends off an empty data back to my soon to be disappointed client.
So far... I've tried callbacks and async/await... but I haven't figured out the syntax for it.. Any tips would be awesome! Thank you for your time friendos!
module.exports = app => {
app.get("/api/seMapServerStatus", (req, res) => {
const ports = [ "27111", "27112", "27117", "27118", "27119", "27110", "27115", "27116", "27113", "27114" ]
const data = []
function hitServers(port){
Gamedig.query({
type: "aGameType",
host: "theServer'sIP",
port: port
}).then((state) => {
data.push(state)
console.log("this is the server", state)
}).catch((error) => {
console.log("Server is offline");
});
};
ports.forEach(port => {
hitServers(port)
})
});
console.log("and here is the final server list", data)
res.json(data);
}
The above code executes synchronously, therefore you return in the same frame before any promise has the chance to resolve.
We can clean the above code up as follows:
module.exports = app => {
app.get("/api/seMapServerStatus", (req, res) => {
const ports = ["27111", "27112", "27117", "27118", "27119", "27110", "27115", "27116", "27113", "27114"]
function hitServers(port) {
return Gamedig.query({
type: "aGameType",
host: "theServer'sIP",
port: port
})
}
// With error handling
function hitServersSafe(port) {
return hitServers(port)
.then(result => {
return {
success: true,
result: result
}
})
.catch(error => {
return {
success: false,
// you probably need to serialize error
error: error
}
})
}
const promises = ports.map(port => hitServers(port))
// With error handling
// const promises = ports.map(port => hitServersSafe(port))
Promise
.all(promises)
.then(data => res.json(data))
.catch(error => {
// do something with error
})
})
}
We map each port to a promise. After we have a list of X promises we wait for all of them to finish.
The calling Promise.all() returns an array of resolved values, or rejects when any promise rejects.
Only after all of the promises have resolved we can move on and send the result to the client.
I'm working on a new framework of microservices built in Node 8 and trying to simplify some of the logic required for passing Promises around between services.
I have a function I import in each service called StandardPromise which you can pass a Promise to. StandardPromise will call .then() on the promise and place the result in an object. If the promise was resolved it will be placed in the data attribute, if was rejected or threw an error then that will go in the err attribute.
The result of the above is that when a service receives a standardized promise by awaiting a call to another service, it can just check if there's anything in err and move forward with data if err is empty. This flow is significantly simpler than having .then() and .catch() blocks in every function.
I'm pretty happy with how it's turning out, and it seems to be working great, but since I haven't seen many examples of this kind of flow I want to know if there's something I'm missing that makes this a terrible idea or an antipattern or anything like that.
Here's a simplified, somewhat pseudocode example:
Service1:
const sp = require('./standardPromise');
const rp = require('request-promise-native');
function ex() {
// Wrap the Promise returned from rp as a "standardPromise"
return sp(rp.get({url: 'https://example.com'}));
}
Service2:
const Service1 = require('./Service1');
async function ex2() {
var res = await Service1.ex();
if (res.err) {
// Do error stuff
console.error(res.err);
return;
}
// Here we know res.data is our clean data
// Do whatever with res.data
return res.data;
}
standardPromise:
module.exports = function(promise) {
try {
return promise.then((data) => {
return {err: undefined, data: data};
}).catch((err) => {
return Promise.resolve({err: err, data: undefined});
});
} catch(err) {
console.error('promise_resolution_error', err);
return Promise.resolve({err: err, data: undefined});
}
}
It can just check if there's anything in err and move forward with data if err is empty. This flow is significantly simpler than having .then() and .catch() blocks in every function.
No, this is much more complicated, as you always have to check for your err. The point of promises is to not have .catch() blocks in every function, as most functions do not deal with errors. This is a significant advantage over the old nodeback pattern.
You would drop your standardPromise stuff and just write
// Service1:
const rp = require('request-promise-native');
function ex() {
return rp.get({url: 'https://example.com'});
}
// Service2:
const Service1 = require('./Service1');
async function ex2() {
try {
var data = await Service1.ex();
} catch(err) {
// Do error stuff
console.error(err);
return;
}
// Here we know data is our clean data
// Do whatever with data
return data;
}
or actually simpler with then for handling errors:
// Service2:
const Service1 = require('./Service1');
function ex2() {
return Service1.ex().then(data => {
// Here we know data is our clean data
// Do whatever with data
return data;
}, err => {
// Do error stuff
console.error(err);
});
}
Using sequelize.js in a nodejs app, and I have a promise.all that takes two promises (a user query, and a color query):
router.get(`/someEndPoint`, (req, res) => {
let userAccount = user.findOne({
where: {
id: //some ID
}
});
let colorStuff = color.findOne({
where: {
colorName: //some color
}
})
Promise.all([userAccount , colorStuff ]).then(([result1, result2]) => {
//do stuff, such as:
res.send('success');
}).catch(err => {
console.log(err)
});
});
At the part that says //do stuff, my console keeps giving me this warning:
a promise was created in a handler at... but was not returned from it,
see (URL that I can't post) at Function.Promise.attempt.Promise.try
I'm not sure how to resolve this. I thought after the .then that the promises are resolved?
Hard to tell without other context, but perhaps you need to return the Promise.all
return Promise.all([user, color])...
From the bluebird docs here: https://github.com/petkaantonov/bluebird/blob/master/docs/docs/warning-explanations.md#warning-a-promise-was-created-in-a-handler-but-was-not-returned-from-it
if there are any other promises created in the // do stuff area, be sure to return those as well.