1

I am trying to do a post request with axios to upload a image to cloudinary from my frontend React app. I am getting this error from the axios code below:

http://localhost:3000 has been blocked by CORS policy: Request header field x-access-token is not allowed by Access-Control-Allow-Headers in preflight response.

Using axios, doesnt work gives me cors error

await axios({
  method: "POST",
  url: "https://api.cloudinary.com/v1_1/******/image/upload/",
  data: {
    file: img,
    upload_preset: "*****",
    cloud_name: "****",
  },
})
  .then((res) => {
    console.log("response");
    console.log(res);
  })
  .catch((err) => console.log(err));

Meanwhile when i use fetch using the same api request, the post request works and doesnt give me error. Anyone know why and how to call the api using axios?

  const data = new FormData();
        data.append("file", img);
        data.append("upload_preset", "*****");
        data.append("cloud_name", "*****");


  await fetch(
           "  https://api.cloudinary.com/v1_1/****/image/upload/",
           {
             method: "post",
             body: data,
           }
         )
           .then((resp) => resp.json())
           .then((data) => {
             setUrlArray((prevState) => [...prevState, data.url]);
           })
           .catch((err) => console.log(err));

Extra info: My upload preset is unsigned.
Also got this from the console after making the axios api call

{
error: {
message: "Upload preset must be specified when using unsigned upload"
}
}

dotaplayer
  • 33
  • 2
  • 6
  • 1
    I would check the headers in your dev tools and see what's different between the requests. – Chris B. Jan 19 '22 at 00:29
  • Axios uses the `data` property, **not** `body`. See [Request Config](https://axios-http.com/docs/req_config#:~:text=data%3A%20%7B%0A%20%20%20%20firstName%3A%20%27Fred%27%0A%20%20%7D%2C) – Phil Jan 19 '22 at 00:51
  • Another difference is that your Axios request is sending JSON but your `fetch()` is sending `multipart/form-data`. If `fetch()` works for you, why not just use that instead of Axios? – Phil Jan 19 '22 at 00:56
  • I need to use axios because the codebase is using axios, and its required to be consistent. I also changed body to data still doesnt work. ;( – dotaplayer Jan 19 '22 at 00:56
  • In your code, where does `axios` come from? Where is the `x-access-token` header coming from? – Phil Jan 19 '22 at 01:30
  • i am using axios from the import like you described below, nothing else. Idk why i get x-access token from header. This is the full cors error----> error Access to XMLHttpRequest at 'https://api.cloudinary.com/v1_1/CLOUDNAME/image/upload/' from origin 'http://localhost:3000' has been blocked by CORS policy: Request header field x-access-token is not allowed by Access-Control-Allow-Headers in preflight response. – dotaplayer Jan 19 '22 at 01:34
  • 1
    I found it. In one of the redux thunk file they have axios.interceptors.request.use(async function (config) { const token = await localStorage.getItem("messenger-token"); config.headers["x-access-token"] = token; return config; }); – dotaplayer Jan 19 '22 at 01:41
  • Also found one in the server file for nodejs. app.use(function (req, res, next) { const token = req.headers["x-access-token"]; if (token) { jwt.verify(token, process.env.SESSION_SECRET, (err, decoded) => { if (err) { return next(); } User.findOne({ where: { id: decoded.id }, }).then((user) => { req.user = user; return next(); }); }); } else { return next(); } }); – dotaplayer Jan 19 '22 at 01:43
  • 1
    What a bizarre interceptor. `localStorage.getItem()` is synchronous, there's no need for `await` there – Phil Jan 19 '22 at 01:56

1 Answers1

3

To create an Axios request equivalent to your working fetch() one, you need to

  1. Craft a FormData instance and set it as the request data so your content-type is multipart/form-data

  2. Make sure you're not using a previously created Axios instance with unwanted default headers

  3. If custom headers have been set on the default Axios instance, eg

    axios.defaults.headers.common["x-access-token"] = TOKEN
    

    you may need to override / delete them in transformRequest

  4. To avoid any interceptors defined on the default Axios instance, create a new separate instance for un-intercepted requests

import axios from "axios" // import the default instance

// create a new instance without interceptors. 
// you could also create this in its own module and import from there
const instance = axios.create()

const data = new FormData()
data.append("file", img);
data.append("upload_preset", "*****");
data.append("cloud_name", "*****");

const res = await instance.post(
  "https://api.cloudinary.com/v1_1/******/image/upload/", 
  data
)

Ideally, if your app is going to customise requests, you should always use an Axios instance (or multiple instances) to avoid messing around with the defaults.

Phil
  • 157,677
  • 23
  • 242
  • 245