Nodejs Express - return 405 for un-supported method

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP



Nodejs Express - return 405 for un-supported method



I'm running a standard NodeJs 8 with Express and currently when a request for an existing path but un-supported method comes in, Express return 404.



For example 'POST /login' is supported, but 'GET /login' is not, but it returns 404.



How can I make Express return 405 in such a case?



Here's the routes file:


const express = require('express');
const router = express.Router();
const loginController = require('../controllers/login');


router.route('/login').post(loginController.loginUser);

module.exports = router;



Please advise.




2 Answers
2



You can simply add the .all() handler to your route chain, like so:


.all()


const methodNotAllowed = (req, res, next) => res.status(405).send();

router
.route(`/login`)
.post(loginController.loginUser)
.all(methodNotAllowed);



Explanation



This works because requests are passed to the handlers in the order they are attached to the route (the request "waterfall"). The .post() handler will catch your POST requests, and the rest will fall through to the .all() handler.


.post()


.all()



Also see this question for more details.



Authenticating all POST routes



If you would like to ensure that the user is logged in for all POST requests, but return a 405 response for any other requests, you can use a regular expression to match all routes with router.post('*'), like so:


router.post('*')


router
.post(`*`, loginController.loginUser)
.all(methodNotAllowed);



The problem with this approach, however, is that no 404 errors will ever be returned to the client, only 405. Therefore I recommend attaching the methodNotAllowed handler to each individual route, like in the first code snippet above. This approach will return 404 errors for routes that don't exist, but 405 errors for routes that do.


methodNotAllowed



Determining the available methods for a route



To determine which methods are allowed for a route, use router.stack:


router.stack


app.use((req, res, next) =>

const methods = router.stack

// Filter for the route that matches the currently matched route
.filter(layer => layer.route.path === req.path)[0]
.route
.methods;

if (!methods[req.method]) methodNotAllowed(req, res, next);
else next();

);





Thank you for the response, one more thing, is there an easy way to return which methods are allowed for this route? or just build such a response manually?
– David Faiz
Aug 8 at 8:54





I've updated my answer with information on how to determine the allowed methods for a route.
– dwhieb
Aug 8 at 9:27





Thanks again, but it seems to work for regular paths such as /login, but fails for paths which have a parameter, for example /getPermissions/:user_id. It returns an error that 'Cannot read property path of undefined', for row '.filter(layer => layer.route.path === req.swagger.apiPath)[0]'
– David Faiz
Aug 8 at 11:00




You can try this that way:


app.route("/login")
.get((req, res) =>
/* HANDLE GET */
)
.post((req, res) =>
/* HANDLE POST */
)
.all((req, res) =>
res.status(405).send();
);



How it works?



If request matches the route. It will go through the handlers. If a handler is present, it will be handled using that specific one. Otherwise, it will reach the 'all' handler that will set the status code to 405 and send the response.



Here You can find the discussion about it:
405 issue



@You question below:



You can try that way:



loginRoutes.js content:


const router = require('express').Router();

router.route('/')
.get((req, res) =>
res.status(200).send()
)

module.exports = router



server file content:


const express = require('express')
const app = express();
const router = express.Router();
const loginRoutes = require('./loginRoutes')

const PORT = process.env.PORT || 8080;

router.use('/login', loginRoutes)
router.route('/login').all((req, res) => res.status(405).send() )
app.use(router);

app.listen(PORT, () => console.log(`started on port: $PORT`))





Can I do something like this: app.use(loginRoutes).all((req, res) => res.status(405).send(); ); ?
– David Faiz
Aug 8 at 8:08





See edited message. You can check out nice project structure of routes/controllers here: github.com/linnovate/mean/tree/master/server
– KarlR
Aug 8 at 8:26






Is that clear for You?
– KarlR
Aug 8 at 8:51





Yes, much more, thank you. One more thing, is there an easy way to return which methods are allowed for this route? or just build such a response manually?
– David Faiz
Aug 8 at 8:54





First idea is to have a documentation hosted with Your API (for example swagger). You can also try to implement OPTIONS method on Your server. You can read more about it here: developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS and here is the example how to handle that in express.js: johnzhang.io/options-request-in-express. For sure there are more ideas how to tackle that problem (maybe some API discover), You need to check it.
– KarlR
Aug 8 at 15:46







By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Firebase Auth - with Email and Password - Check user already registered

Dynamically update html content plain JS

How to determine optimal route across keyboard