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
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".