Resolving CORS Restriction using Node.js: A Guide for Working with Third-Party APIs
As front-end developers, we may have encountered the CORS (Cross-Origin Resource Sharing) issue when trying to access an API from a different domain. This issue arises when the browser blocks requests made from a different domain, preventing our application from accessing the data we need. However, there is a lot of fixes/solutions to this problem, and we will look into one of the easiest yet efficient solution. It involves using Node.js to create a backend proxy server, which can be used to bypass CORS restrictions by forwarding the client's request to a server that is not subject to the same origin policy.
In this document, we will see how to use Node.js to create a server that can make API requests without being blocked by CORS restrictions.
Problem Statement
When making an API request from a Instafood
- a Hobby React application for online food delivery, to an Swiggy API
endpoint, the browser is throwing an CORS
error, which is preventing the application from accessing the data it needs. This CORS error is occurring because the Swiggy API server is not configured to allow cross-origin
requests from the domain where the React application is hosted. As a result, the React application is unable to retrieve the data it needs from the Swiggy API.
Usually, when the server is also accessible by us, the problem could be fixed by setting appropriate headers to allow the domain in which our application is running. But, in our case, the Swiggy API is a third-party API, which means that we do not have control over its server configuration. Therefore, we need to find a way to work around the CORS error and retrieve the data from the Swiggy API so that the React application can function properly.
Possible Solutions
Following are some of the possible solutions to relax this CORS restriction :
1. Browser Extensions
Browser extensions can be used as a quick
fix for CORS issues in situations where you do not have access to the server-side code. CORS-related browser extensions work by adding the appropriate headers to outgoing requests, effectively bypassing the browser's same-origin policy.
Example : Allow CORS: A Chrome extension that adds the necessary CORS headers to requests.
This solution will only work for the particular browser where the extension is installed, and will not affect requests made from other browsers or applications. It is important to note that using a browser extension to fix CORS issues is not a recommended long-term solution. It is always best to address CORS issues at the server level by properly configuring the CORS headers and allowing requests from trusted domains.
2. CORS proxy services
There are several CORS proxy services available that act as middlemen between the client and the server, adding the necessary CORS headers to the request.
Example : CORS Anywhere
is a Node.js proxy server that can be used to bypass CORS restrictions by adding the necessary headers to the proxied request. When a request is made to CORS Anywhere, it adds the appropriate CORS headers and forwards the request to the destination API. Once the response is received from the API, the proxy server removes the CORS headers and sends the response back to the requesting client.
Note that using a third-party CORS proxy like CORS Anywhere is not recommended for production use, as it can potentially expose our API key and other sensitive information to the proxy server. It is better to set up our own server and handle CORS headers ourselves.
3. Server-Side/Backend Proxy
This the solution that we will be using to fix the CORS restriction. It is simliar to CORS proxy service mentioned above, we will be created our own server/service instead of using the third-party service.
Step 1: Set up our environment
Before we get started, make sure you have Node.js installed on our system. If you don't have it installed, head over to the official Node.js website and download the latest stable version.
Step 2: Create a new project
To get started, create an empty git repository (since we will be deploying the server from github)and clone it in our local machine. Then, run the following command to initialize a new Node.js project:
npm init
This command will create a package.json file that will hold information about our project and its dependencies.
Step 3: Install the required packages
Next, we need to install the packages required to create our server. Run the following command to install Express, cors, and cross-fetch:
npm install express cors cross-fetch
Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.
cors is a package that allows you to enable CORS with various options.
cross-fetch is a lightweight package that allows us to make HTTP requests It provides a polyfill for the Fetch API in Node.js.
Step 4: Create our server
Create a new file in our project directory called server.js
. This file will contain the code for our server.
Import the required packages mentioned above and use it
const express = require('express');
const cors = require('cors');
const fetch = require('cross-fetch');
const app = express();
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Server is listening on port ${port}`);
});
In NodeJS, require() is a built-in function
to include external modules that exist in separate files. require() statement basically reads a JavaScript file, executes it, and then proceeds to return the export object. One of the major differences between require()
and import()
is that require() can be called from anywhere inside the program whereas import() cannot be called conditionally, it always runs at the beginning of the file. Create an express server app
and create port number variable which takes value from .env file if exists or set value as 3000. Now, the server can be started by using npm start
and Server is listening on port 3000
will be printed in console.
Enabling CORS
app.use(cors());
app.use(cors())
- This enables CORS (cross-origin resource sharing), In order for our server to be accessible by other origins (domains).
Calling use(cors())
will enable the express server to respond to preflight requests.
A preflight request
is basically an OPTION
request sent by the browser to the server before the actual request is sent, in order to ask which origin and which request options the server accepts.
So CORS
are basically a set of headers sent by the server to the browser. Calling cors() with no additional information will set the default
: our server will be accessible to any domain
that requests a resource from our server via a browser.
Create a simple test API Endpoint
app.get('/', (req, res) => {
res.json({"test":"hello instafood lovers !!! "});
}
The above code will create an API endpoint /
, for which the request can be sent. For eg: To hit the above API, request from browser looks something like http://localhost:3000/
. This the basic step for creating an API in node.js It takes two params req
which contains the request object from the browser and res
object which is used to send response to the browser.
To send response in json format , res.json()
is used. So, when http://localhost:3000/
is requested by browser, {"test":"hello instafood lovers !!! "}
is sent as response.
Create API Endpoints
app.get('/api/restaurants', (req, res) => {
const { lat, lng } = req.query;
console.log(req.query);
const url = `https://www.swiggy.com/dapi/restaurants/list/v5?lat=${lat}&lng=${lng}&page_type=DESKTOP_WEB_LISTING`;
fetch(url, {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'
}
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
res.json(data);
})
.catch(error => {
console.error(error);
res.status(500).send('An error occurred');
});
});
The above code will create an API endpoint /api/restaurants
, for which the request can be sent. For eg: To hit the above API, request from browser looks something like http://localhost:3000/api/restaurants
.
query - it is the query parameter sent in the URL request.
In the above code, the lat & lng value are taken for the query params and used while sending request to Swiggy API.
Now, API call is made to fetch data from swiggy API with API url, headers. When swiggy sends response, it is first checked if the response status is ok, if ok .json() is read the json data from the incoming stream. This json data is then sent it to browser using res.json() as mentioned above. If there is any internal server error, those will be catched in catch block.
Similarly, another API endpoint is created for /api/menu
Testing API Endpoints
Now that we have our Node.js server up and running, we can test it out by making a request to the /api/restaurants
endpoint with some latitude and longitude values as query parameters. In a web browser, go to http://localhost:3000/api/restaurants?lat=12.9351929&lng=77.62448069999999&page_type=DESKTOP_WEB_LISTING
, where lat and lng are the latitude and longitude values of the location you want to search for restaurants. You should see a JSON response with a list of restaurants in the specified location.
Requesting API from React app
Now that we have our server up and running, we can expose its API to a React app. In our React app, we can use the fetch API to make a GET request to http://localhost:3000/api/restaurants
or http://localhost:3000/api/menu
. This will retrieve the data from the Swiggy API via our Node.js server and allow you to use it in our React app.
Here's an example of how you can use the fetch API to make a request to our Node.js server from a React app:
- Using fetch :
fetch('http://localhost:3000/api/restaurants?lat=12.9351929&lng=77.62448069999999&page_type=DESKTOP_WEB_LISTING')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
- Using async/await :
const response = await fetch(`http://localhost:3000/api/restaurants?lat=12.9351929&lng=77.62448069999999&page_type=DESKTOP_WEB_LISTING`)
const data = await response.json();
Step 5: Deploy the server
Once you have tested our server locally, you can deploy it to a production environment.
To deploy our Node.js app on render.com, you can follow these steps:
Create an account on https://render.com/ if you haven't already.
Click on the
New +
button and selectWeb Service
from the dropdown menu.Connect
to the github repository (node server) which you want to deployIn the
Settings
tab, scroll down to theEnvironment Variables
section and addPORT
environment variables to3000
.Wait for the deployment to finish. Once it's done, you should see a success message and a link to our server url.
Click on the link url to test our server.
Note : Now that our server is deployed you can change the API url in react app to the domain in which the server is deployed. For example : if your server is hosted in http://instafood.onrender.com
, in react app while sending request to API, use this domain name :
const response = await fetch(`http://instafood.onrender.com/api/restaurants?lat=12.9351929&lng=77.62448069999999&page_type=DESKTOP_WEB_LISTING`)
const data = await response.json();
GitHub Code
The source code for this node server is available on Github Repo.
Conclusion
In this document, we have discussed the CORS issue that can arise when making requests from a client-side application to a server that is hosted on a different domain. Feel free to reach out to me, if you still have any doubts or need clarifications.
Subscribe to my newsletter
Read articles from Harshitha Solai Varadarajan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by