I am creating an express middleware. My index.js exports is a function that returns a function(req,res,next) {...}
function myMiddleware (options) {
//some stuff with options...
return function middleware(req, res, next) {
//stuff
}
}
module.exports = myMiddleware;
So the user would could start using my middleware like this:
var express = require('express'),
myMiddleware = require('my-middleware'),
app = express();
app.use(myMiddleware());
The important thing is that I must make sure that middleware has access to cookies. Of course I could parse req.header.cookie myself but there exists a cookie-parser module that does the parsing for me.
So I would like to make sure that cookie-parser middleware is already being used when my middleware starts up. I could probably instruct the user to first use cookieParser() but I dislike it.
Is there a way to do it easily?
EDIT
I could even port cookie-parser since it is not a huge module. But that is probably the least thing I would do since it can introduce a lot of maintenance work that I would normally not do.
You have two options:
Force the cookie-parser middleware if req.cookies is undefined.
If cookie-parser has already been called, this call will be ignored. See: https://github.com/expressjs/cookie-parser/blob/master/index.js
The downfall to this approach is that the cookieParser middleware will only have the default options.
var cookieParser = require('cookie-parser');
function myMiddleware(options) {
return function(req, res, next) {
return cookieParser()(req, res, function() {
// Your middleware code
return next();
});
}
}
Give a warning if req.cookies is undefined
function myMiddleware(options) {
return function(req, res, next) {
if(!req.cookies) {
// Some warning
}
// Your middleware code
return next();
}
}
In express, the execution order of middleware is defined by the order it has been attached to the app/router.
req.param being an exception, as it is executed before any route handler that matches the parameter.
The order in which your routes and middleware functions are declared is the order which they will be executed. I do not know of a way to change this, maybe fiddling with the internals of express router, but there is no documented/clean way to achive this by default.
To make sure your middleware has access to the cookie parsed by cookie-parser, attach the cookie parsing middleware before any route definitions.
app.use(cookieParser());
// all other middleware that uses cookies
Related
I've been playing around with setting up a basic atlassian-connect-express (ACE) application, and have modified the starter code provided by the ACE package to be suitable for serverless deployment. One of the problems I faced after doing this was that routing is now divided into stages, e.g. /dev, /prod. I did a bit of research and found that a way to deal with this would be to use an express Router and mount it to the appropriate endpoint for the stage being deployed to. The problem I then faced is that the authentication middleware provided by ACE seems to be application level and then can't be used by each router.
Typically the routes were added to the express app like this:
import ace from 'atlassian-connect-express';
import express from 'express';
import routes from './routes';
const app = express();
const addon = ace(app);
app.use(addon.middleware());
routes(app, addon);
and in ./routes/index.js
export default function routes(app, addon) {
// Redirect root path to /atlassian-connect.json,
// which will be served by atlassian-connect-express.
app.get('/', (req, res) => {
res.redirect('/atlassian-connect.json');
});
// This is an example route used by "generalPages" module (see atlassian-connect.json).
// Verify that the incoming request is authenticated with Atlassian Connect.
app.get('/hello-world', addon.authenticate(), (req, res) => {
// Rendering a template is easy; the render method takes two params:
// name of template and a json object to pass the context in.
res.render('hello-world', {
title: 'Atlassian Connect'
});
});
// Add additional route handlers here...
}
I've changed ./routes/index.js to work as a router object and export that, however this leaves me unable to use the addon.authenticate() middleware
import ace from 'atlassian-connect-express';
import express from 'express';
import routes from './routes';
const app = express();
const addon = ace(app);
app.use('/dev', require('./routes'));
and in ./routes/index.js
const express = require('express');
const router = express.Router();
// Redirect root path to /atlassian-connect.json,
// which will be served by atlassian-connect-express.
router.get('/', (req, res) => {
res.redirect('/atlassian-connect.json');
});
// This is an example route used by "generalPages" module (see atlassian-connect.json).
// Verify that the incoming request is authenticated with Atlassian Connect.
router.get('/hello-world', addon.authenticate(), (req, res) => {
// Rendering a template is easy; the render method takes two params:
// name of template and a json object to pass the context in.
res.render('hello-world', {
title: 'Atlassian Connect'
});
});
module.exports = router;
Obviously having no knowledge of addon, the router cannot use that authentication middleware.
Is it possible to pass that middleware through to the router when attaching it to the application? If not, is there another way I can handle URL prefixes without using a router?
According to the documentation, any nodejs express middleware function can be replaced by App or Router instances:
Since router and app implement the middleware interface, you can use
them as you would any other middleware function.
This is some generic error handling I use:
express()
.use('/test', new TestRouter())
.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send(err.message);
})
.listen(PORT);
I tried to replace my error handling with an error-handling Router, but now the callback is never executed and express uses it's default error handling.
const handler = new express.Router();
handler.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send(err.message);
});
express()
.use('/test', new TestRouter())
.use(handler)
.listen(PORT);
Why is this not working as expected and how could I solve it?
Error handlers need to be configured as the last calls to use, as per the docs
You define error-handling middleware last, after other app.use() and routes calls;
I would say "routes" also buckets Routers therefore what you are trying to do doesn't look like it's supported.
Just digging further into this, I believe the issue is down to the fact Routers have separate Layer stacks. Express behind the scenes is effectively just a Router (we see here where this is setup, and then further down where the middleware is delegated on to the Router). Internally, middleware functions are represented as "Layers" (as seen here) and looking at the constructor, we can see Routers have their own stack.
So consider the following example:
express()
.use(new RouterA())
.use(new RouterB())
.use((err, req, res, next) => {
...
})
.listen(PORT);
Which, by looking at the source, can be viewed as:
express()
.use((req, res, next) => {
router.handle(req, res, next);
})
.use((req, res, next) => {
router.handle(req, res, next);
})
.use((err, req, res, next) => {
...
});
Therefore, if an error is thrown in RouterA, the Router would firstly check its own middleware stack for a matching error Layer (i.e. a (err, req, res, next) function) and execute that, it would then bubble up to the app level and perform the same action.
So given your example, if you consider the translated code, this explains why it won't catch your error handler in the second Router - a Router signature does not match that of an error handler therefore it would be skipped.
I have multiple app.use in my index/starting point of my app.
i.e
app.use(
if (!req.contextToken && req.contextTokenchecked) {
req.queryToFirebase = false;
req.contextTokenchecked = true;
req.contextToken = {}
}
next()
)
app.use(//Do something 2)
and so on..
Now, This is sort of makes my code untidy (in index.js) so I thought about creating a separate js file (say intialize.js) which will contain all my app.use
Till now, I am used to only creating separate routes
const express = require('express')
const router = express.Router()
and then import it in my index.js
app.use('/auth', auth)
But this time I don't want my routes in separate file rather all
app.use()
In one common.js file
Second, I also have a route which loads data from gmail (gmail.js).
app.use('/gmail', gmail)
currently, In all the routes, I am adding a middleware isLoggedInmanually. Is it possible to do something so that all the routes inside it my gmail.js inherits that middleware
The middlewares you register are always executed in the order they are registered. So if you have a code like this:
app.use((req, res, next) => {
// middleware A
next()
})
app.use((req, res, next) => {
// middleware B
next()
})
app.use(middlewareC)
app.use('/gmail', gmail)
Then you can for sure create one common file for those middlewares before the app.use('/gmail', gmail):
common.js
let router = express.Router()
router.use((req, res, next) => {
// middleware A
next()
})
router.use((req, res, next) => {
// middleware B
next()
})
router.use(middlewareC)
module.exports = router
main.js
app.use(require('./common.js'))
app.use('/gmail', gmail)
The API for use (or any others of those registering methods) is ([path,] callback [, callback...])
So you can register as many middlewares as callback as you want, so you can add a isLoggedIn in front of the gmail router:
app.use('/gmail', isLoggedIn, gmail)
If router.all() just match all methods,could it be instead by router.use()?
and what router.use() diff between router.route()?
router.all: What this means is, it doesn't matter the method of the request.. (post, get, put), if the url matches, execute the function.
ex- router.all("/abc",fn) will be work for all request to /abc
router.use() : router.use() helps you write modular routes and modules.. You basically define a middle ware for routes.
router.use("/pqr", pqrRoutes)
now for all requests that start with /pqr like /pqr/new or /pqr/xyz can be handles inside the pqrRoutes.
router.route(): this is nice way to define the different Method implementations for a single url end point.
lets just say you have two api end points. router.get("/jkl") and router.post("/jkl"), with router.route() you cam sort of combine these different api handlers..
you can say router.route("/jkl").get(fn1).post(fn2)
router.all() matches every http protocol, router.use() is for middleware, and router.route() returns an instance of a single route which you can then use to handle HTTP verbs with optional middleware.
You should check out the documentation for more informations
app.all(), which is not derived from any HTTP method. This method is used for loading middleware functions at a path for all request methods.
app.all('/secret', function (req, res, next) {
console.log('Accessing the secret section ...')
next() // pass control to the next handler
})
Use the express.Router class to create modular, mountable route handlers. A Router instance is a complete middleware and routing system; for this reason, it is often referred to as a “mini-app”.
The following example creates a router as a module, loads a middleware function in it, defines some routes, and mounts the router module on a path in the main app.
var express = require('express')
var router = express.Router()
// middleware that is specific to this router
router.use(function timeLog (req, res, next) {
console.log('Time: ', Date.now())
next()
})
Bascially we use .use when we use a middleware
express.Router
Use the express.Router class to create modular, mountable route handlers. A Router instance is a complete middleware and routing system; for this reason, it is often referred to as a “mini-app”.
The following example creates a router as a module, loads a middleware function in it, defines some routes, and mounts the router module on a path in the main app.
var express = require('express')
var router = express.Router()
// middleware that is specific to this router
router.use(function timeLog (req, res, next) {
console.log('Time: ', Date.now())
next()
})
// define the home page route
router.get('/', function (req, res) {
res.send('Birds home page')
})
There can we more than 1 router this instance of router has name router defined below the express defination.
Here is the documentation for router
https://expressjs.com/en/guide/routing.html
Router.use()
Routers behave like middleware and can be .use()d by the app in other routers. In other words, routers allow you to chunk your big app into numerous mini-apps that you can later put together. For small apps, this might be overkill, but as soon as you think, “This app.js file is getting big,” it’s time to think about breaking down your app with routers.
router.route(path)
The router.route(path) method is used to chain HTTP verb methods. For example, in a create, read, update, and
delete (CRUD) server that has POST, GET, PUT, and DELETE endpoints for the /posts/:id URL (e.g., /posts/53fb401
dc96c1caa7b78bbdb), we can use the Router class as follows:
var express = require('express');
var router = express.Router();
router.param('postId', function(request, response, next) {
// Find post by ID
// Save post to request
request.post = {
name: 'Node.js',
url: 'http://your-url/node-js-blog'
};
return next();
});
The Router.route(path) method provides the convenience of chaining methods, which is a more appealing way
to structure, your code than re-typing router for each route.
Alternatively, we can use router.VERB(path, [callback...], callback) to define the routes just as we
would use app.VERB(). Similarly, the router.use() and router.param() methods work the same as app.use() and
app.param().
Am I shooting myself in the foot:
I want to make config, core, and mean available on the app and req objects in my Express app.
I'm using properties not in the 4.x API. Anything I should know?
Is there a problem with just adding them as properties?
// express.js
module.exports = function(db, config, meanModules) {
var app = express();
// ...
// Get mean-core
var core = require('meanjs-core')(db, config);
// Attach config, core, and modules to app <==== POSSIBLE FOOT SHOOTING
app.config = config;
app.core = core;
app.mean = meanModules;
// Middleware to adjust req
app.use(function(req, res, next) {
// Add config, core, and modules to all requests <==== POSSIBLE FOOT SHOOTING
req.config = config;
req.core = core;
req.mean = meanModules;
next();
});
// ...
}
I would recommend attaching a single property to app that's probably never going to conflict, and accessing everything from there, like app.myLibrary.
app.myLibrary = {config: config, core: core, mean: meanModules};
And access app.myLibrary from within routes/middleware:
req.app.myLibrary
Unless something dynamic is happening in the middleware that varies per request, it's likely better to just access it with req.app.myLibrary.