what happens in app.use(express.static) and app.use(require("cors")()) and what middlewares are - node.js

I started with express a few days ago.
I dont really understand what happens in:
const express = require("express")
const app = express()
app.use(express.static(path.join(), "public"))
app.use(require("cors")())
app.listen(3000, () => console.log("running"))
the first example worked for me but i dont really understand it.
and basiclly i dont understand what happens in app.use() and what middlewares are..
someone can help me pls?
i read many blogs and I didnt got it :(

The Background
There are several parts to explaining this. First, off app.use() expects a middleware function to be passed to it. That would be a function with a signature like this:
app.use(function(req, res, next) {
console.log(req.path); // log incoming request path
next(); // continue routing to other handlers
});
It accepts other combinations of parameters, including an initial path and you can pass multiple middleware functions too and it will chain them together, but the basics of your question is about a single middleware function as shown above. That middleware function gets three arguments req - the incoming request object, res - the outgoing response objet and next - a function to call if you want to continue routing or report an error.
The job of one of these middleware function is to use the input in the req object to do some kind of processing of that input (depending upon what the purpose of the middleware function is) and then do one of three things:
Send a response using something like res.send() in which case the request has been handled and a response has been sent and no further routing will be done.
Continue routing to further request handlers in the chain by calling next().
Abort routing and go to the Express error handler by calling next(err).
The express.static() Middleware
So, that's what is expected of a function passed to app.use(). Now, let's look at the two examples you ask about. Let's start with express.static():
app.use(express.static(path.join(), "public"))
First, this isn't proper use of express.static(). I'm not sure exactly what you intended, but I will assume you meant something like this:
app.use(express.static(path.join(__dirname, "public")));
In this case, express.static() takes some configuration information which is the resulting path from calling path.join(__dirname, "public") and uses that to create a custom middleware function. So, calling express.static(path.join(__dirname, "public")) returns a function that expects to be called with the three middleware arguments we previously discussed. It is logically identical to this:
const publicPath = path.join(__dirname, "public");
const myStaticMiddleware = express.static(publicPath);
app.use(myStaticMiddleware);
which is logically equivalent to this:
const publicPath = path.join(__dirname, "public");
const myStaticMiddleware = express.static(publicPath);
app.use(function(req, res, next) {
myStaticMiddleware(req, res, next);
});
Where the code has been broken down into separate steps just so you can see each step separately.
And, in case you didn't already know, the point of the express.static() middleware is to serve static files from a designated directory if an incoming request matches a filename in that designated directory exactly and has an appropriate file type.
The cors Middleware
For your second example:
app.use(require("cors")())
Let's again break that down to the individual steps:
const cors = require("cors"); // load cors module
const corsMiddleware = cors(); // create cors middleware function
app.use(corsMiddleware); // register middleware with Express server
Which can be expanded to:
const cors = require("cors");
const corsMiddleware = cors();
app.use(function(req, res, next) {
corsMiddleware(req, res, next);
});
Just to show you that corsMiddleware is called with these three arguments.
The purpose of this particular middleware is to help configure a response to this request so that cross origin requests will be accepted.

Related

How to use single piece of middleware with more than one express router?

I am working on a university project and we have decided to go for MEAN technology stack. To be honest I am a beginner with NodeJS and express, more precisely this is the first time I do sth with it.
I've found that is preferable to use express.Router rather than putting all routes to express instance e.g. app.post('path', function(req, res) { ... })
So this is what I have
var express = require('express');
var app = express();
function authorizationMiddleware(req, res, next) {
...
}
// handles login, doesn't meed autorizationMiddleware
var authRouter = express.Router();
authRouter.route('/login')
.post(function (req, res) {
...
});
// handles fetching of a single, all person(s), fetching of transactions for a person
var personRouter = require('./routes/personRoutes')(Person, Transaction, autorizationMiddleware);
//handles adding of a new transaction e.g. POST /api/transactions where params such as sender, recipient and amount are passed in body
var transactionRouther = require('./routes/transactionRoutes')(Person, Transaction, autorizationMiddleware);
app.use('/api', authRouter);
app.use('/api/persons', personRouter);
app.use('/api/transactions', transactionRoutes);
app.listen(8080, function () {
console.log('Listening on port: ' + 8080);
});
As you can see I have three routers (not even sure if I have gonne too far with them), authRouter is handling login only and I have also decided to separate persons logic from transactions logic too. (maybe I could have handled creation of new transaction in a way like /api/persons/:personId/transactions but I rather liked the idea of sending all required params in body).
I would like to ask if you agree with the solution I tried. As you can see I am passing authrizationMiddleware function (handles verification of JWT token) function to router modules and using it there.
Is there maybe a better way to use the same middleware with of multiple routers or is this a legit way?
Thx in advance
Cheers!
I don't get why you use 3 Routers. The "common" way to go (or at least the way I go) is to put all the routes in the same place, except when the path is very different or the purpose is different (for example I separate the error routes from the others).
For example, let's say I need to build a rest api for an app, I would probably have paths like:
/users/:userid
/users/:userid/comments/:commentid
/locations
...
All these routes can go in the same Router and if you want, you can apply specific authentication/authorization middlewares to them:
router.get("/users/:userid",
doAuthentication, authorizeOnUserId,
userController.getUserById);
router.get("/locations",
doAuthentication, authorizeLocations,
locationController.getAllLocations);
The middlewares are called in sequence and the request is passed on to the next middleware only if there are no errors (unauthenticaed/ unhauthorized).
Then you can simply import your routes like this:
app.use('/api', router);
Using this technique allows you to have a fine grain control over your routes.
Hope this helps.

How Express recorgonizes middlewares?

I'm novice in Express and a little bit confused about how it handles middlewares? So basically I have two middlewares which looks like:
app.use(require('_/app/middlewares/errors/404'))
app.use(require('_/app/middlewares/errors/500'))
404
var log = require('_/log')
module.exports = function (req, res, next) {
log.warn('page not found', req.url)
res.status(404).render('errors/404')
}
500
var log = require('_/log')
module.exports = function (er, req, res, next) {
log.error(er.message)
res.locals.error = er
res.status(500).render('errors/500')
}
So now I want to add my custom middleware app.use(require('_/app/middleware/shareLocals')) which looks like:
module.exports = function (req, res, next) {
res.locals.base_url = req.protocol + '://' + req.get('host');
next();
}
The main problem is that now when I try to use base_url I get 404 error...
So how Express understands what middleware do? That is between my middleware and 404 are no visual differences:
it receives same params
it doesn’t have any if's in it, just throws 404 error
Appears the feeling the middlewares in Express are made for errors (when excepts err as first param) and for 404 (when there is no first err)...
P.S.
Is there any difference defining middlewares before or after routes?
P.S. Is there any difference defining middlewares before or after routes?
Yes.
The order in which you register your middlewares (and routes) have a lot to say.
Image express as a giant list. Starting at the first element in the list, you have the first middleware OR route you have defined, next is the second, etc.
When express gets a request, it appears to be matching your route/name of route/middleware, and if it's a hit, it executes the middleware/route and potentially waits for a "next()" call.
So if you have a route "/test" it will only be executed if you have a request matching "/test". routes with different names obviously wont get triggered. middlewares can also have names: app.use("/test", middlewareA). This will also only trigger if "/test" is requested. The way you do it, all requests (within the routes namespace) will be triggered app.use(middlewareA). It's like a wildcard.
Now, to the implications of things being ordered:
Your 404 middleware should only be used AFTER all routes have been defined. that way, when the list reached the 404 middleware, no routes have actually been found.
returning/sending result/not calling next() at the end of a middleware will all potentially create problems in your flow. I wont go into details about this, but be aware of it.
I am guessing your own middleware is added after the 404 middleware. That is probably the problem. If not, you should surrender more of your code so we can take a better look. But remember, order is everything :)

