Is it possible to use multiple .get on app.route? - node.js

app.route('/users')
.post(user.post)
.get(user.get)
.get(user.everyone)
.put(user.update)
.delete(user.delete);
I have ran into the problem of my function using two res.send, so I am getting the 'Error: cannot set header after they are sent.' error, to fix this I have turned it into two functions which I am trying to use two .get on the app.route, but it seems I can only use one as when I use two the second one doesn't work.
Is there a way I could use two .get on one app.route?
If not, what are my options to get around this problem?

You need to create separate routes for each api endpoint like this:
app.route('/users').get(req, res) => {
//Get all users
});
app.route('/users:/id').get(req, res) => {
//Get specific user
});
app.route('/users').post(req, res) => {
//Create new user
});
app.route('/users/:id').put(req, res) => {
//Update user
});
app.route('/users/:id').delete(req, res) => {
//Delete user
});

Once res.send is called, means your server has sent the response to the browser or whatever. you can't change the already sent response and its header.
You can use multiple callbacks on one route and one method(post,get)
An array of callback functions can handle a route. For example:
var cb0 = function (req, res, next) {
console.log('CB0')
next()
}
var cb1 = function (req, res, next) {
console.log('CB1')
next()
}
var cb2 = function (req, res) {
res.send('Hello from C!')
}
app.get('/example/c', [cb0, cb1, cb2])

yes, you can use multiple HTTP requests either its .get or .post but with different params. or routes.

Related

Handle middleware using chai-http Node Js Express

I am facing issue I am testing an api using chai-http but it struck in middlewear means it does not call next() function. Not sure how to handle this, little guide may be helps me alot. Here is my code may be it helps you understand better.
Middleware
function middleware(req, res, next) => {
// doing some stuff
console.log("step 1") // prints
next()
}
API
router.get('/orders', middleware, (req, res) => {
// some stuff
console.log("step 2") // not prints
res.send(result)
})
Test Case
let order = new Order(mockOrder);
order.save((err, order) => {
chai.request(server)
.get('/orders')
.end((err, res) => {
// some stuff
})
})
This update might be useful for you.It is from github.
Another large breaking change is that the chai.request(server) object will automatically close the server connection after the first request. This means if you have code like this:
you'll need to change it to:
const request = chai.request(app).keepOpen()

Wait for data to be written in POST in nodejs json-server

I am using the json-server module to create a server that accepts POSTS, GET from a json file.
I see that on a POST, the data is being written to the JSON file, as expected. Is there a way for me to wait for the data to be written before calling another function that acts on the data?
server.use((req, res, next) => {
if (req.method === 'POST') {
startCol.masterProc();
}
})
The masterProc function works on the json file, but it seems to be called before the data is actually persisted to the json file. This might be a nodejs question, but could you please help me with actually waiting for the persistence before a function call?
Thanks!
The middleware you created intercepts the request before it gets to your db.json, so that's why when calling startCol.masterProc() the data is not there yet.
I believe you'll need to let the request pass through (that is, remove server.use()) and put your code in the router.render() method, like so:
const router = jsonServer.router('db.json')
router.render = (req, res) => {
if (req.method === 'POST') {
startCol.masterProc();
}
}
But just out of curiosity, what happens if you call next() on your server.use right before calling startCol.masterProc()? Like so:
server.use((req, res, next) => {
next();
if (req.method === 'POST') {
startCol.masterProc();
}
})

ExpressJS Handle multiple get route with the same endpoint

