Bypassing CORS via custom proxy backend

Alen Duda - Dec 13 '21 - - Dev Community

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.

Enter fullscreen mode Exit fullscreen mode

CORS error in console

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"
  },


Enter fullscreen mode Exit fullscreen mode

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}`);
});


Enter fullscreen mode Exit fullscreen mode

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:
Original API response

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);
    });


Enter fullscreen mode Exit fullscreen mode

Example custom API response after modification:
Custom API response

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.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .