102

I've written a REST API on the express framework for node.js that works for requests from the js console in Chrome, and URL bar, etc. I'm now trying to get it working for requests from another app, on a different domain (CORS).

The first request, made automatically by the javascript front end, is to /api/search?uri=, and appears to be failing on the "preflight" OPTIONS request.

In my express app, I am adding CORS headers, using:

var allowCrossDomain = function(req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
    res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');

    // intercept OPTIONS method
    if ('OPTIONS' == req.method) {
      res.send(200);
    }
    else {
      next();
    }
};

and:

app.configure(function () {
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(allowCrossDomain);
  app.use(express.static(path.join(application_root, "public")));
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

From the Chrome console I get these headers:

Request URL:http://furious-night-5419.herokuapp.com/api/search?uri=http%3A%2F%2Flocalhost%3A5000%2Fcollections%2F1%2Fdocuments%2F1

Request Method:OPTIONS

Status Code:200 OK

Request Headers

Accept:*/*
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Access-Control-Request-Headers:origin, x-annotator-auth-token, accept
Access-Control-Request-Method:GET
Connection:keep-alive
Host:furious-night-5419.herokuapp.com
Origin:http://localhost:5000
Referer:http://localhost:5000/collections/1/documents/1
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5

Query String Parameters

uri:http://localhost:5000/collections/1/documents/1

Response Headers

Allow:GET
Connection:keep-alive
Content-Length:3
Content-Type:text/html; charset=utf-8
X-Powered-By:Express

Does this look like a lack of proper headers being sent by the API application?

Thanks.

Jamie Folsom
  • 1,287
  • 2
  • 10
  • 20
  • I'm getting this error in a code I didn't wrote, but I don't understand the need of a handler for the `OPTIONS` method. Could someone please help me to understand why not handling just the `POST` method instead of handling both `POST` **and** `OPTIONS` method? – Ulysses Alves Jun 20 '16 at 13:50
  • Might also want to include `PATCH` if you will use it instead of `PUT` to update a resource – Danny Jan 23 '19 at 16:39

6 Answers6

66

I've cheked your code on a clean ExpressJS app and it works just fine.

Try move your app.use(allowCrossDomain) to the top of configure function.

Olegas
  • 10,349
  • 8
  • 51
  • 72
  • 8
    The reason it did is because you need to have it defined before the `app.use(app.router);` Cheers! – Michal Aug 19 '12 at 14:34
  • In my case, next POST is not being called after sending back res.send(200) when req.method == 'OPTIONS'. am I missing anything else ? – Aldo Nov 22 '12 at 17:03
  • 2Aldo: Code needed. May be you forgot some headers? If your client is not sending a POST after your server is correctly served a preflight OPTIONS request, try to check developer tools console. WebKit logs this kind of errors to web inspector's console. – Olegas Nov 24 '12 at 20:13
  • 1
    @ConnorLeech Very nice. I had been doing the allowCrossDomain approach as above and was tired of dealing with all that header mgmt. Also - since we only needed CORS for dev, wasn't making any sense to devote so many cycles to figuring out what was going on. Glad there is node.js support for easily allowing this. – Steven Jan 28 '15 at 19:30
  • 1
    @ConnorLeech I think you should add your comment as an answer... works like a treat, and it's nice and simple – drmrbrewer Nov 19 '17 at 17:25
5

I'm adding this as an answer only because the original post was put in as a comment and as such it got overlooked by yours truly the first time I went over this page.

As @ConnorLeech points out in his comment to the accepted answer above, there is a very handy npm package called, not surprisingly, cors. It's use is as simple as var cors = require('cors'); app.use(cors()); (again, cribbed from Mr. Leech's answer) and can also be applied in a stricter, more configurable fashion as outlined in their docs.

It may also be worth pointing out that the original comment I refer to above was made in 2014. It's 2019 now and looking at the npm package's github page the repo was updated as recently as nine days ago.

flyingace
  • 1,707
  • 17
  • 24
4

for supporting cookies withCredentials you need this line xhr.withCredentials = true;

mdn docs xhr.withCredentials

In the Express Server add this block before all the other

`app.all('*', function(req, res, next) {
     var origin = req.get('origin'); 
     res.header('Access-Control-Allow-Origin', origin);
     res.header("Access-Control-Allow-Headers", "X-Requested-With");
     res.header('Access-Control-Allow-Headers', 'Content-Type');
     next();
});`
doron aviguy
  • 2,554
  • 2
  • 22
  • 18
2

It could not be the case for most people browsing this question but I had this exact same problem and the solution was not related to CORS.

Turns out that JSON Web Token secret string was not defined in the environment variables, so the token could not be signed. This caused to any POST request that relies on checking or signing a token to get a timeout and return a 503 error, telling the browser that there's something wrong in CORS, which it's not. Adding the environment variable in Heroku solved the issue.

I hope this helps someone.

CatBrownie
  • 150
  • 3
  • 11
  • Yes that was the problem with me. Could you be more specific how you solved problem. I am still in development still and am running on Express.js w/node cors package. – Alan Nov 27 '17 at 18:06
  • @alan I was using promises to verifying the token in my application, some how if the JWT secret is not defined, the promise will never resolve, here's the [code](https://github.com/elbuki/remindr-api/blob/develop/src/middlewares/verifyToken.js) I was using if you need further reference. – CatBrownie Nov 27 '17 at 22:20
  • Thanks for responding. I will look over code. By secret I assume you are talking about the second private key. I am using oauth2. – Alan Nov 27 '17 at 22:24
  • I will add that any failing promise will produce this misleading error! I had to be explicit about a Node version being used or else my database calls (mongo) were simply hanging and the only thing the browser could tell me was 503 and then some Cors related silliness. This error really confused me for the longest as I had `app.use(cors());` going. – alphanumeric0101 Apr 02 '18 at 20:22
  • @CatBrownie, thanks for this answer. Exactly what I needed! – brunelli Apr 01 '21 at 09:13
2

enter image description here

Follow the below steps:

npm install cors --save

Inside your root js file:

 var express = require('express') 
 var cors = require('cors')
 var app = express()
 app.use(cors())
kk.
  • 3,747
  • 12
  • 36
  • 67
0

The Cors error could be a cover up for your actual error. Look in the logs of your heroku build to see your potential real error. Mine was this.

Heroku Postgres: "psql: FATAL: no pg_hba.conf entry for host"

Kuda
  • 95
  • 1
  • 9