Making CORS API requests with create-react-app and webpack - no express

17,808

Solution 1

The fact that you're seeing a 500 response to the preflight OPTIONS request makes me think that perhaps the OPTIONS request method simply isn't allowed by the web server.

Many web servers default to only allowing HEAD, GET and POST, so that would be the first thing I'd check.

If that doesn't help, ensure that the above webpack.config code runs for all requests (i.e. for the OPTIONS and the GET/POST).

If that doesn't help, post a full set of request and response headers to both the OPTIONS and the GET request, and let's see where we go from there.

Solution 2

Cannot add comment yet, so I will put it as an answer.

If you are trying to access an API served by another node app or from another host. You can config webpack-dev-server to proxy that request to the real server.

Check the Create React App Doc: Proxying API Requests in Development for more details.

Solution 3

the best option is to use a proxy especially if you use external API's or multiple API's. Just install proxy-middleware and create the file src/setupProxy.js and inside the file, you can add similar code to this:

const proxy = require('proxy-middleware');

module.exports = app => {
  // setup proxies
  // 1st api
  app.use("/etherscan", proxy("http://api.etherscan.io/api"));
  // 2nd api
  app.use("/cryptocompare", proxy("https://min-api.cryptocompare.com/data"));

  // Note: setupProxy is an express server so you can also override anything in the req or res before proxy them for example 
  app.use("/cryptocompare", (req,res,next) => {
     req.headers = { 
        ...req.headers,
        "my-header":"my header value"
     }
     return proxy("https://min-api.cryptocompare.com/data")(req,res,next);
  });

  app.use("/cryptocompare", proxy("https://min-api.cryptocompare.com/data"));


  // also it's better to use .env variables
  // const {REACT_APP_API_ETHERSCAN_PROXY, REACT_APP_API_ETHERSCAN_BASE_URL} = process.env;
  // app.use(REACT_APP_API_ETHERSCAN_PROXY, REACT_APP_API_ETHERSCAN_BASE_URL);      
};

And for your API it's also better if you can get the base URL from .env variables for example:

const {REACT_APP_API_ETHERSCAN_PROXY, REACT_APP_API_ETHERSCAN_BASE_URL} = process.env;
const baseURL = REACT_APP_API_ETHERSCAN_PROXY || REACT_APP_API_ETHERSCAN_BASE_URL;

Additional advice: instead of keep passing process.env.REACT_APP_API_URL and headers for every API call you may take a look at axios where you can create an API instance with default properties for example:

const {
  REACT_APP_API_ETHERSCAN_PROXY, 
  REACT_APP_API_ETHERSCAN_BASE_URL,
  REACT_APP_API_TIMEOUT,
  REACT_APP_API_ETHERSCAN_KEY
} = process.env;

export const etherscanAPI = axios.create({
  "baseURL": REACT_APP_API_ETHERSCAN_PROXY || REACT_APP_API_ETHERSCAN_BASE_URL,
  "timeout": parseInt(REACT_APP_API_TIMEOUT),
  "headers": {
    "X-Requested-With": "XMLHttpRequest"
  },
  "params": {
    "apiKey": REACT_APP_API_ETHERSCAN_KEY
  }
});

then etherscanAPI.get("/whatever")

Share:
17,808
Admin
Author by

Admin

Updated on June 05, 2022

Comments

  • Admin
    Admin almost 2 years

    I'm trying to make API calls without disabling CORS in my browser. The app was created using react-create-app. It is using webpack, and webpackDevServer for dev. All the answers I found on here say to put the following code into my webpack.config file.

    devServer: {
          headers: {
            "Access-Control-Allow-Origin": "*",
            "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
            "Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization"
          }
      }
    

    However, I've tried it in a number of different places and it doesn't work.

    I continue to get the error:

    Failed to load resource: the server responded with a status of 500 ()

    and

    Failed to load ... Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. The response had HTTP status code 500.

    I also attempted to change my calls from axios calls to fetch calls

    return fetch(process.env.REACT_APP_API_URL + "/prod/user", {
        mode: 'no-cors',
        credentials: 'include',
        method: "GET",
        headers: {
            "x-api-key": process.env.REACT_APP_API_KEY
        }
    })
    .then(response => {
       return response.json()
    })
    .then(resp => dispatch(receiveUsers(resp.data)))
    .catch(error => {console.log(error)})
    

    That got me closer but the console now is returning

    Response {type: "opaque", url: "", redirected: false, status: 0, ok: false, …}

    How do I get around this CORS issue? Thanks in advance for your help!

    Response/Request Headers

  • Admin
    Admin about 6 years
    This was helpful thanks. The API I'm making my calls to does in fact allow *, and I was calling the POST and GET incorrectly. It wound up that I had to specify the content-type to be "application/x-www-form-urlencoded" for POST and stringify the data to post to the API. So now I can GET and POST, but my PUT request gets caught up in CORS because it's being sent as OPTIONS - which is returning a 200 - but failing the preflight access control check. I should be able to make the PUT request, I'm not sure why it isn't working. Maybe it's something with the way I'm making the request w/ axios?
  • Admin
    Admin about 6 years
    Correction. PUT is always going to make the browser throw a preflight OPTIONS request. The question now is, why is the browser blocking the PUT request when the API allows CORS.
  • roryhewitt
    roryhewitt about 6 years
    A PUT request will cause the browser to first send a preflight OPTIONS request. If you're not sending valid CORS headers in response to that OPTIONS request, then the browser won't even make the subsequent PUT request itself. Can you post the request/response headers for the OPTIONS request?
  • Admin
    Admin about 6 years
    Hey Rory, thanks - I added a link to the image in the original post.
  • roryhewitt
    roryhewitt about 6 years
    OK, so you need to include the CORS response headers with the OPTIONS request as well as with the PUT request. That's not currently happening. I suspect your webpack.config definition isn't being used for the OPTIONS request, but I don't know exactly what your code is. But I do see that now you're getting a 200 response for the OPTIONS request, so something has changed since you first posted the question.
  • Admin
    Admin almost 6 years
    The API needed to be adjusted to accommodate the OPTIONS request. Thanks for your help @roryhewitt
  • Rick
    Rick over 5 years
    Link has changed. The new one is: facebook.github.io/create-react-app/docs/…
  • Catarina
    Catarina over 4 years
    how can I send headers in setupProxy?
  • cakidnyc
    cakidnyc over 4 years
    I had this same problem and this proxy route is by far the easiest. Just one line in your package.json (e.g. "proxy": "localhost:8090") to route to another host for API calls while in DEV mode (npm start).