Express 4 router.param doesnt fire

I have a node express 4 app and I want to mount a route (contacts) on a parent route. i.e
/:fundid/contacts
In my fund route I declare
var router = require('express').Router({ mergeParams: true });
var contactRoutes = require('./contacts');
router.use('/:fundid/contacts', contactRoutes);
In my contact route
var router = require('express').Router({ mergeParams: true });
router.param('fundid', function(res, req, next, id){});
The problem is that this param call does not fire. From what I can garner from the documentation these param calls are relative to the router they are declared on, but i would have thought mergeParams:true would affect this, but it doesn't. The route is otherwise working, and both routes are called. Am I missing something?
The reason I want to do this is because I want to mount the contacts route on multiple parent routes, and build a filter based on those parent parameters
This comment suggests that parameters are tied to the router they are declared with; so in your case, fundid can only be handled by the "fund" router. mergeParams serves a different purpose, namely to provide access to req.params.fundid from child routers.
You can always use a request middleware in your contact router to perform special operations based on the fundid, though:
router.use(function(req, res, next) {
var id = req.params.fundid;
...
});

404 when accessing new route

I'm trying to add a new route (/profile) to my NodeJS Express web application. I've modified my app.js file like this:
var routes = require('./routes/index');
var profile = require('./routes/profile');
app.use('/', routes);
app.use('/profile', profile);
The '/' index path works fine, my issue is with '/profile'. Whenever I try to access it, I get a 404. This is profile.js:
var express = require('express');
var router = express.Router();
router.get('/profile', function(req, res) {
var username = req.session.username;
if(username) {
res.render('profile');
} else {
res.redirect('/login');
}
});
module.exports = router;
I don't understand what I'm doing wrong because in the example express application that is generated, '/users' works fine. I basically copied that format, but it's throwing a 404. Any ideas?
In my profile.js, I had to change my GET request path to this:
router.get('/', function(req, res) {
//code
});
Otherwise, the router would be looking for /profile/profile. When I change it to /, it's just looking for the root of `/profile', or at least that's how I understand it.
To understand what you are doing wrong you should know that Node.js uses middleware functions to route your requests. To simplify you can think about it as a chain of functions.
Middleware is like a plumbing pipe, requests start at the first middleware you define and work their way “down” the middleware stack processing for each path they match.
So with the following statement you added a middleware function to handle any request starting with the root path /profile, and it is a common pattern in Node to use the use method to define the root paths.
app.use('/profile', profile);
The use method is doing part of the routing in your scenario and the statement above will match any route starting with that path, including /profile/all or /profile/12 or even /profile/go/deeper/inside.
However, you want to narrow down that routing to something more specific, so that is why you pass a router middleware function (profile in your case) to match more specific routes instead of all routes starting with /profile.
The profile middleware function is actually the next step in the chain of functions to execute, and it will start from the root path specified in the use statement, which is the reason why you need to start again with / and not with /profile. If you wanted to match a profile by ID you would do:
router.get('/:id', ...)
Which would be concatenated with the base URL (from the /use statement) and would match a request like /profile/2 or /profile/abc.

