I'm curious if there is any way to issue an internal request in express without going through all the actual overhead of a real request. An example probably shows the motivation better:
app.get("/pages/:page", funciton(req, res)
{
database_get(req.params.page, function(result)
{
// "Page" has an internal data reference, which we want to inline with the actual data:
request(result.user_href, function(user_response)
{
result.user = user.response.json;
res.send(result);
});
});
});
/// ....
app.get("/user/:name", function() ... );
So what we have here is a route whose data requires making another request to get further data. I'd like to access it by just doing something like app.go_get(user_href) instead of the heavy weight actual request. Now, I've asked around and the going strategy seems to be "split out your logic". However, it actually requires me to duplicate the logic, since the recursive data is referenced properly through URLs (as in the example above). So I end up having to do my own routing and duplicating routes everywhere.
Can you avoid the overhead of a real request? No. If you need the href from the first request in order to go to get a user object, you absolutely need to follow that link by making a second "real request."
If you have a database of users, you CAN avoid the request by including the user's ID on the page, and making a regular database call instead of following your own href.
Demo refactor on splitting out logic:
// Keep as little logic as possible in your routes:
app.get('/page/:page', function(req, res){
var pageId = req.params.page;
makePage(pageId, function(err, result){
if(err){ return res.send(500) }
res.send(result)
})
})
// Abstract anything with a bunch of callback hell:
function makePage(pageId, callback){
database_get(pageId, function(result) {
// Since it's only now you know where to get the user info, the second request is acceptable
// But abstract it:
getUserByHref(result.user_href, function(err, data){
if(err){return callback(err)};
result.user = data.json;
callback(null, result);
});
});
}
// Also abstract anything used more than once:
function getUserByHref(href, callback){
request(href, function(err, response, body){
if(response.statusCode != 200){
return callback(err);
}
var user = JSON.parse(body);
return callback(null, user);
})
}
// It sounds like you don't have local users
// If you did, you would abstract the database call, and use getUserById
function getUserById(id, callback){
db.fetch(id, function(err, data){
return callback(err, data);
})
}
I've made a dedicated middleware for this uest, see my detailed answer here: https://stackoverflow.com/a/59514893/133327
Related
I'm making a webpage with Node JS with dustjs and PostgreSQL. How do I make a search query in the html, so I can pass the value to the app.get
Do I need to use JQuery?
app.get('/teachers', function(req, res){
pool.connect(function(err, client, done){
if(err) {
return console.error("error", err);
}
client.query('SELECT * FROM teachers', function(err, result){
if(err){
return console.error('error running query', err)
}
res.render('teacherindex', {teachers: result.rows});
done();
});
});
});
app.get('/teachers/:str', (req,res)=>{
pool.connect((err, client, done) => {
if (err) throw err
client.query('SELECT * FROM teachers WHERE name = $1', [req.query.namesearch], (err, result) => {
done()
if (err) {
console.log(err.stack)
} else {
res.render('teacherindex', {teachers: result.rows});
}
})
})
})
This is my JQuery
$("#myBtn").click(function(){
var str = $("#myInput").val();
var url = '/teachers/'+str;
if(confirm('Search Record?')){
$.ajax({
url: url,
type: 'put',
success: function(result){
console.log('Searching');
window.location.href='/teachers';
},
error: function(err){
console.log(err);
}
});
}
});
My HTML
<input type="text" id="myInput" data-id="namesearch">
<button type="button" id="myBtn">Show Value</button>
Thank you!
FINAL ANSWER:
Ok so it turns out the issue you were having was something completely different. You are trying to use server side rendering for this, and I was showing you how to render the retrieved data on the client side.
I have forked, and updated your repo - which can be found at the link below..
Please review my changes and let me know if you have any questions.
Working repo: https://github.com/oze4/hanstanawi.github.io
Demo Video: https://raw.githubusercontent.com/oze4/hanstanawi.github.io/master/fake_uni_demo.mp4
EDIT:
I went ahead and built a repository to try and help you grasp these concepts. You can find the repo here - I tried to keep things as simple and understandable as possible, but let me know if you have any questions.
I had to make some minor changes to the paths, which I have commented explanations on the code in the repo.
I am using a "mock" database (just a JSON object in a different file) but the logic remains the same.
The index.js is the main entry point and contains all route data.
The index.html file is what gets sent to the user, and is the main HTML file, which contains the jQuery code.
If you download/fork/test out the code in that repo, open up your browsers developer tools, go to the network tab, and check out the differences.
Using req.params
Using req.query
ORIGINAL ANSWER:
So there are a couple of things wrong with your code and why you are unable to see the value of the textbox server side.
You are sending a PUT request but your server is expecting a GET request
You are looking for the value in req.query when you should be looking for it in req.params
You are looking for the incorrect variable name in your route (on top of using query when you should be using params) req.query.namesearch needs to be req.params.str
See here for more on req.query vs req.params
More detailed examples below.
In your route you are specifying app.get - in other words, you are expecting a GET request to be sent to your server.. but your are sending a PUT request..
If you were sending your AJAX to your server by using something like /teachers?str=someName then you would use req.query.str - or if you wanted to use namesearch you would do: /teachers?namesearch=someName and then to get the value: req.query.namesearch
If you send your AJAX to your server by using the something like /teachers/someName then you should be using req.params.str
// ||
// \/ Server is expecting a GET request
app.get('/teachers/:str', (req, res) => {
// GET THE CORRECT VALUE
let namesearch = req.params.str;
pool.connect((err, client, done) => {
// ... other code here
client.query(
'SELECT * FROM teachers WHERE name = $1',
// SPECIFY THE CORRECT VALUE
namesearch,
(err, result) => {
// ... other code here
})
})
});
But in your AJAX request, you are specifying PUT.. (should be GET)
By default, AJAX will send GET requests, so you really don't have to specify any type here, but I personally like to specify GET in type, just for the sake of brevity - just more succinct in my opinion.
Again, specifying GET in type is not needed since AJAX sends GET by default, specifying GET in type is a matter of preference.
$("#myBtn").click(function () {
// ... other code here
let textboxValue = $("#myTextbox").val();
let theURL = "/teachers/" + textboxValue;
// OR if you wanted to use `req.query.str` server side
// let theURL = "/teachers?str=" + textboxValue;
if (confirm('Search Record?')) {
$.ajax({
url: theURL,
// ||
// \/ You are sending a PUT request, not a GET request
type: 'put', // EITHER CHANGE THIS TO GET OR JUST REMOVE type
// ... other code here
});
}
});
It appears you are grabbing the value correctly from the textbox, you just need to make sure your server is accepting the same type that you are sending.
I have anPOST api endpoint lets say /users to fetch the list of users.
It is POST because body of the request is very huge and might not fit in url for GET request.
suppose the body of user POST have a key called age , which should give me user of certain age ie kind of filtering
now in express i have route like
app.post('/users', function(r,res){
// function body
})
and i cant actually put any code inside that function body
so i was able to intercept the request by using one more handler for /users and putting it before the original handler but obviously it intercepts all /users requests and breaks earlier functionality
how can i intercept only the request with particular age and then pass through other requests to the original handler, so that original functionality keeps working?
I want to know how can i do this using route handlers and not middlewares ?
i cant mess with the url or request body also
First off, this sounds like a really bad design so really the better way to fix things is to just fix the URL design so you don't have this conflict between code you can and can't modify. I say this because it sounds like you're trying to "hack" into something rather than make a proper design.
If your code is using the regular body-parser middleware, then the body of the post will already be parsed and in req.body. So, you can look for the desired parameter in req.body.age and check its value.
If it meets your criteria, then you can process the request and you're done. If it doesn't meet your request, then you call next() to continue processing to other request handlers.
// make sure this is defined BEFORE other /users request handlers
app.post('/users', function(req, res, next) {
// test some condition here
if (+req.body.age > 30) {
// process the request and send a response
res.send("You're too old");
} else {
// continue processing to other request handlers
next();
}
})
The way I deal with this is if I have a route that works, and I need something else, I add another route that is similar. This way you leave the original alone - which provides a working service. This is what I think you re describing.
You can call routes anything you like. If you want a list of users you can pass a variable like this:
$.get('/contactCard/'+qry);
app.get('/contactCard/:sort', function(req, res) {
var cId = req.params.sort;
console.log('cId: ' + cId);
then you set up your search query and go get the data a bit like this:
let params = {
TableName: ddbTable,
ProjectionExpression : "cEmail,Forename,Surname",
KeyConditionExpression: "ID = :e ",
ExpressionAttributeValues: {
":e" : cId
}
};
console.log("params", JSON.stringify(params, null, 2));
docClient.query(params, function(err, data) {
then you check for error or success:
if (err) {
console.log("Error:", JSON.stringify(err, null, 2));
} else {
console.log("Success", JSON.stringify(data, null, 2));
let contacts = data;
then here you render to the page you want and pass the data as you wish.
res.render('members/contactcard', {
contacts:contacts,
static_path: '/static'
});
}
});
I'm using the MEAN stack for a web app. In my controller.js, I have the following:
var refresh3 = function() {
$http.get('/users').success(function(response) {
console.log("I got the data I requested");
$scope.users = response;
$scope.contact3 = "";
});
};
refresh3();
This pulls back every object in the "users" collection from my MongoDB. How could I add parameters to this to bring back, for example, only objects where "name" : "Bob" ? I have tried adding parameters in the '/users' parentheses using:
$http.get('/users', {params:{"name":"Bob"}})
I've also tried variants of that, but no luck. Any help would be appreciated!
If your server is receiving the data
(and it should, as $http.get('/users', {params:{"name":"Bob"}}) is correct)
On server side, make use of the query string:
req.query
like so:
app.get('/users', function(req,res){
if(req.query.name){
db.users.find({"name":req.query.name},function (err, docs) { console.log(docs); res.json(docs); });
}
else{
db.users.find(function (err, docs) { console.log(docs); res.json(docs); });
}
});
WHAT WAS THE ISSUE?
You hinted in your comments that your server was set to respond to the app.get('/users') GET request like so:
db.users.find(function (err, docs) {
// docs is an array of all the documents in users collection
console.log(docs); res.json(docs); }); });
So I believe that your angularjs $http get is correct, and your server is receiving the parameters {"name":"Bob"} as it should;
it just doesn't know what to do with them:
all it is set to do is to return the whole collection in the particular case of a app.get('/users') GET request.
HOW TO CONFIGURE YOUR SERVER FOR REST
You do not have to re-invent the wheel on the server.
Rather, you could consider using a middleware to automate the task (in the present case, the task is to issue a proper MongoDb request when you receive a get query with parameters from the client)
e.g. express-restify-mongoose middleware
I am having a tough time finding a solution to my problem online and was hoping someone on here might be able to help me. I have an express route that does a few API requests for different JSON objects. I would like to build a JSON response for my client side view but all of my attempts so far either yield previous request data or no data at all.
So my question to you JavaScript pros using node/express js. How do you sync up multiple sources of JSON objects into one single object to be returned to the client side in one response? Is there a library or some callback magic that you use?
Thanks in advance for any and all help!
Async is one of the more popular libraries for this purpose. There are many other async and promise libraries that can help with this. There are different methods that have different behaviors depending on what you need. I think series method is what you need, but check the documentation carefully.
var async = require('async');
var request = require('request');
app.get('endpoint',function(req, res){
async.series([
function(callback){request.get('url',callback)},
function(callback){request.get('url2',callback)},
function(callback){request.get('url'3,callback)},
],
function(err,results){
//handle error
//results is an array of values returned from each one
var processedData = {
a: results[0],
b: results[1],
c: results[2]
};
res.send(processedDAta)
})
})
You could also do this yourself (and is good practice for learning how to organize your node.js code).. callbackhell is a good write up of using named functions and modules to organize callbacks.
Here is a one possible way to do it. (Not tested)
app.get('/endpoint',function(req, res){
var dataSources = ['url1', 'url2',url3];
var requestData = [];
processRequestData = function(){
//do you stuff here
res.send(processedData);
};
dataSources.forEach(function(dataSource){
request.get(dataSource,function(err,result){
//handle error
requestData.push(result);
if(requestData.length == dataSources.length){
processRequestData();
}
})
})
});
Async.js(https://github.com/caolan/async) can help with tasks like this. A quick example:
app.get('/resource', function(req, res){
// object to send back in the response
var results = {};
// helper function to make requests and handle the data to results object
var getData = function(url, propertyName, next){
http.get(url, function(err, data){
// let async.js know if there is an error
if(err) return next(err);
// save data to results object
results[propertyName] = data;
// async.js needs us to notify it when each operation is complete - so we call the callback without passing any data back to
next();
});
};
// array of operations to execute in series or parallel
var operations = [];
operations.push(function(next){
getData('http://url-one', 'users', next);
});
operations.push(function(next){
getData('http://url-two', 'posts', next);
});
// async.js has a few options on how to execute your operations - here we use series
async.series(operations, function(err){
if(err){
throw err;
}
// if we get to this point, all of the operations have exectued and called
// next() without any errors - send the response with a populated results object.
res.send(results);
});
});
I haven't actually tried this code yet but it should give you a good idea on how to do it.
I'm using urllib to make a request to a webpage and I'm trying to return it's headers like so:
var getHeaders = function(webpage){
var info = urllib.request(webpage, {}, function(err, data, res){
// console.log(res.headers); works fine and shows them
return res.headers; // I thought it should make the info variable have the headers information
});
return info;
}
Now when I try to get the headers like maybe set-cookie of a webpage I intended it to return the that from the website but it doesn't, so is there a way to return the headers or is it just not possible to do that?
In Node pretty much everything is done asynchronously, so you'll just need your function to be asynchronous.
var getHeaders = function (webpage, done) {
urllib.request(webpage, {}, function(err, data, res){
done(err, res.headers);
});
}
The traditional pattern is to use callbacks that return an error as the first argument (or a falsy value in case everything went well), and whatever you need to return afterwards.
Consuming the method is then very similar to what you had to do with the urllib thing.
getHeaders(webpage, function (err, headers) {
if (err) {
throw err; // or, you know, deal with it.
}
console.log(headers);
});