how to allow pass through in express handlers? - javascript

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'
});
}
});

Related

How do I search for a data in the database with Node JS, Postgres, and dust js

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.

Changing req object within api server?

I recently picked up a client who has a dev team. I am working on a website that has already been developed and am running into some things that are slightly strange to me.
I've always though it was essentially bad to mess with the request object within route handling (I could be completely wrong here).
The following code reaaaaally confuses me as I am not sure why they are assigning the req.query.msg to something instead of just creating a variable and passing it through on the ejs page render.
/********************************************************
* *
* CHANGE PASSWORD ROUTE THAT POSTS THE NEW PASSWORD TO *
* DATABASE. *
* *
********************************************************/
app.post('/client/password', function (req, res) {
var url = URLS.ClientChangePW;
if(req.session.securityquestions[0].SSN !== "null" || req.session.securityquestions[0].SSN !== "undefined"){
if(req.body.pwd !== req.body.pwdconf){
res.redirect('/client/changePassword' + config.PWD_MISMATCH);
} else {
var ssn = req.session.securityquestions[0].SSN;
while(ssn.length < 9){
ssn = "0" + ssn;
}
url = url.replace("#ssn", ssn);
url = url.replace("#newpw", req.body.pwd);
}
request.put(url, function (err, xres, body) {
var data = JSON.parse(body);
if(data.status === 200){
email(req.session.securityquestions[0].EMAIL, "none", "forgotpw", function(result){
if(result){
req.query.msg = "Your password has been reset.";
} else {
req.query.msg = "Request unsuccessful. Please call number here for assistance.";
}
res.render('pages/login', {
session: req.session,
msg: req.query.msg
});
});
} else {
req.query.msg = "Request unsuccessful. Please call number here for assistance.";
res.render('pages/login', {
session: req.session,
msg: req.query.msg
});
}
});
}
});
Again, I have never really messed with the req object so I could be wrong. I always thought the client sets up the request and we use that to send a response.
I am not sure why they are assigning the req.query.msg to something instead of just creating a variable and passing it through on the ejs page render.
There does not appear to be any reason to be assigning to the req.query.msg property here. If this were my code, I'd be using a separate local variable for it.
Again, I have never really messed with the req object so I could be wrong. I always thought the client sets up the request and we use that to send a response.
Though this is not what is happening here, it is common in Express development to use middleware that sets state on the req object for request handlers further down the routing stack to use. The req object is the canonical object where you keep request-specific state while processing the request. If you only have one request handler function working on the request, then there's no reason to put state on the req object as you can just use local variables in that one request handler function. But, if you're using middleware whose job it is to set things up before ultimately getting to a request handler, then the req object is where that setup state is usually put. You'll notice that req.session is also used in this code. That .session property was put there by some middleware upstream in the request processing.
So, it IS common to add state to the req object when using middleware. But, in the .msg property example in the code you show, there is no particular reason to put that on the req object as its value is only needed in the local function so it can just as easily (and I would argue more clearly) be in a local variable.

ExpressJS + JWT. What's the proper way to get auth data?

Let's jump to an example. I'll skip some parts like imports, exports.
I have a controller called controllers/book.js with one route:
router.get('/my-books', function(req, res) {
if(Auth.authenticated(req,res)) {
Book.getMyBooks(function(rows) {
response.operationSucceed(req, res, rows);
});
}
});
Then, in a model called models/book.js I have that function:
function getMyBooks(successCallback) {
db.query('SELECT * FROM book b WHERE b.id_user=?', [Auth.getLoggedUID()], function(rows) {
successCallback(rows);
});
}
My question is about Auth.getLoggedUID function.
Assuming that I have a JWT authentication and assuming that I have an UID in payload (is that even acceptable?), what's the best option to retrieve it? Is there any, EXCEPT passing the req every time to every function where I need auth data?
I may have a function execution inside a function, do I need to pass the req through both of them to get the user ID? Like this?:
function getBooks(req) {
getMyBooks(req);
getCriminalBooks(req);
getEvenOtherBooksByAuthor(req, authorId);
}
Honestly I wouldn't like that.
Maybe my whole concept is wrong and I should be doing things differently?
Can someone point me the right direction in scenarios like this?
You can pass UID in header and retrieve it inside your controller as:
var uid =req.header('UID');
Then pass this UID where ever you want there is no need to carryforward whole req object everywhere.
You can use a middleware function. Let's say that every request that hits your endpoints, will have a token which you should check and possibly decode it. After that, you can set the decoded content to the req object. So something like this:
app.use(function(req, res, next) {
// get the token from the request headers most likely.
// verify and decode the token
// set the decoded content to the request
var payload = ..
req.payload = payload;
});
After this you can access the payload in every single endpoint you have. So for example in some controller you can do:
app.get('/hey', function(req, res) {
var payload = req.payload;
});

Express.js middleware extra (fourth) argument

