I am experimenting with t library, and am trying to install the functionality into my own search bar to practice (still a student).
Can anyone provide advice to the format this would be in??
#PascalLamers answer is the cleanest way to go about it, using the "await" pattern.
To see what this looks like with promise chaining:
app.get('/:url', async function (req, res) {
return wappalyzer
.init()
.then(function(){
return wappalyzer.open(decodeURIComponent(req.params.url))
})
.then(function(site){
return site.analyze()
})
.then(function(data){
return res.status(200).json(data);
})
.catch(function(err){
return res.status(500).json({ message : err.message })
})
}
Each then executes a single async operation, and on success passes its result to the next then() operation.
Each then() is only called upon the success of the previous then(). If an error occurs at any point, the catch() is executed and the function returns (in this case, returns an error 500).
Completely ignoring what wappalyzer actually is or does, I would suggest the following, since you are already providing an async function as callback to your route controller :
app.get('/:url', async function (req, res) {
try {
await wappalyzer.init();
const site = await wappalyzer.open(decodeURIComponent(req.query.url));
const data = await site.analyze();
// if you end up here, everything was successfull
return res.status(200).json(data);
} catch (ex) {
// if you end up here, something went wrong
return res.status(500).json({ message : ex.message });
}
});
The above doesn't really make much sense, since you are telling your backend to look for an url param but using a query parameter instead. I woudln't recommend to send an url as either, param or query. Use the request body instead :
// receive request in backend, with an endpoint that makes a bit more sense
// also using POST method, otherwise you won't be able to send a body
app.post('/search', async function (req, res) {
const url = req.body.url;
// do stuff
});
// sending request to backend, using axios for example
const respond = await axios.post('/search', { url : 'www.google.com' });
// or using fetch
const respond = await fetch('/search', {
method: 'post',
body: JSON.stringify({ url : 'www.google.com' });
Please be aware these are just pointers, nothing to copy & paste :) .
If you are using Express, I suggest reading the documentation again. I think you are misunderstanding how the router works : https://expressjs.com/de/guide/routing.html#route-parameters
Related
I have a middleware error handler that is working great but next(err) and return and return next(err) seems to not stop execution when dealing with promises. What is the proper way to stop execution of my code when an error is found?
For reference: err is a standard Error class in this case.
I don't think you need the code in userProvider.fetchFriends to help with this but if that's wrong please let me know.
const uid = req.query.steamUserId;
//Use the userProvider to get steam friend data
const friendData = await userProvider.fetchFriends(uid)
.catch(err => {
return next(err); //Does not stop execution. Why? just next(err) doesn't either.
});
//Putting this after every catch works but seems very stupid. How do I avoid this?
if(res.writableEnded)
return;
...Other code that runs but causes errors
}
You've got two problems here.
First: next() is explicitly continuing to the next middleware or endpoint.
To finish dealing with the request, send a response.
const middleware = (req, res, next) => {
if (something(req)) {
next();
} else {
res.status(500).json({ ok: false, msg: "some error message" });
}
}
Second: You need to watch your asynchronous logic carefully.
You can't:
trigger something asynchronous
send a response
send a different response when the asynchronous function is complete
You can only send one response to a request.
Either:
Don't call next or res.something in the catch block and just log the error internally or
Don't call next or res.something outside the promise handling and move it to a then handler instead (you might want to switch to using async/await to make your logic easier to follow)
The issue was that I was mixing async/await with .then/.catch. Needed to use try/catch.
ty #jonsharpe
export const getSteamFriends = async (req, res, next) => {
try{
const uid = req.query.steamUserId;
//Use the userProvider to get steam friend data
const friendData = await userProvider.fetchFriends(uid);
//more code in the middle
} catch(e) {
return next(e);
}
};
I managed to use axios by putting this link in my html:
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
What I am trying to do is call axios twice, once to call my http request to send in a JSON Object, and another to send in a file along with the JSON object. What I want to do is to make it so that it is required for both to be sent, meaning that the user cannot just send the JSON object by itself or the file by itself. However, my code does not check for that for some reason. I believe it is due to async not being present, But idk how to add that when I am using vanilla js. Here is my code:
axios.post('/account/signup', userObj).then((profile)=>{
//data returns current user info, including if they are verified or not
return axios.post('/account/signup/veteranFile-upload', formData)
}).then(()=>{
//window.location.replace('/')
console.log('Hello?')
}).catch( function (error){
showAppropriateTextSignUp(error.response.data, signupErr)
})
For some extra info, my model requires the file and the JSON data being sent.
your second then is in the wrong place. also is formData defined?
axios.post('/account/signup', userObj).then((profile)=>{
//data returns current user info, including if they are verified or not
return axios.post('/account/signup/veteranFile-upload', formData).then(()=>{
//window.location.replace('/')
console.log('Hello?')
}).catch( function (error){
showAppropriateTextSignUp(error.response.data, signupErr)
});
});
a much cleaner way: use async await:
const api = async() => {
try{
const profile = await axios.post('/account/signup', userObj);
const whatever = axios.post('/account/signup/veteranFile-upload', formData);
console.log(whatever)
} catch (e) {
showAppropriateTextSignUp(error.response.data, signupErr)
}
}
you might need to chain the axios call instead of returning. That is
axios.post('/account/signup', userObj)
.then((profile)=>{
//current user info, including if they are verified or not
axios.post('/account/signup/veteranFile-upload', formData).then(resp=>{
//do something with the response
//window.location.replace('/')
}).catch(err=>{
console.log(err)
})
console.log('Hello?')
}).catch( function (error){
showAppropriateTextSignUp(error.response.data, signupErr)
})
I have a simple node+express app that makes a few async http/get url requests if you visit a certain page. This is to simply get some data from the db, and send that to the view. The routes are handled using a standard separate routes.js file.
routes.js
...
const bluebird = require('bluebird');
const promiseRequest = bluebird.promisify(require('request'));
Promise.all([
promiseRequest(`http://url1...`),
promiseRequest(`http://url2...`)
])
.then((promiseRes) => {
res.render(...); // loads the view for the client
})
.catch((err) => {
errorHandler(err, req, res); // to be handled by express middleware
});
The http url requests are handled using a controller file, which makes a query to the db, and returns either the values or an error.
controller.js
try {
const {rows, rowCount} = await db.query(findAllQuery);
return res.status(200).send({rows, rowCount});
} catch (error) {
return res.status(400).send({
"name": error.name,
"psql_error_code": error.code
});
}
The controller is working fine, but I am purposely typing in a wrong get url so that the controller returns the res.status(400)... object back to the route. No matter what I return to the route, the promise resolves. I even tried returning a new Error('Error description'), but the promisify receives this as a resolution.
Any help would be deeply appreciated!
--
The response from the controller to the route is a lengthy object.
As I can not see how you have implemented errorHandler(). One thing I can think of that you should look for is that - if inside .catch(err){} of the called promise you are not returning any error, it will go to the then of the calling function.
So i've been doing some reading and I think I have a general grasp on this subject but could use some insight from someone more experienced. I've been trying to write a simple RSS reader in Meteor and have been facing some issues with calling the Meteor method asynchronously. I currently define the method on the server(synchronously) and call it on the client(asynchronously). What I don't understand is that when I try to make the HTTP.call on the server, I return an undefined value passed to my client if I pass a callback into the request. But when I make the API request synchronously everything seems to work fine. Is this the normal behavior I should expect/the way I should be making the API call?
Meteor.methods({
getSubReddit(subreddit) {
this.unblock();
const url = 'http://www.reddit.com/r/' + subreddit + '/.rss';
const response = HTTP.get(url, {}, (err, res) => {
if(!err) {
//console.log(res.content);
return res;
} else {
return err;
}
});
}
});
Here's the method defined on the server side. Note that logging res.content shows that I'm actually getting the right content back from the call. I've tried reading some other answers on the topic and seen some things about using Future/wrapAsync, but I'm not sure I get it. Any help would be greatly appreciated!
The HTTP.get is doing async work, so callback passed to it will be called out of this meteor method call context.
To get desired result you should do it like this:
Meteor.methods({
getSubReddit(subreddit) {
// IMPORTANT: unblock methods call queue
this.unblock();
const url = 'http://www.reddit.com/r/' + subreddit + '/.rss';
const httpGetSync = Meteor.wrapAsync(HTTP.get);
try {
const response = httpGetSync(url, {});
//console.log(response.content);
return response.content;
} catch (err) {
// pass error to client
throw new Meteor.Error(...);
}
}
});
I tried to call res() after some async stuff finishes, inside Async.waterfall([], cb)
But as it seems, the req/res objects are not available in that scope. I call them from my callback cb.
function (req, res, next) {
var query = req.query;
async.waterfall([
async.apply(userManager.register, query.username, query.email, query.password)
], function (err, result) {
if (err)
console.log(err);
if (err && err.internal == false)
return res(err); //TypeError: res is not a function
console.log(result);
});
The only solution I have in mind is, passing the req/res to my backend, and call it there.
But that would mean that my background code needs to have a req and res object. Moreover it returns something to my server, which is also bad.
Thanks for your help.
Your issue is not about scope. It's that res is not a function so you can't call it like res(err). res is an object with methods. You can send an error response either like this which will go to the default error handler in Express:
next(err)
Or like this:
res.status(500).send("Internal Error occurred").
which sends a 500 status on the response and then sends whatever content you want to describe the error.
I can't think of any circumstance where you want to pass the res object into your backend. Your backend should fetch and return data and then your route handler should turn that data or error into a response.