23

I'm struggling with the fetch API in Javascript. When I try to POST something to my server with fetch method, the request body contains an empty array. But when I use Postman it works. Here is my server-side code in Node.js:

const express = require('express')
const app = express()
const port = 3000

app.use(express.json())
app.post('/api', function (req, res) {
    console.log(req.body)
})
app.listen(port)

Here is my client-side code:

fetch('http://"theserverip":3000/api', {
    method: 'POST',
    headers: { "Content-Type": "application/json" },
    mode: 'no-cors',
    body: JSON.stringify({
        name: 'dean',
        login: 'dean',
    })
})
.then((res) => {
    console.log(res)
})

The problem is that the req.body is empty on server side.

VLAZ
  • 26,331
  • 9
  • 49
  • 67
Trietch
  • 373
  • 1
  • 2
  • 9

5 Answers5

42

The issue is

mode: 'no-cors'

From the documentation...

Prevents the method from being anything other than HEAD, GET or POST, and the headers from being anything other than simple headers

These are accept, accept-language, content-language, content-type and range.

The simple content-type header restriction allows the values

  • text/plain,
  • application/x-www-form-urlencoded, and
  • multipart/form-data

This causes your nicely crafted Content-Type: application/json header to become content-type: text/plain (at least when tested through Chrome).

Since your Express server is expecting JSON, it won't parse this request.

I recommend omitting the mode config. This uses the default "cors" option instead.


Since your request is not simple, you'll probably want to add some CORS middleware to your Express server.

Another (slightly hacky) option is to tell Express to parse text/plain requests as JSON. This allows you to send JSON strings as simple requests which can also avoid a pre-flight OPTIONS request, thus lowering the overall network traffic...

app.use(express.json({
  type: ['application/json', 'text/plain']
}))

EDIT: Added ending parenthesis to app.use final code block.

Phil
  • 157,677
  • 23
  • 242
  • 245
4

To complement the excellent answers that mentioned removing the mode: 'no-cors' option, and if you don't feel like adding a CORS middleware to Express, you may just want to handle "pre-flight" requests under an OPTIONS block:

app.options('*', (req, res) => {
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); // Add other headers here
  res.setHeader('Access-Control-Allow-Methods', 'POST'); // Add other methods here
  res.send();
});

Another nice answer here : https://www.vinaygopinath.me/blog/tech/enable-cors-with-pre-flight/

Hope this helps!

Philippe Sultan
  • 2,111
  • 17
  • 23
  • Why would you opt for an incomplete CORS implementation over the industry standard middleware? – Phil Nov 16 '22 at 00:10
3

Please see document mode, no-cors mode only send simple headers. So the content-Type is changed to text/html; instead of application/json so your server can't recognize body format and return empty.

Remove mode: 'no-cors', and you should be fine.

yip102011
  • 751
  • 4
  • 11
0

none seems to work for me I solved this issue using middleware

app.use(restify.plugins.bodyParser());

or

app.use(multer().array())
Jason Aller
  • 3,541
  • 28
  • 38
  • 38
-1

Many of the above answers weren't resolving my issue, so anyone running into this problem may need to do what I needed to do and enable their app to be capable of reading JSON in a body, as the app can't do that by default which is what's leading to the empty body

I resolved this by adding:

app.use(bodyParser.json());

POST/PUT requests sent the body no problem after adding this one line

Technically, the bodyParser module is deprecated, so this isn't the most ideal solution, but if adding the above line to your code makes it work then you at least know your issue lies in the way your app can parse/read JSON in a body

mmarion
  • 859
  • 8
  • 20