On the optional arguments to an Express.js middleware , probably a fourth one apart from being an error handling middleware, I have a use case where it comes useful. It may be achieved by a different way, but anyways.
I want to make some api routes permission checked, the middleware I wish to write should look up database for a requesting user for how many reputation points (integer) he/she has. I defined an object to hold privileges and reputation points required as key value pairs. Later, middleware should look up this privileges object to see if the user has greater than or equal reputation points for a corresponding action. I want to pass this corresponding action name which is a key in the privileges object as a different string for each route. This can be achieved by sending the string actionNames via requests to the routes, but I find it less secure (data in request can be tampered to have an action name that a malicious user has access to and to have required data fields for another wished but not permitted action).
All the case is just like that of SE's.
By the way, apparently I also need route based (not Router based) middleware mounting, I am not sure if Express.js supports, but this is another story.
Maybe one can describe and ask this use case as can I parametrize a middleware function with my parameters, not just by incoming req and res objects?
How can I achieve this use case with Express.js middleware? Or should I use another mechanism?
/// Privilege Check middleware
// used per route with corresponding actionName
// signature function (request, response, next, actionNameOneOfKeysInPrevilegesObject::String)
var privilegeCheck = function (req, res, next, actionName) {
db.one(
`
SELECT reputation FROM users WHERE id = $(id)
`,
{id: req.user.id} // remember req.user was set by jwt.sign(data) during login or signup with demanded data; here it is {id:, name:, username:,}
)
.then(function (data) {
if(data >= privileges[actionName]) {
next();
}
else {
res.status(403).json({errorMessage: "You need to have " + privileges.questionUpvote + " reputation to upvote."});
}
})
.catch(function (error) {
console.error(error);
})
};
// reputations needed for privileged actions
var privileges =
{
questionAsk: 5,
answer: 15,
acceptAnswer: 0,
comment: 5,
questionEdit: 20,
answerEdit: 20,
commentsEdit: 0,
postsUpvote: 30,
postsDownvote: 30,
commentsUpvote: 5,
questionUpvote: 10,
questionDownvote: 125,
}
Use route-specific middleware like so:
function privilegeCheck(actionName) {
return function (req, res, next) {
db.one(
`
SELECT reputation FROM users WHERE id = $(id)
`,
{id: req.user.id} // remember req.user was set by jwt.sign(data) during login or signup with demanded data; here it is {id:, name:, username:,}
)
.then(function (data) {
if(data >= privileges[actionName]) {
next();
}
else {
res.status(403).json({errorMessage: "You need to have " + privileges.questionUpvote + " reputation to upvote."});
}
})
.catch(function (error) {
console.error(error);
})
;
}
};
// Then, for each of your routes, invoke your privilegeCheck() function
// as an "in-between" argument between your path and your route handler
// function.
app.get('/my/route', privilegeCheck("myActionName"), (req, res) => {
res.status(200).send('handled /my/route');
});
See the "Application-level middleware" section at http://expressjs.com/en/guide/using-middleware.html, starting from "This example shows a middleware sub-stack that handles GET requests to the /user/:id path."
But the documentation there does not show that you can chain functions in the app.get(). You can see that at
https://scotch.io/tutorials/route-middleware-to-check-if-a-user-is-authenticated-in-node-js
In fact, you can have as many "middleware" (ie three-argument function) arguments to any of Express's route handling functions (get(), put(), post(), delete()) as you need.
Unfortunately, Express is coded so that if your middleware function has exactly four parameters, it is considered error handling middleware. IMO this was not a good design decision.
To do what you want to do, the canonical way to do is to attach data to the request stream (req).
For example:
app.use(function(req,res,next){
req.data = {foo:'bar'};
});
app.use(function(req,res,next){
const data = req.data;
}):
Coming from languages like Java, I thought this was some of the craziest coding ever, but it works because req is unique to that request + JS is "single-threaded".

Using the PUT method with Express.js

I'm trying to implement update functionality to an Express.js app, and I'd like to use a PUT request to send the new data, but I keep getting errors using PUT. From everything I've read, it's just a matter of using app.put, but that isn't working. I've got the following in my routes file:
send = function(req, res) {
req.send(res.locals.content);
};
app.put('/api/:company', function(res,req) {
res.send('this is an update');
}, send);
When I use postman to make a PUT request, I get a "cannot PUT /api/petshop" as an error. I don't understand why I can't PUT, or what's going wrong.
You may be lacking the actual update function. You have the put path returning the result back to the client but missing the part when you tell the database to update the data.
If you're using MongoDB and ExpressJS, you could write something like this :
app.put('/api/:company', function (req, res) {
var company = req.company;
company = _.extend(company, req.body);
company.save(function(err) {
if (err) {
return res.send('/company', {
errors: err.errors,
company: company
});
} else {
res.jsonp(company);
}
})
});
This mean stack project may help you as it covers this CRUD functionality which I just used here swapping their articles for your companies. same same.
Your callback function has the arguments in the wrong order.
Change the order of callback to function(req, res).
Don't use function(res, req).
Also if you want to redirect in put or delete (to get adress), you can't use normal res.redirect('/path'), you should use res.redirect(303, '/path') instead. (source)
If not, you'll get Cannot PUT error.
Have you been checking out your headers information?
Because header should be header['content-type'] = 'application/json'; then only you will get the update object in server side (node-express), otherwise if you have content type plain 'text/htm' like that you will get empty req.body in your node app.

Categories

Resources