Bypassing CORS via custom proxy backend
Alen Duda
Posted on December 13, 2021
As a frontend developer, sooner or later you will encounter a CORS error, something like this:
to XMLHttpRequest at 'https://...' from origin 'https://...' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
CORS intro
Plainly, Cross-Origin Resource Sharing is a security mechanism which enables web browsers to access data from domain2.com while browsing domain1.com. It can also be used to restrict access only to predefined domains. Basically, it requires the backend and frontend to be on the same server or to specifically set allowed origins which can access the backend.
CORS is disabled by default and, if you have access to the server-side code, there are ways to enable it. If you are in a school group project with a backend dev, be sure to remind him/her to enable CORS or you might be stuck with mock data (speaking from experience).
Inspiration
I first encountered the red CORS error in the browser console on a university project one saturday night when I tried to connect to our Java Spring backend and couldn't get it to work, even though it worked from Postman. Since Java (and specifically Spring) was (and is) almost as Ancient Greek to me, I wanted to try a way to bypass this error. Since CORS is on the browser level, an idea popped up: why not build a simple(r) JS backend which does the same API request, but has CORS enabled so I could connect to it instead of the original Java backend.
Express backend
Express.js is the first node.js web framework I encountered and is well-suited for this task. We will create a minimal node/express backend application which uses axios as the http library and the cors package to enable CORS on our server (otherwise this whole ordeal would be pointless).
Project setup and package installation
After creating a folder for our project, open a terminal and navigate to it. We initialize the most basic package.json file with
npm init -y
Once done, we install the required packages:
npm i express cors axios
Before starting to code, we need a file which will be run. Common names are server.js
or app.js
. Since this project will have all the code in a single file (not the best practice, but for demonstration purposes), we can simply use index.js
. Create that file and modify the package.json file so the scripts key looks like this:
"scripts": {
"start": "node index"
},
Coding time
Finally, time to code! Open index.js
(or whatever you called it in the previous step) so we can create our server. I will copy all the code required here, along with the comments for (almost) each line.
// packages import
const express = require("express");
const app = express();
const cors = require("cors");
const axios = require("axios");
// enable CORS
app.use(cors());
// set the port on which our app wil run
// important to read from environment variable if deploying
const port = process.env.PORT || 5000;
// basic string route to prevent Glitch error
app.get("/", (req, res) => {
res.send("Hello World!");
});
// the route we're working with
app.get("/users", (req, res) => {
// replace with a custom URL as required
const backendUrl = "https://jsonplaceholder.typicode.com/users";
// return the data without modification
axios.get(backendUrl).then(response => res.send(response.data));
});
// console text when app is running
app.listen(port, () => {
console.log(`Server listening at http://localhost:${port}`);
});
And that is it! You can use the above code and upload it to Glitch, for example, so it can be hosted and accessed if you deploy your frontend app. That's why we require reading the PORT from environment variable (if available) and set a root route to return a simple string, otherwise Glitch would belive the app has an error since nothing is returned.
The "/users"
route contains the main code we need to connect to the backend which doesn't have CORS access enabled and returns the same, unmodified data.
Additional bonus: data modification
While you can return the data as-is, nothing stops you from modifying the original response to be more adapted to your frontend app's needs. If there is a lot of data and modifications required, that could improve the frontend app's performance on lower-end devices and slower connections, since less noise data will be received and less modifications are required client-side.
Example response from original backend API:
The code snippet for modifying this is pretty straightforward (assuming the response has the same data structure as above):
axios.get(backendUrl).then(response => {
const lastEpisodes = response.data.data.lastAvailableEpisodes;
const shows = lastEpisodes.map(episode => ({
id: episode.contentItemId,
title: episode.caption,
audioFile: episode.audio.metadata[0].path
}));
res.send(shows);
});
Example custom API response after modification:
I believe you agree that the second response is much cleaner and easier to follow.
Conclusion
This was a very basic example of using a custom, bare-bones backend as a proxy to bypass CORS-restricted content you would generally have access to. It also follows a so-called happy path, meaning there is no error handling, but that would detract from the topic. The whole process from creating the project, modifying the response and deployment to Glitch can take less than 10 minutes, which is much quicker than waiting for your backend-dev colleague to wake up the next morning when the inspiration is gone.
Posted on December 13, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.