routing a request in node.js with express - express

I have generated a sample app using express generator and inside app.js there is
var routes = require('./routes/index');
var users = require('./routes/users');
There are two files generated index.js and users.js
To use the code inside the files there are this lines
app.use('/', routes);
app.use('/users', users);
My question,is app.use('/', routes) handles every request that is on a route starting with '/', then how will a request to a route to '/users' will be routed to users route

app.use() is middleware. You pass it an optional path and a function and it is the function's job to decide if it wants to pass the request on to further middleware or further routes. It does that by calling next() or if it doesn't want to pass it on, it doesn't call next().
So, if you have:
app.use("/", fn);
That middleware will get called for all paths, but the code inside the function you pass it decides whether to pass the request on or not.

Related

Express router middleware gets called on unexpected routes

Using express, I have multiple routes where I need some middleware. They aren't under any common /url/path.
Router-level middleware says:
Router-level middleware works in the same way as application-level middleware, except it is bound to an instance of express.Router()
As I read it, the implication is that the middleware applies only to the routes of the router instance. But that seems not to be the case.
In the following example, I expect "router called" to be logged only for http://localhost:10000/router.
But it gets logged for http://localhost:10000/noRouter also, which I don't understand. Why does middleware used by the router instance get called for routes that are added to app directly? Is it possible to create a Router so that only routes bound to that Router get the middleware applied?
const express = require('express')
const app = express()
const port = 10000
const router = express.Router();
router.use((req, _res, next) => {
console.log("router called")
next()
})
app.use(router)
router.get('/router', (req, res) => {
res.send("ok")
})
app.get('/noRouter', (req, res) => {
res.send("ok")
})
app.listen(port, () => {
console.log(`Listening at http://localhost:${port}`)
})
P.S.: I'm not stuck and have it working using a different approach. I just want to understand why this doesn't work...
A router is just inserted into a chain of request handlers and is searched/executed in order.
Because you do this:
app.use(router)
You are specifically sending ALL requests to your router. So, all requests will wind through the various handlers in that router looking for one that matches the incoming path. That will include your router.use() middleware which matches ALL paths so it will execute for every URL that gets sent to your router. There is no logic in a router that first checks to see if some route in the router matches before it executes any middleware. A router works just like the app object in this regard and executes middleware in the order it encounters them. So, the only way your middleware doesn't get executed is if the request never gets to the router at all.
If you want middleware to only apply to routes in a router, then you have a couple choices:
Put the router on a common path prefix such as:
app.use("/somerouterprefix", router);
Then, only URLs that get routed to your router will run the middleware in the router. This will, of course, only execute the middleware for routes that start with that prefix, but it will also execute the middleware for routes that start with the prefix, but don't even have a matching route handler in the router. Remember, I said earlier that everything that gets sent to the router will cause your middleware to execute.
Or, secondly, put the middleware on each individual handler in your router so that it will only get executed when it matches some route on your router such as:
const router = express.Router();
// give the middleware a function name so you can
// use it in specific route definitions
function myMiddleware((req, _res, next) => {
console.log("router called")
next()
});
// specify the middleware in your route definition
router.get('/router', myMiddleware, (req, res) => {
res.send("ok")
});

How to pass through multiple requests on a route EXPRESS JS