I build an application API with expressJS. In this application there is a public part and an private part.
Both part need to display a list of user. But only the private part can display the full list.
I use express-authorize middlewar to handle autorization. So i have defined two authorization :
'public:users:list'
and
'private:users:list'
In express i have tried to write routes like this :
router.route('/users')
.get([passport.authenticate('jwt', { session: false }), authorization.authorizer.isPermitted('private:users:list')], (req, res) => {
getUserList(req, res);
})
.get([passport.authenticate('jwt', { session: false }), authorization.authorizer.isPermitted('public:users:list')], (req, res) => {
req.query.filter.roles.name = 'user'
getUserList(req, res);
});
But this does not work. Request fail as unauthorized on the first get and never goes to the second. Is there a mean to make this works? Or any others techniques. If possible avoid to create another endpoint like (router.route('usersPublic').
Thx
EDIT:
I have override the onDenied function like this and make the change in the route.
var authorizer = new authorizer.Authorizer(authorizer.options);
authorizer.options.onDenied = function (req, res, next) { next('route') };
authorizer.options.withSubject = function (req, res, done) {}
That seems to work. But if no route are valid. i get a 404 error instead of 403. Any suggestion?
EDIT2:
onDenied should not be set at global but inside the route itself like this
router.route('/users')..get([passport.authenticate('jwt', { session: false }), authorization.authorizer.onDenied((req, res, next) => next('route')).isPermitted('private:users:list')], (req, res) => {
getUserList(req, res);
});
router.route('/users')..get([passport.authenticate('jwt', { session: false }), authorization.authorizer.isPermitted('public:users:list')], (req, res) => {
req.query.filter.roles.name = 'user'
getUserList(req, res);
});
I'm not familiar with express-authorize, so the following is pieced together from its source and may not work.
First, you need to change how authentication mismatches are handled. The default is to redirect to /login, but you need to change that so the request gets passed to the next route chain:
let myAuthorizer = authorization.authorizer.onDenied((req, res, next) => next('route'));
Then you need to set up two separate routes for /users:
router.route('/users').get([passport.authenticate('jwt', { session: false }), myAuthorizer.isPermitted('private:users:list') ], (req, res) => {
getUserList(req, res);
})
router.route('/users').get([passport.authenticate('jwt', { session: false }), authorization.authorizer.isPermitted('public:users:list')], (req, res) => {
req.query.filter.roles.name = 'user'
getUserList(req, res);
});
The next('route') is explained here, but it basically means that instead of passing the request to the next handler in the current chain, it's passed to the next full route chain (which in this case is the second router.route('/users') if private access is denied).

modify response in express/nodejs

i am making multiple streamdata in Nodejs using express.
this is how i make a url:
app.get('/temp/1', function(req, res){
res.send('hello, i am not modified')
})
my question is: is it possible to modify the response of that url?
i tried like this:
app.get(/modify/1, function(req, res){
app.get('/temp/1', function(req, res){
res.send('hello, i am modified')
})
res.send('done');
}
So i would think that the response is changed, but nothing happens actually.
is there a way to achieve it?
Here's an example using express-modify-response:
const modifyResponse = require('express-modify-response');
...
let modify = modifyResponse(
function(req, res) { return true }, // always modify the response
function(req, res, body) { return 'hello, i am modified' } // the new response
);
app.get('/temp/1', modify, function(req, res){
res.send('hello, i am not modified')
})
EDIT: second attempt. You have an endpoint /temp/1 that sends a particular response, and you want an endpoint /modify/1 that will take that response and modify it.
This calls for some abstraction:
function someFunction(id) {
return 'hello, i am not modified';
}
app.get('/temp/1', function(req, res) {
res.send(someFunction(1));
});
app.get('/modify/1', function(req, res) {
let value = someFunction(1);
// Remove the word `not`.
value = value.replace(/not /, '');
res.send(value);
});
So both handlers use the same function, that provides the actual output, but /modify/1 modifies the output before returning it to the client.

Express logging with storing the request information

Background
Yes, there are a lot of different Node.js logging library winston, bunyan and console.log. It's easy to log down the information of the specific request when it has called and when and what information would be in response.
The problem
The problem begins with the sub function calls. When under one request your calling multiple functions which also uses the same logging, how would you pass the request meta - data to these log calls (function parameters seems to be one possible way but these are really messy) ?
Example
Small visual for coders:
// Middleware to set some request based information
app.use(function (req, res, next) {
req.rid = 'Random generated request id for tracking sub queries';
});
app.get('/', function (req, rest) {
async.series({
'users': async.apply(db.users.find),
'posts': async.apply(db.posts.find),
}, function (err, dbRes) {
console.log('API call made ', req.rid)
res.end(dbRes);
});
});
// Now the database functions are in other file but we also need to track down the request id in there
(db.js)
module.exports = {
users: {
find: function () {
console.log('Calling users listing ', req.rid); // ERROR this is not possible to access, not in this scope
// Make query and return result
}
},
posts: {
find: function () {
console.log('Calling post listing ', req.rid); // ERROR this is not possible to access, not in this scope
// Make query and return result
}
}
};
You can log your requests with simple conf in your app.js with;
app.use(function(req, res, next){
console.log('%s %s', req.method, req.url);
next();
});
However, you need to provide logs for specific functions in your controller.

Resources