Home > front end >  In Nodejs, how to have individual "allowed methods" for each express enpoint?
In Nodejs, how to have individual "allowed methods" for each express enpoint?

Time:10-11

I am building a rest API with nodejs and express.

I am trying to implement a small CORS system to the endpoints:

cors.js

export const cors = ({ allowedMethods }) => {
  return (req, res, next) => {
    res.header('Access-Control-Allow-Origin', '*');
    if(!allowedMethods.includes(req.method)){
      return res.sendStatus(405);
    }
    next();
  }
}

server.js

const app = express()

app.use(express.json())

app.post('/', cors({allowedMethods: ['POST']}), (req, res) => {

})

app.put('/photo', cors({allowedMethods: ['PUT']}), (req, res) => {

})

Let's say I have these 2 endpoints, every time I go to a URL with an unallowed method, I get a 404 response but I want "405 Not Allowed". (makes sense, because of app.<method>())

How is it possible to look up the allowedMethods for each endpoint and decide what to do next?

I've tried using app.use(cors()), but that catches all endpoints and I'll never know about the allowedMethod specified for a specific endpoint. And I don't think that node cors package would do it.

CodePudding user response:

You're conflating two different things here.

CORS has a mechanism for stating what HTTP methods are allowed to be used to make a cross origin request from JavaScript which is used in combination with a preflight OPTIONS request.

HTTP has a mechanism to say that a request is using an HTTP method that is not allowed at all.


Since you are attempting to generate 405 Not Allowed responses, you are dealing with the latter. This has nothing to do with CORS and should be kept separate from CORS middleware.

all will match all requests for a path, no matter what HTTP method is used, so you can write:

const method_not_allowed = (req, res) => {
    return res.sendStatus(405);
};

app.post('/', cors(), (req, res) => { });
app.all('/', method_not_allowed);

Any POST request will get caught by app.post('/' and any other request to / will carry on and get matched by app.all('/').


That said, it looks like you are reinventing the wheel and could just use this existing module.


If you need to also deal with Access-Control-Allow-Methods then you need to take care of that separately.

That does need to be handled with the rest of your CORS logic, and you need to handle both the methods you want requests to be made with (post in this example) and OPTIONS (for the preflight).

CodePudding user response:

If you don't mind a bit of configuration:

const routes = {
    "/": {
        get: (request, response) => { ... },
        post: (request, response) => { ... },
    },
    "/photo": {
        put: (request, response) => { ... },
    },
};

app.use((request, response, next) => {
    const route = routes[request.url] || {};
    if (!Object.keys(route).includes(request.method.toLowerCase())) {
        return response.sendStatus(405);
    }
    next();
});

for (let route in routes) {
    for (let method in routes[route]) {
        app[method](route, routes[route][method]);
    }
}

Even though you'll get in trouble soon with request params (/photos/:photo_id).

EDIT: didn't know about app.all, much cleaner!

  • Related