node-restify: Routes and middleware not run in defined order

Bug Report

Restify Version

v7.2.1

Node.js Version

v10.5.0

Expected behaviour

In restify v4, using the serveStatic plugin before including any middleware would not run the middleware when serving a file.

Actual behaviour

Middleware is now run prior to serving files.

Repro case

const restify = require('restify');
const errors  = require('restify-errors');

const server = restify.createServer();

server.get('/', restify.plugins.serveStatic({
	directory : './public/',
	default   : 'index.html'
}));

server.use((request, response, next) => {
	return next(new errors.ForbiddenError('Nope'));
});

server.get('/api/test', (request, response, next) => {
	response.send({});
	return next();
});

server.listen(8000);

Expected: Request to / serves index.html, request to /api/test gives a ForbiddenError Actual: Request to both / and /api/test gives a ForbiddenError

Cause

Changes somewhere between v4 and v7, presumably the change in routing/middleware handling.

Are you willing and able to fix this?

No, there appears to be other issues with serveStatic in general so this should be tackled as part of sorting the overall problem out.

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Comments: 17 (8 by maintainers)

Most upvoted comments

@alechirsch, I had the same issue, and I ended up using restify-router, which allowed me to:

  1. register unauthenticated routes
  2. register a new router (to encapsulate my authenticated routes)
  3. inside the router registration, register the authentication middleware (which applies it to all the routes registered in the router)
  4. also inside the router registration, register authenticated routes
  5. profit

This changed removed one of the core features that our application needs. We have the same use case as most on this thread.

  1. Register all unauthenticated route
  2. Register authentication middleware
  3. Register all authenticated routes

Can a flag on server creation be implemented to preserve the ordering that handlers are registered? We can not upgrade to the latest version as it will break our API.

While the documentation for use() is correct in 7.x, this breaking behavior really needs to be added to the migration guide.

Hi all - this was a deliberate change, one unfortunately poorly documented. We definitely could have done a better job on this front 😦 The intention was to get away from unintentional bugs as a result of shifting use/pre/{verb} statements around in the code, and moving towards explicit ordering specified by the API. For example:

const restify = require('restify');
const server = restify.createServer();

server.use(function(req, res, next) {
    console.warn('use1');
    return next();
});

server.pre(function(req, res, next) {
    console.warn('pre1');
    return next();
});

server.get('/', [
    function myGet(req, res, next) {
        console.warn('get1');
        return next();
    },
    function myGet(req, res, next) {
        console.warn('get2');
        res.end();
        return next();
    }
]);

server.use([
    function(req, res, next) {
        console.warn('use2');
        return next();
    },
    function(req, res, next) {
        console.warn('use3');
        return next();
    },
]);

server.pre([
    function(req, res, next) {
        console.warn('pre2');
        return next();
    },
    function(req, res, next) {
        console.warn('pre3');
        return next();
    },
]);

server.listen(3000);

Would result in:

pre1
pre2
pre3
use1
use2
use3
get1
get2

Moving the statements around would not change the output. Since pre, use, and {verb} all take arrays, existing behavior is possible by leveraging factories of some sort to “bind” common middlewares for use cases like auth, e.g.:

server.get(prependWithCommon(myHandler()));
server.get(prependWithAuth(myHandler()));

We’ve also seen teams build out a JSON config mapping for their URLs to handlers, and use that to programmatically install routes and bind auth only when the route specifies it.

Ultimately, the behavior is more consistent, although at the cost of verbosity. We’ll definitely need to update the docs to reflect this change more explicitly. We understand this might not be to everyone’s liking; we should definitely continue the conversation if there are use cases we can better support.