Routes are overwriting middleware

My static file middleware calls are being overwritten by my routes, even though I'm running it in the correct order:
First I set my static files, like this:
app.use('/public/', express.static('/home/skerit/temp'));
app.use('/public/', express.static('/home/skerit/app/public'));
Then I add my route:
app.get('/:controller/:action', callback);
And now, when I try to get /public/empty.txt the route is executed.
When I remove the route, it works just fine.
And I'm sure they happen in the correct order. (First app.use, then app.get)
So what could be wrong here?
Edit: Since I said I'm executing them in the correct order, here's some proof.
I added simple log statements to the execution of the code:
[debug] - [stages.js:186] Static middleware has been set!
[debug] - [stages.js:191] Startin routes!
[debug] - [routes.js:7] Setting controller routes
[info] - [database.js:79] Database connection to default has been made
[info] - [database.js:93] Stored procedures on MongoDB server default
[info] - [database.js:120] Created connection to all datasources
[debug] - [stages.js:202] Setting /:controller/:action
As you can see: 'Static middleware has been set' comes up first. Only then does it start setting the other routes.
app.get is not what decides if a route is run before or after a middleware, it is app.use(app.router) that does that.
you need app.use(app.router) to be after app.use(express.static);
here are two examples:
var http = require('http');
var express = require('express');
app = express();
app.use(app.router);
app.use('/public/', express.static('/home/skerit/temp'));
app.get('/:controller/:action', function (req, res) {
res.send(req.params.controller);
});
var server = http.createServer(app);
server.listen(process.env.port || 3000);
this snippet doesn't work, when you run localhost:3000/public/somefile - you'll get "public" returned.
however if you use the following snippet:
var http = require('http');
var express = require('express');
app = express();
app.use('/public/', express.static('/home/skerit/temp'));
app.get('/:controller/:action', function (req, res) {
res.send(req.params.controller);
});
app.use(app.router);
var server = http.createServer(app);
server.listen(process.env.port || 3000);
localhost:3000/public/somefile should return the file's content. Note the location of app.use(app.router). (if you don't add it, by default it should be last).
Note also that if /home/skerit/temp/somefile doesn't exist, then the control will pass to the next middleware and it will look like express.static didn't work, so make sure your paths are correct.
EDIT:
Following a comment I've been informed that my original answer was incorrect, that is, that app.get does decide at what point in the middleware stack app.router is put.Namely at the first time either app.router is used or app.get is used.
In this case I would suspect that your problem is most likely due to the fact that you are using a url to a file that doesn't exist.

Resources