Express restful routes pathing issue - node.js

I'm having an issue with my Rest service. The two routes I'm trying to have are:
router.get('/stocks/:ticker', function(req, res){
and
router.get('/stocks/watchlist/', function(req, res){
when I call
http://localhost:8000/stocks/watchlist
my API believes 'watchlist' is the ticker symbol and goes to the incorrect route. I'm sure I'm missing something fundamental here.

Move the watchlist route in front of the other one.
That way it will get matched before the more open ended one gets to see the request at all. Routes are compared in the order they were defined and the first one to match gets it first. If it doesn't call next() to continue routing, then it will be the only one to see the route.

Related

How can I share a root path using Express?

I want to be able to have two endpoints sharing the root path, both for different purposes.
One will be for obtain a user via its ID and the other via token.
Right now I have the following routes:
router.get('/:idUser', paramValidationRules(), validate, verifyJWT, getUserFromId);
router.route('/me').get(verifyJWT, getUserFromToken);
Running tests, the 'me' on the second route is considered a parameter and its redirected to the first route. Is possible to share a root path specifying that one will be used strictly to 'me' and the other one to an integer?
First, you're always hitting /:idUser before /me, so it will always stop at /:iduser and never react me.
So to solve the issue that you can never access /me, put the /me route declaration before /:idUser.
As to only catching numbers, there isn't a built in way, but you could use middleware (and change the order of your routes to the original):
router.get('/:idUser', function(req, res, next) {
req.id = /^\d+$/.test(req.params.idUser);
next();
}, paramValidationRules(), validate, verifyJWT, getUserFromId);
Then, (unfortunately) in all your middleware and handler (or just the handler) in the opening of the function add:
if(!req.id)
return next();

Using res.locals in node.js model file

I am overall clueless about how and why you set up a node.js app, and how any of the app.use functions work - the tutorials on it don't explain the why of anything.
Anyway, I have socket.io, res.locals and index.js set up like so in the app.js root file.
const sockets = require('./models/socket')(io)
app.use(function (req, res, next) {
res.locals.user_id = req.session.user_id;
next();
});
const routes = require('./routes/index');
app.use('/', routes);
I'd like to be able to access res.locals in the socket.js model, like I can in index.js found in the routes folder.
I can't guess how to go about doing this. If anybody is able to explain how and why I can or can't that would be a bonus. Thanks!
Welcome to Expressjs, there are a few fundamentals you should probably research before going any further, they'll help solve some of your confusion. I'll give a brief explanation of them but I suggest you do further research. I'll then answer your actual question at the end.
Middleware and app.use
Expressjs is built upon an idea that everything is just "middleware". Middleware is a function which runs as part of a request chain. A request chain is essentially a single client request, which then goes through a chain of a number of middleware functions until it either reaches the end of the chain, exits early by returning a response to the client, or errors.
Express middleware is a function which takes the following three arguments.
req (request) - Representing the request made by a client to your
server.
res (response) - Representing the response you will return to
the client.
next - A way of telling express that your current
middleware function is done, and it should now call the next piece of
middleware. This can either be called "empty" as next(); or with an
error next(new Error());. If it is called empty, it will trigger
the next piece of middleware, if it is called with an error then it
will call the first piece of error middleware. If next is not called at the
end of a piece of middleware, then the request is deemed finished and the
response object is sent to the user.
app.use is a way of setting middleware, this means it will run for every request (unless next() is either not called by the previous piece of middleware for some reason, or it's called with an error). This middleware will run for any HTTP request type (GET, POST, PUT, DELETE, etc).
app.use can take multiple arguments, the important ones for beginners to learn are: app.use(func) and app.use(path, func). The former sets "global" middleware which runs no matter what endpoint (url path) the client requests, the latter (with a specific path) is run only if that specific path is hit. I.e. app.use('/hello', (req, res, next) => { res.send('world'); }); will return "world" when the endpoint "/hello" is hit, but not if the client requests "/hi". Where as app.use((req, res, next) => { res.send('world'); }); would return "world" when you hit any endpoint.
There are more complex things you can do with this, but that's the basics of attaching middleware to your application. The order they are attached to the application, is the order in which they will run.
One more thing, this will blow your mind, an express application made with the standard const app = express() can also be used as middleware. This means you can create several express applications, and then mount them using app.use to a single express application. This is pretty advanced, but does allow you to do some really great things with Express.
Why can you not access res.locals in socket.io? (The real question)
Within your middleware handler, you are setting up a res.locals.use_id property. This only lives with that individual request, you can pass it around as long as the request is alive by passing it into other functions, but outside of that request it doesn't exist. res is literally the response object that tells Express how to respond to the clients request, you can set properties of it during the request but once that HTTP request has ended it's gone.
Socket.io is a way of handling web socket requests, not standard HTTP requests. Thus, in a standard express HTTP request you will not be able to hand off the connection to anything with socket.io, because the connection is a single short lived HTTP request. Likewise, you won't be able to do the same the other way.
If you wish to find the users id in a socket.io request, you'll have to do this within the socket.io request itself.
Right now, you're entering a piece of middleware for an Express.js request, you are then calling next() which runs the next piece of express middleware, at no point does it cross over into Socket.io realms. This is often confused by tutorials because Socket.io can handle requests across the same port as Express is listening on, but the two are not crossed over. So you will need to write separate middleware for both Express.js requests chains, and socket.io request chains. There are ways of writing this code once and then writing an adapter to use it across both platforms, but that's not what you've tried to do here.
I would suggest you look at doing just nodejs and express for a time before taking on socket.io as well, otherwise you're trying to learn a whole heap of technologies all at once is quite a lot to try and take on board all at once.

MEAN: can I register more than a mini router app in app.use()?

In my node/express app.js main file, I have created a mini-app router:
var router = express.Router();
that I am passing inside to my controller functions and exporting again, at the end, I am registering the router in
app.use('/Link', router);
I now wanted to set up a second Controller folder with extra Controller function and routes only for my Angular NGX-Charts, where I prep up my data from mongoDB in correct format. Therefore, I wanted to create a second router object where I am passing and registering the right routes and middleware for that router object.
My question now is, can I create and register more than one router object for my express instance, like app.use('/Link',router1, router2, router3,...) ?
and does it behave the same like one router object then (I mean, will it find the appropriate router according to which routes I am navigating to in my browser and execute the correct middleware)?
Sure, you can do that. Common use-cases would be password-protection, generating auth tokens, parsing payloads, etc.
app.use accepts any number of "middlewares" after the first argument.
Check the docs for more details: https://expressjs.com/en/4x/api.html#app.use
The arguments are fairly flexible, there are a number of options for what you can pass.
A middleware function.
A series of middleware functions (separated by commas).
An array of middleware functions.
A combination of all of the above.
Each function gets 3 arguments, which are the Request, Response, and next callback function. Here's an example with an inline middleware that logs something and forwards to the next handler.
app.use('/secret-stuff', authorize, (req, res, next) => {
console.log('token from auth middleware', req.authToken)
next()
}, render)
One thing to note is that you can only send one response, so only the final handler would send the response to the user. Everything before that should call next() to activate the next middleware in the chain.
You could pass a number of routers as long as you make sure to forward (call next()) when the paths are unmatched. You would need to use some kind of path pattern that would allow for the middleware routers to handle greater specificity in the path (e.g. '/Link/*'), otherwise you wouldn't be able to define any sub-path handlers in the middleware routers.
In the past, I haven't had the need for sub-routers. Middleware works fine for modularization.

How to push a sequence of html pages after one request using NodeJS and ExpressJS

I am turning around in stackoverflow without finding an answer to my question. I have used expressJS fur several days in order to make an access webpage that returns first an interstitial and then a webpage depending on several informations I can get from the requester IP and so on.
My first idea for the interstitial was to use this piece of code:
var interstitial = function(req, res, next) {
res.render('interstitial');
next();
}
router.get('/', interstitial, nextPage);
setting a timeout on the next nextPage callback function of router.get().
However it looks that I could not do that. I had an error "Error: Can't set headers after they are sent.". I suppose this is due to the fact that res.render already give a response to the request and in the philosophy of express, the next function is passing the req, res args for another reply to another function that possibly could do it. Am I right?
In that case, is there a way to give several answer, with timeout to one request? (a res.render, and after that in the next callback a rest.send...).
Or is this mandatory to force client to ask a request to give back another response? (using js on the client side for instance, or timers on client side, or maybe discussing with client script using socket.io).
Thanks
Not sure I fully understand, but you should be placing all your deterministic logic within the function of the handler you're using for your endpoint.
Kinda like so:
router.get('/', function(req, res){
var origin = request.origin;
if (origin == '11.22.33.44'){
res.send('Interstitial Page.');
}else{
res.send('Home Page');
}
});
You would replace the simple text responses with your actual pages, but the general idea is that once that endpoint is handled you can't next() it to secondary handler.

Router in Node Express 4

This is a question on how router is handled underneath.
If I have router:
var router = express.Router();
router.get('/', function(req, res) {
res.end('router');
});
app.use('/', router);
If I've fetch anywhere other than http://localhost/, say http://localhost/whatever I'll get a Cannot GET whatever. Who is giving out this message? I think it's the router, is it correct?
Now if I add a middleware after the router.
app.use('/', function(req, res) {
console.log('-------> here');
});
Now if I go to http://localhost/whatever,then the browser never gets any response back and is just hanging there waiting for stuff.
So, it means the middleware architecture knows that if another middleware is added, then router does not have the final say. And it is expecting another route with possibly another router instance to be added. But if not, then router has the final say.
Isn't that kind of inconsistent? Somehow the router, which is itself a middleware, and the app object kind of know what each other is doing? Because router behave a little differently (call next() or give out "Cannot get" message) depending on whether another middleware is added.
I kind of dig into the code I can't really tell, but it looks like the entire middleware architecture is handled by the router object. Can someone explain a bit what's going on? Thanks.
You are missing one key step in your middleware. Middleware is a chain of asynchronous functions. Each one is called, in turn, with a reference to the next one. The expectation is that you pass control to the next middleware in the stack when you are done, which you are not doing.
app.use('/', function(req, res) {
console.log('-------> here');
});
Try this:
app.use('/', function(req, res, next) {
console.log('-------> here');
next();
});
What will happen then is control will get passed to the router just as it did without your middleware. The router cannot find any routes to handle /whatever, so you still get a 404 not found error.
I'm not sure that this is router who's giving "Cannot get" message.
Each middleware and the app itself try to handle the request in the order that they are being called.
If there is no router's or app's get (or post, etc.) able to handle the request, "Cannot get" message appears.
In the first scenario (before adding a middleware after the router), router can't handle the request, so Express looks forward
to find another app.use or app.METHOD, and not finding, it shows "Cannot get" message.
In the second scenario, another app.use after the router does exist, so it handles the request.
Thus, as for me everything is consistent.
Hope my answer helps you, but if your vision doesn't match mine, I'm ready for discussion :P
P.S. Sorry for my English

Resources