Search code examples
node.jsangularexpressherokucors

CORS preflight request error on angular app deployed on heroku with node express backend


I have deployed an angular app on heroku (with the frontends and backend separated in multiple heroku apps) for a project at school and I'm stuck on an issue with CORS preflight requests.

Everytime I send a request to the backend I get the following error

Access to XMLHttpRequest at 'https://playlist-manager-backend.herokuapp.com/user/authenticate' from origin 'https://playlist-manager-admin.herokuapp.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Here is the code of my server.js pushed on the backend app (playlist-manager-backend)

const express = require("express");
const { connect: dbconnect } = require("./src/db");
const { apiPort } = require("./src/config");
const routes = require("./src/routes");
const bodyParser = require("body-parser");
// Connect to the database
dbconnect();

// Create the server
const app = express();

// const cookieParser = require("cookie-parser");
// app.use(cookieParser());
app.use(bodyParser.json({ limit: "5mb" }));
// app.get("/", (req, res) => res.send("Hello World"));
const cors = require("cors");

/*let whitelist = ["https://playlist-manager-admin.herokuapp.com/","https://playlist- manager-user.herokuapp.com/"];*/

const corsOptions = {
    origin: ["https://playlist-manager-admin.herokuapp.com/", "https://playlist-manager-user.herokuapp.com/"],
    preflightContinue:false,
    credentials: true
}

app.use(cors(corsOptions));


app.use("/", routes);
app.use(express.static("./adverts"));

app.listen(apiPort, () => {
    console.log(`Server is listening on port ${apiPort}`);
});

The routes to the backend functions are defined using router in the routes.js file like this.

const express = require("express");
const router = express.Router();

const userRoute = require("../user/user.route");
const playlistRoute = require("../playlist/playlist.route");
const videoRoute = require("../video/video.route");
const annonceUploadRoute = require("../annoncesUpload/annoncesUpload.route");
const historyRoute = require("../history/history.route");
const advertiserRoute = require("../advertiser/advertiser.route");

router.use("/user", userRoute);
router.use("/playlist", playlistRoute);
router.use("/video", videoRoute);
router.use("/annoncesUpload", annonceUploadRoute);
router.use("/history", historyRoute);
router.use("/annonceur", advertiserRoute);

module.exports = router;

Here is an example of how routes are implemented :

router.post("/getById", async function(req, res) {
     const request = {
         _id: req.body._id,
     };
     const result = await bo.getById(request);
     res.send(result);
});

The frontend sends a post request to the backend using a service implemented like this :

import { Injectable } from '@angular/core';
import {HttpClient} from '@angular/common/http';
import { Observable } from 'rxjs';
import { environment } from 'src/environments/environment';

export interface BackendData {
  success: string ;
  data: any ;
}

@Injectable({
  providedIn: 'root'
})
export class MessageService {

  constructor(private http:HttpClient) { }

  sendMessage(Url:string, data:any): Observable<BackendData> {
    const serveur = environment.server + Url ;
    
    return this.http.post<BackendData>(
      serveur,
      data,
      {withCredentials: true}
    );
  }
}

I tried every solutions I saw online but I always got the same error or another error related to CORS policy. I need help figuring out this behavior. The app was working fine locally when I specified localhost with cors package like in the documentation.

Edit : I removed slashes on the URL's and my server.js is like this :

const express = require("express");
const { connect: dbconnect } = require("./src/db");
const { apiPort } = require("./src/config");
const routes = require("./src/routes");
const bodyParser = require("body-parser");
// Connect to the database
dbconnect();

// Create the server
const app = express();

// const cookieParser = require("cookie-parser");
// app.use(cookieParser());
app.use(bodyParser.json({ limit: "5mb" }));
// app.get("/", (req, res) => res.send("Hello World"));
const cors = require("cors");

/*let whitelist = ["https://playlist-manager-admin.herokuapp.com/", "https://playlist-manager-user.herokuapp.com/"];*/

const corsOptions = {
  origin: ["https://playlist-manager-admin.herokuapp.com", "https://playlist-manager-user.herokuapp.com"],
  credentials: true
}

app.use(cors(corsOptions));


app.use("/", routes);
app.use(express.static("./adverts"));

app.listen(apiPort, () => {
    console.log(`Server is listening on port ${apiPort}`);
});

This is the network tab after sending the request

This is the xhr request shown in the network tab

This is the preflight request in the network tab


Solution

  • Thanks to jubObs comments I managed to fix this. The source of the problem was that the backend server wasn't starting which was causing a 503 status. I forgot to add the file defining the credentials for the database connections which caused the backend to fail before adding the headers.

    here is the new server.js code :

    const express = require("express");
    const { connect: dbconnect } = require("./src/db");
    const { apiPort } = require("./src/config");
    const routes = require("./src/routes");
    const bodyParser = require("body-parser");
    // Connect to the database
    dbconnect();
    
    // Create the server
    const app = express();
    
    // const cookieParser = require("cookie-parser");
    // app.use(cookieParser());
    app.use(bodyParser.json({ limit: "5mb" }));
    // app.get("/", (req, res) => res.send("Hello World"));
    const cors = require("cors");
    
    /*let whitelist = ["https://playlist-manager-admin.herokuapp.com/", "https://playlist-manager-user.herokuapp.com/"];*/
    
    const corsOptions = {
       origin: ["https://playlist-manager-admin.herokuapp.com", "https://playlist-manager-user.herokuapp.com"],
       credentials: true
    }
    
    app.use(cors(corsOptions));
    
    
    app.use("/", routes);
    app.use(express.static("./adverts"));
    
    app.listen(apiPort, () => {
       console.log(`Server is listening on port ${apiPort}`);
    });
    

    The database connection was using dotenv and the .env file that was defining the database credentials wasn't present in the main branch so I pushed it. To figure this out I checked the application logs on heroku which are in "more" and "view logs".