Last updated: Apr 5, 2024
Reading timeยท4 min
The "Cannot POST /" error in Express and Node.js is caused when there is a mismatch between the path in the route handler and the path your form is making a POST request to.
To solve the error, make sure the action
attribute of the form corresponds
to the path in the request handler.
Here is a minimal example of making a POST request in an Express.js application.
Let's start with the form that makes the POST request.
This is my index.html
file.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> </head> <body> <h2>bobbyhadz.com</h2> <form action="/register" method="post"> <div> <label>Username:</label> <input type="text" name="username" /><br /> </div> <div> <label>Password:</label> <input type="password" name="password" /> </div> <div><input type="submit" value="Register" /></div> </form> </body> </html>
The form is quite minimal, it has 2 fields - a username and a password.
This is my index.js
file.
// ๐๏ธ using require() CommonJS imports const express = require('express'); const bodyParser = require('body-parser'); const app = express(); app.use(express.static(__dirname + '/public')); app.use(bodyParser.json()); app.use( bodyParser.urlencoded({ extended: false, }), ); app.get('/register', function (req, res) { // ๐๏ธ if your HTML file is in the root directory (next to package.json) res.sendFile(__dirname + '/index.html'); }); app.post('/register', function (req, res) { console.log( `username: ${req.body.username}, password: ${req.body.password}`, ); res.send( `username: ${req.body.username}, password: ${req.body.password}`, ); }); const port = 5000; app.listen(port, () => { console.log(`Example app listening on port ${port}`); });
Here is the equivalent code sample using the ES6 modules import/export syntax.
// ๐๏ธ using ES6 Modules import/export syntax import express from 'express'; import bodyParser from 'body-parser'; import path from 'path'; import {fileURLToPath} from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const app = express(); app.use(express.static(__dirname + '/public')); app.use(bodyParser.json()); app.use( bodyParser.urlencoded({ extended: false, }), ); app.get('/register', function (req, res) { // ๐๏ธ if your HTML file is in the root directory (next to package.json) res.sendFile(__dirname + '/index.html'); }); app.post('/register', function (req, res) { console.log( `username: ${req.body.username}, password: ${req.body.password}`, ); res.send( `username: ${req.body.username}, password: ${req.body.password}`, ); }); const port = 5000; app.listen(port, () => { console.log(`Example app listening on port ${port}`); });
You can start the server with npx nodemon index.js
and open
http://localhost:5000/register.
npx nodemon index.js
As shown in the short clip everything works as expected.
Things to note when solving the error:
action
attribute matches the path in your
app.post
route handler.For example, my form element has an action
attribute set to /register
.
<form action="/register" method="post">
So my route handler should also set its path to /register
.
app.post('/register', function (req, res) { // ... })
You should also have a corresponding app.get
handler that is used to render
the form when the user visits the page.
app.get('/register', function (req, res) { // ๐๏ธ if your HTML file is in the root directory (next to package.json) res.sendFile(__dirname + '/index.html'); });
/register
route, the app.get()
route handler
renders the index.html
file (the form).action
attribute POSTS the data to the /register
route.app.post()
route handler for /register
.app.post()
route handler logs the submitted username and password and
sends them back to the user.app.post('/register', function (req, res) { console.log( `username: ${req.body.username}, password: ${req.body.password}`, ); res.send( `username: ${req.body.username}, password: ${req.body.password}`, ); });
The code sample assumes that your index.html
and index.js
files are located
in the root directory of your project (where your package.json
file is).
bodyParser
module.// ๐๏ธ using require() CommonJS syntax const express = require('express'); const bodyParser = require('body-parser'); const app = express(); app.use(express.static(__dirname + '/public')); app.use(bodyParser.json()); app.use( bodyParser.urlencoded({ extended: false, }), );
And here is the equivalent code sample using the ES6 modules import/export syntax.
// ๐๏ธ using ES6 import/export syntax import express from 'express'; import bodyParser from 'body-parser'; import path from 'path'; import {fileURLToPath} from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const app = express(); app.use(express.static(__dirname + '/public')); app.use(bodyParser.json()); app.use( bodyParser.urlencoded({ extended: false, }), );
The code sample assumes that you have a /public
directory where you store your
static (JS, CSS, images) files.
req.body
object.app.post('/register', function (req, res) { console.log( `username: ${req.body.username}, password: ${req.body.password}`, ); res.send( `username: ${req.body.username}, password: ${req.body.password}`, ); });
The action
attribute of the form
element in your index.html
file should
match the path in your app.get()
and app.post()
request handlers.
<form action="/register" method="post">
The action
attribute is set to /register
in the example above.
Here is the app.get()
route handler which renders the form element when the
user visits /register
.
app.get('/register', function (req, res) { res.sendFile(__dirname + '/index.html'); });
The route handler shows the form when the user visits the /register
route.
And here is the app.post()
route handler which is called when the user submits
the form (because the form's action corresponds to the path).
app.post('/register', function (req, res) { console.log( `username: ${req.body.username}, password: ${req.body.password}`, ); res.send( `username: ${req.body.username}, password: ${req.body.password}`, ); });
The route gets triggered when the form with action
set to /register
is
submitted.
You can learn more about the related topics by checking out the following tutorials: