Express router "res" object has to take me to other pages - node.js

See the example below:
var apiRouter = express.Router();
apiRouter.post('/api/postAgree', function(req, res, next){
userModel.findOneAndUpdate(
{profileID: req.session.facebookProfileId},
{$push:{postsAgreed: req.query.postID}},
{safe: true, upsert: true},
function(err, model) {
if (err){
console.log(err);
}
}
)
Now, the MongoDB operation is already done and I want to stay on the same page.
Will I be doing this:
res.render('theSamePageIamOn', {foo:bar});
I know this works but it seems like it is a very inefficient way of doing it.
So my question really is: If I have a button on a page which makes an API call but I want to stay on the same page, how will I do that? The res.(options) function sort of is made like it has to take me to other pages

Thanks to #robertklep and #GiladArtzi - it should be an AJAX call and the response should be in the form of:
res.json()
Then the response can be handled by the frontend using other tools like: Angular

I'm not sure what you're talking about, just call the function....
function doesSomething (args) {
console.log(args)
}
apiRouter.post('/api/postAgree', function(req, res, next){
doesSomething("HELLO")
});
Function calls don't expects the user to go to another page each time an API call is handled.

Related

How to run function with this function

How to run function within a function
If the first function throw any error then 2nd function should run
server.post('/User', (req, res,next) =>
server.post('/submit', (req, res) =>
server.post('/User', (req, res,next) => {
// Some Task
if(){
//Do something
}
else {
server.post('/submit', (req, res) => {
If the first function returns a promise you could run those functions after catching the error like this:
firstFunction()
.catch(e => {
secondFunction();
});
Otherwise you could use a try-catch statement like this:
try {
firstFunction();
} catch (e) {
secondFunction();
}
In Express, the next parameter allows a route/middleware to pass processing to the next route/middleware (actually, all routes are merely middlewares that don't call next). Therefore you can implement passing your route processing by just calling next():
function submitHandler (req, res) {
// ...
}
server.post('/User',
(req, res, next) => {
if(/* all ok */){
//Do something
}
else {
next(); // continue processing
return;
}
},
submitHandler // continue with this function
)
server.post('/submit', submitHandler); // also have a separate endpoint where
// submitHandler can be accessed directly
Remember that server.post(...) doesn't actually carry out that operation in that route. It just registers a route handler for some future route. So, you don't generally do things like:
server.post("/firstRoute", function(req, res, next) {
if (some condition) {
server.post("/secondRoute", ...);
}
});
This would configure a second route handler for all users based on what one user did in their /firstRoute request.
Remember a server contains a set of route handlers that work for ALL users that will use the server now and in the future. What your code proposes is that based on some data in one particular incoming request, you would change the routes that the server supports for all users. That's not how you would code a server.
If you need to maintain server-side state for a particular user such that data from one request influences how some future request works, then you would typically create a user session object, store the state for that user in the session and then one some future request, you can consult the data in the session to help you respond to the future request.
A simple example would be a shopping cart. Each time you add something to the cart, the data is added to the session object. Then, when the user asks to view the cart, you render a page by consulting the items in that user's session object. If they then ask to "check out", you purchase the items in the cart for them. You don't register and deregister routes for "view cart" and "checkout". Those routes are always registered. Instead, you use the data in the session object to help you process those requests.
If you want to run the same code from several different routes, then you just move that code into a shared function and call that shared function from more than one route handler.

Where does the callback within this express app coming from?

after some freecodecamp I started doing the Express js tutorial from MDN (https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data/Home_page 2) for some backend.
I am stuck at understanding where the callback in the async.parallel is coming from and what is represents.
If I delete the callback the site wont load, so it must have some important meaning but unfortunately I have no glue. Is it calling the function(err, results) { res.render(‘index’, […] }) to make the result availalble for data?
var Book = require(’…/models/book’);
var async = require(‘async’);
exports.index = function(req, res) {
async.parallel({
book_count: function(callback) {
Book.countDocuments({}, callback);
},
[...]
[...]
function(err, results) {
res.render('index', {
title: 'Local Library Home',
error: err, data: results
});
});
};
A Callback is a generic function invoked upon the completion of an asynchronous request. In this particular instance, the Callback is being utilized as a method of getting the data out of the asynchronous request to fill the number of books on your page. These are required because these query's are non-blocking, meaning Javascript will keep executing other surrounding code until the Callback is invoked. If you want more detail on how they work in general look here as previously mentioned by #dnp1204. I hope this answered you question.

perform a mongoose query in querystring

I'm trying to do a simple mongoose query using the query string.
This works
router.get('/', function(req,res) {
myModel.find({name:"test e"}, function(err,data){
if(err) console.log(err)
res.json(data);
});
});
This doesn't work (I get the whole collection)..
router.get('/', function(req,res) {
console.log(req.query.q)
myModel.find(req.query.q, function(err,data){
if(err) console.log(err)
res.json(data);
});
});
with this request
/api/myModel?q={name:"test e"}
I don't think it's an url encoding issue since I print the 'q' var and it looks fine server side.
Side question: if this isn't the standard mode, what's the RESTful standard way to query a db?
Edit for more general details:
I don't need a simple access by id or name like Ashley B suggests, I need a proper search engine for my db (the user, using a graphic web interface, have to be able to query each field separately, potentially)
Edit 2:
thanks to Blakes Seven I solved my initial problem, but if you know or use a better way to perform a complex query I would happy to discuss. Maybe I should expose anther resource "/api/seach"?
I think I can answer your first question by answering your second (side question).
Let's say you have a User model, normally you'd have an endpoint to get all users, like so:
router.get('/users', function(req, res) {
// find all users
users.find({}, function(err, data){
if(err) console.log(err)
res.json(data);
});
});
To find a specific user you then have an endpoint like so:
router.get('/users/:name', function(req, res) {
// get the user's name from the url and find that user
users.find({name: req.params.name}, function(err, data){
if(err) console.log(err)
res.json(data);
});
});
So rather than passing the whole query through the query string, you just find use a specific part. Allowing the users to directly access your data with their own queries makes it much much harder to secure.
I would recommend you to use some library to parse the querystrings to mongoDB queries. It would fix your problem and make your code better.
querymen would help you by transforming /myModel?q=test+e into {name: 'test e'}, giving you full control over the querystring schema.
var querymen = require('querymen')
// querymen.middleware() defines an express middleware with querystring schema
router.get('/', querymen.middleware({
q: {
type: String,
paths: ['name']
}
}), function(req, res) {
console.log(req.querymen.query) // {name: 'test e'}
myModel.find(req.querymen.query, function(err,data){
if(err) console.log(err)
res.json(data);
});
});
The proper query should look like this:
/api/myModels?name=test%20e
The myModals part is in plural.
Check here: How to design RESTful search/filtering?

Change the order of a routes variable in the front end NodeJs/Express/Jade

Novice to NodeJS and Express but lets say I have this route for mywebsite.com/tournaments.
router.get('/tournaments', function (req, res) {
TournamentController.getAllTournaments(function (err, docs) {
if (err) {
//render error
} else {
res.render('tournaments', {
data: {
title: 'mysite',
command: 'tournaments',
user: req.session.user,
tournaments: docs
}
});
}
});
});
data.tournaments is an array of tournaments in order of their date. Lets say in the front end I have a select/option form where the user can choose date/prize/players as the order to sort the tournaments by. How can I sort the data.tournaments without having to call another route or refresh the page? I'm using Jade on the front end.
You can always sort them directly in the Browser via Javascript, either do it yourself or use a plugin like datatables.
If you don't wanna do that but do it on the server, you'll need an ajax call for that and a route that handles the sorting (based on the parameters you pass), and afterwards change the DOM according to the response. This goes without refreshing the page, but you'll need a route for that, or change the existing route and extend your controller to take optional parameters, something like
router.get('/tournaments/:sort?', function (req, res) {
TournamentController.getAllTournaments(req.param('sort'), function (err, docs) {
/* ... */
});
});

Response headers of previous request affecting current request

The following code is the user-facing part of a new node app we are building:
var loadInvoice = function(req, res, next) {
Invoice.findById(req.params.invoiceId, function (err, invoice) {
if (err) {
res.send(404, 'Page not found');
} else {
req.invoice = invoice;
next();
}
});
};
app.namespace('/invoices/:invoiceId', loadInvoice, function () {
app.get('', function(req, res){
var templateVals = {
//some template data
};
res.render('paymentselection', templateVals);
});
app.post('', function(req, res){
var data = {
// some data for the apiCall
};
someAPI.someRequest(data, function(err, data) {
console.log(res.status());
res.redirect(data.url);
});
});
});
The first method returns a confirmation page where the user presses a button to post to the same url, which triggers a redirect to an external website.
This all works exactly once. Every second request will crash the app with the message Cant set headers after they are sent. After carefull inspection of the code I could find no reason for this to happen so I added the console.log line which indeed confirms the location header has been set. But it is set to the value i got from someAPI on the previous request not the current one.
This makes absolutely no sense to me. I do not store this value anywhere nor do I do caching or persistence of this data in any way.
Does anybody know what could be causing this?
I use express, express-namespace, mogoose and swig
I found out the problem was being caused bij the 'Restler' libaray used within 'someAPI'. I have no idea how this is possible but swapping it out with something else fixed the problem.

Resources