I'm trying to pass through a view as well as a json file so that I can manipulate it within the view.`
var express = require('express');
var router = express.Router();
var data = require('../monsters.json');
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index');
res.json(data);
});
module.exports = router;
`
It passes through the render of the index.ejs view but in the console there is no indication of a json file with it. Nor is there any way to manipulate the file and read it. I'm not sure if I'm just being silly and the method for passing it through doesn't exist.
EDIT: I've now tried this and am pretty sure its being passed through, is there anyway I can verify that the file has been passed through? `
router.get('/', function(req, res, next) {
res.render('index');
res.header("Content-Type",'application/json');
res.sendFile(path.join(__dirname, 'monsters.json'));
});
`
You can't send multiple responses. You get one response per request.
If the file you want to include is JSON, then you can embed it into a <script> tag in your HTML page using your template engine so everything would be in your HTML page and it can be the one response. Then, the JSON will get parsed automatically into a Javascript object that is available to your page's Javascript.
The other alternative is that you have the Javascript in the page make it's own Ajax call to your server to fetch the desired JSON in a separate http request. You would then make a route on your web server for handling that Ajax request and sending back the desired file.
As far as I am aware, you can't respond twice - you can however seemingly provide a callback to res.render(view, locals, callback) so perhaps you can serve it then. Otherwise, I would bake it into the view by generating the view on the backend with the file content as a parameter.
For more info, see;
http://expressjs.com/en/api.html#res.render

How to change express router path without changing URL?

I am statically serving my site from one directory. I have one dynamic route for setting the URL parameter roomCode. I want all routes to this path to serve the root index page without changing the URL on the client (that way I can still use the roomCode in my JavaScript).
Here is what I currently have:
// direct rooms to the index page
app.use('/room/:roomCode([A-Z]{4})', (_, res) => {
res.sendFile(path.join(__dirname, 'dist/index.html'))
})
// serve from the dist build
app.use(express.static(path.join(__dirname, 'dist')))
Instead of manually sending the dist/index.html file, I would like to simply change the route path to / for the following middleware and let the static server send the file. Something like this:
// direct rooms to the index page
app.use('/room/:roomCode([A-Z]{4})', (_, res, next) => {
req.path = '/'
next()
})
// serve from the dist build
app.use(express.static(path.join(__dirname, 'dist')))
This way, when the static middleware is reached, it believes the path was /, so it will serve the index page at the root.
Is this possible?
To reroute a request, you must change req.originalUrl to the new route, and then send it to the router handler with app._router.handle(req, res, next).
// direct rooms to the index page
app.use('/room/:roomCode([A-Z]{4})', (req, res, next) => {
// this reroutes the request without a redirect
// so that the clients URL doesn't change
req.originalUrl = '/'
app._router.handle(req, res, next)
})
// serve from the dist build
app.use(express.static(path.join(__dirname, 'dist')))
The documentation for req.originalUrl is a little confusing. It says:
This property is much like req.url; however, it retains the original request URL, allowing you to rewrite req.url freely for internal routing purposes.
This sounds like if you change req.url, it will change how it is routed. However, it does not. It does allow you to change it and then manually check it in latter middleware. But the middleware will still be called based on the original URL. Therefore, we need to overwrite the original URL and send it back through the router.

Using optionally authorized routes with passport and express

I would like to make some currently authenticated routes to be optionally authenticated. So that on my routes I could just simply add a public middleware to my router in a following manner:
.get('/projects/:id', public)
My current app.js flow looks like this:
// Authentication
app.use(authRoute);
app.use(defaultRouter);
// request handling
app.all('*', defaultRequestHandler);
So by default all my routes are authenticated, and in my authRoute I do a passport authentication, where I assign the user id to the request.
Using the previous flow, the only way I could think of to make it happen, was to assign an anonymous user id to the request in authRoute if the authentication fails. Also add another middle layer between authRoute and router, which checks from a list if the request url is a public route or not. Is there a simpler way? Thanks!
me im doing it like this:
you have structure like following as example:
-controllers
-helpers
-public
-routes
--index.js
--api
--- index.js
--- subscriber.js
-views
in app.js:
app.use('/', './routes/index');
in directory routes in index.js file
const express = require('express');
const router = express.Router();
router.use(middleware(),require('./api');
router.use(require('./auth');
module.exports = router;
in directory routes/api in index.js file
const express = require('express')
const router = express.Router();
router.use('/subscriber', require('./subscriber'));
module.exports = router;
So every Route which is in directory API is using the auth middleware.
When you add a new rout which needs to be controlled about authentication you can add a new rout in the index.js file in the directory './api/'
Every other route is defined in another folder.

What is the difference between get/post/... and use

All:
I am new to Express 4 router.
When I tried some login/signup example, I got one question about the .use and .get/.post function:
I saw sometimes it uses:
var express = require('express');
var router = express.Router();
router.get('/hello', function(req, res, next) {
res.send("Welcome");
});
and in main app, we use it like:
app.use("/", router);
While some other time, it uses:
var express = require('express');
var router = express.Router();
//here the router uses .use() function rather than .get/.post
router.use('/hello', function(req, res, next) {
res.send("Welcome");
});
and in main app, we use it like:
app.use("/", router);
So I am wondering what is the difference between them, does the .use() just a general name for all of get/post/put/... together?
I find this post: Difference between app.use and app.get in express.js
But still not feel easy to understand this....
Thanks
In addition to what Jonathan Lonowski said in the posted link, it might help to not compare use to get and post, but to compare it to all because both all and use work regardless of the HTTP verb used while that's obviously not true for get. Everything I'm about to say applies if you replace "all" with "get", it'll just narrow that handler down to a specific HTTP verb.
So, what's the difference between all and use?
app.all will handle incoming requests at the specified URL path regardless of the HTTP verb, just as app.use does. However, how it compares the requested URL to the handler is different. For example:
var r = express.Router();
r.use('/foo', function (...) { ... }); // Route A
r.all('/bar', function (...) { ... }); // Route B
If you make a request to /foo/123 Route A will be run.
If you make a request, however, to /bar/123 Route B will NOT be run.
This is because with HTTP verbs express compares the full path, but with 'use' it only cares about the beginning of the url. Because the URL /foo/123 begins with /foo Route A will run, but because /bar/123 does not match the FULL URL, Route B will not be. Note: You could make .all behave in the same way: r.all('/bar/*', ...), but use is easier and more appropriate for this.
So, what you would tend to mount with one vs the other is different. For example:
var app = express();
var router1 = express.Router();
var router2 = express.Router();
router2.all('*', function (req, res) { ... }); // Must specify a path!
router1.use('/secondary-routes', router2); // Can't do this with all.
app.use(router1); // Look Ma, no path!
Here I've used all to handle a request coming in, where I've used use to mount an entire router. Also, note that the usage of router.METHOD functions require a URL string as the first parameter, while use does not.
At the end of the day, if you:
Want all requests that come in under a given path (or even every request) to use the specified middleware, or
Want to mount an entire sub router/application, or
Want to include a plugin into your application
... Then use is probably what you want.
If you:
Are handling a specific request at a specific URL path (i.e. probably not doing a * match in the URL)
Generally won't be calling next and will instead actually be handling the request
... Then an HTTP verb method (like get, post or all) is probably what you want.
.use is used in 2 cases, middlewares and "modular mountable route handlers".
In your example
router.use('/hello', function(req, res, next) {
res.send("Welcome");
});
This means that any requests sent to /hello will be terminated with "Welcome" and the actual .get attached to /hello will not be called.
So, in short, call use when you need to apply some general middlewares or want to do modular architecture with routers. use can be "used" as request handlers, but you shouldn't because it is not designed for that purpose