I have a question. Following this code:
const express = require('express');
const usersRouter = require('./routes/users');
const carsRouter = require('./routes/cars');
const app = express();
app.use(express.json());
app.use('/users', usersRouter);
app.use('/cars', carsRouter);
//and other app use routes
module.exports = app;
the question is about app.use routes, imagine having 50, 100, 1000 of routes, it isnt a best practise to update app.js everytime! how to refactor it the right way?
There are two possible cases:
there are actually 50, 100, 1000 different functions (or routers) that corresponds that many routes. In this case, you need to organize them in some neat way. I suggest scanning folders and calling app.use them in a cycle.
there aren't so many different functions, but instead there are copy/paste code or maybe some few routes correspond to one function. This means you can either organize that functions in an array and then, make app.use inside of a loop. Or, maybe you can create generic muddleware that analyzes path and calls proper function to deal with the request:
app.use(function(req, res, next) {
if (req.path.match(/.../)) {
return ...
}
if (req.path.match(/.../)) {
return ...
}
...
// if nothing matches, run the next middleware
next();
});
Related
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")
});
var cookieParser = require('cookie-parser')
var cookieValidator = require('./cookieValidator')
var app = express()
async function validateCookies (req, res, next) {
await cookieValidator(req.cookies)
next()
}
app.use(cookieParser())
app.use(validateCookies)
// error handler
app.use(function (err, req, res, next) {
res.status(400).send(err.message)
})
app.listen(3000)
In the above code both cookieParser() and validateCookies are middlewares but the way of executing them are different. There is a function like () with cookieParser but not with validateCookies. Can someone please explain why? Sorry If I am sounding foolish.
The app.use() function takes a single parameter, which is a function reference for a function that serves as a middleware for your router in Express.
You’ve correctly included the reference to your validateCookies function without parenthesis, as you just want Express to know which function you’d like it to use as middleware, and not to execute it at the time (Express will invoke the function for you when it’s time).
cookie-parser is a bit of an outlier (and I can see from where your confusion stems). The cookieParser() function actually returns a function reference upon successful execution. This design is likely because this particular module allows developers to pass in certain values to change the resulting function’s behavior as a middleware.
I am trying to implement some middleware in Express that should be called for all routes. This middleware should alter the request object.
I've tried several things already but seem to keep having the same issue. Soon as the middleware is left it looks like the request object is changed back to it's original state.
Currently my code resembles (I simplified it with a minimalistic example):
route.js:
const express = require('express');
const router = express.Router();
router.get('/getMe', (req, res) => {
// return the desired data.
// I expect req.params.myString to exist here but it does not.
});
module.exports = router;
index.js:
const express = require('express');
const router = express.Router();
router.use('/', require('./route'));
module.exports = router;
app.js:
const express = require('express');
const app = express();
const routes = require('./index');
app.use((req, res, next) => {
// Adding req.params.myString to the request object.
if (req.params.myString === undefined) req.params.myString = 'hello world';
next();
});
app.use('/api', routes);
As you can see I left out some of the code to keep it more readable. This is the code that gets the response and sets up the server.
Again, I am expecting that req.params.myString becomes available in the endpoint. Does anyone see what I am doing wrong?
In express docs ( http://expressjs.com/en/api.html#req.params ) it says:
If you need to make changes to a key in req.params, use the app.param
handler. Changes are applicable only to parameters already defined in
the route path.
So you need to check app.param handler.
http://expressjs.com/en/4x/api.html#app.param
You should app.set("myString", "hello World") inside your app.js and then you can access the field in your route.js/index.js scripts by using req.app.get("myString"). Or this should work too, set it like app.myString = "Hello world" and access it like req.app.myString.
Here are my routes:
app.get('/signUp', routes.signUp);
app.post('/signUp' , routes.signUp);
Here is my separate file for routes.
exports.signUp = function(req, res) {
res.render('signUp');
};
The second block of code is behaviour I want in response to a get request.
How do I respond to a post request? I have already tied up the signUp function with behaviour that responds to get. Do I bundle up the post behaviour in the same function and render the sign up page again? Suppose I simply want to render the view, I don't want the post behaviour to execute in that case so it would be strange to bundle those together.
I believe the express router module should resolve this for you.
route file -
var express = require('express');
var router = express.Router();
router.route("/")
.get(function (req, res) {
res.render('signUp');
})
.post(function (req, res) {
//do something else
})
module.exports = router
index.js/app.js/server.js/whatever you call it.
//..
signUp = require("./routes/signup.js"); //or wherever this is
//...
app.use("/signUp", signUp);
//..
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