The Problem
Recently I was working on a Frontend Mentor challenge, an app where you can input an IP address or url and fetch and display information about it.
Things were going well. I got the layout sorted, I set up a custom map using React Leaflet and Mapbox.
When it was time to fetch the data from the API, however, I ran into problems.
Frontend Mentor had suggested using the IP Geolocation API by IPify to get information based off an IP address or domain.
If you sign up for a free account with IPify and paste https://geo.ipify.org/api/v1?apiKey=YOUR_API_KEY
into your url bar, it'll send back JSON with information about your IP address, your location, and your ISP provider.
But if you try to make a HTTP request to this API from your client-side code, you'll get a message about CORS.
What is CORS?
CORS (Cross Origin Resource Sharing) is a mechanism that uses HTTP headers to give a browser permission to access resources from a server that's different from the server of the website making the request.
It exists for a very good reason: security. You don't want some random website to be able to ping your server and get sensitive data from it. To prevent this, browsers restrict cross-origin HTTP requests initiated from client-side scripts by default.
You need to specify which urls can access data from your server by making sure the correct CORS headers get sent with each request. But you can't just change the CORS settings on someone else's server because that would defeat the whole point of CORS as a security layer.
CORS Proxies
The solution that most resources recommended is to use a proxy, another server that will receive the request from your browser, pass it on to the server you're trying to access, and then pass that server's response back to you. This way the entire request response process occurs server-side and isn't subject to CORS.
There are a number of CORS proxies you can use, such as crossorigin.me or CORS Anywhere.
But I didn't want to rely on a third-party proxy. After all, they can see all the data that you pass to them and could stop working at any time. I also didn't want to run and manage my own Node app just to act as a proxy.
While doing some research, I came across a blog post by Jim Nielsen about setting up a CORS proxy with Netlify that promised to solve CORS issues using Netlify redirects without having to run your own proxy server or rely on some random one.
Redirects
Using redirect rules to proxy to an external service is something that Netlify actively supports. For example, you can set things up so that any requests to /api/search
will be redirected to https://api.example.com/search
. The Netlify docs explain how to do it.
You can set redirect rules in a _redirects
file:
/api/* https://api.example.com/:splat 200
Or you can include them in your netlify.toml
file which sets the configuration for your entire app on Netlify.
[[redirects]]
from = "/api/*"
to = "https://api.example.com/:splat"
status = 200
force = true
If you want to test this out locally before deploying it, Netlify Dev lets you run Netlify locally on your machine.
Armed with this information, I added the redirects rules to my netlify.toml
file, ran the netlify dev
command and it worked!
Relieved to be making progress, I deployed the app to Netlify... and ran into a new issue.
Everything worked locally but once the app was deployed I kept getting a 502 Bad Gateway response, meaning that the proxy server received an invalid response from the server it was proxying to.
Netlify have really great Support Forums and the good people there pointed me to potential solutions - setting specific headers on my redirects, checking for differences between my local environment and the build environment on Netlify. But none of this made a difference.
A Netlify engineer was able to confirm that the problem was that the API was not responding to the request within 30 seconds. For proxy rewrites, Netlify closes the connection in 30 seconds and since the request wasn't complete by then, it returns a 502. They suggested I move my code into a serverless function and see if that would resolve the issue.
Solution - Serverless Functions
Serverless functions let you write server-side code that can work like an API endpoint, run automatically in response to events, or process complex jobs in the background, all without managing your own server.
Netlify Functions make setting up and running serverless functions pretty straightforward.
You create a functions
directory in the root of your project and, inside that, a javascript file with the name of your function.
base-directory/
|- package.json
|- node_modules
|- functions/
hello.js
In the function's file, you export an async handler function which returns a status code and a body. These functions can carry out tasks such as fetching data from an API, sending automated emails, or validating user input, before sending back a response.
exports.handler = async (event, context) => {
return { statusCode: 200, body: JSON.stringify({ message: "Hello World" }) };
};
By default, to call a serverless function you make a request to .netlify/functions/your-serverless-function
. But you can also use redirects in your netlify.toml
to make this more concise.
[build]
functions = "functions"
[[redirects]]
from = "/api/*"
to = "/.netlify/functions/:splat"
status = 200
Now, requests to /api/hello
will be the same as .netlify/functions/hello
.
Customizing Requests
I moved the API request into a serverless function, ran Netlify Dev, and was immediately able to get back IP information.
But for this particular project, I needed to be able to vary the requests I sent to the API.
A request to https://geo.ipify.org/api/v1?apiKey=YOUR_API_KEY
will send back information about the IP address that sent the request.
On the other hand, a request to https://geo.ipify.org/api/v1?apiKey=YOUR_API_KEY&ipAddress=SOME_IP_ADDRESS
will return information about the specified IP address.
And finally, a request to https://geo.ipify.org/api/v1?apiKey=YOUR_API_KEY&domain=SOME_DOMAIN
will return information about that domain.
But how was I going to pass this data to the serverless function?
Well, thankfully, Ben Hong has a helpful tutorial on customizing the request with serverless functions.
Basically, I used fetch to make a POST request to the serverless function, passing the search input's value as the body of the request.
// App.js
const res = await fetch(`/api/getIpInfo`, {
method: "POST",
body: JSON.stringify({
searchTerm: ipAddress,
}),
});
const ipInfo = await res.json();
Then in the serverless function, I extracted the search term from the body, used validator.js to check if it was an IP address or domain, and appended it to the API endpoint.
// getIpInfo.js
const fetch = require("node-fetch");
const validator = require("validator");
exports.handler = async (event, context) => {
const eventBody = JSON.parse(event.body);
// Check for IP Address or domain
let extension = "";
if (eventBody.searchTerm) {
const { searchTerm } = eventBody;
if (validator.isIP(searchTerm)) {
extension = `&ipAddress=${searchTerm}`;
} else if (validator.isURL(searchTerm)) {
const domain = new URL(searchTerm);
extension = `&domain=${domain.hostname}`;
}
}
try {
const res = await fetch(`${API_ENDPOINT}${extension}`);
const data = await res.json();
return { statusCode: 200, body: JSON.stringify({ data }) };
} catch (error) {
console.log(error);
return {
statusCode: 500,
body: JSON.stringify({ error: "Failed fetching data" }),
};
}
};
Success! Thanks to serverless functions, my app can now make requests to the IP Geolocation API from the browser without running into any CORS issues.
Try It Yourself
If you are running into CORS issues when making HTTP requests from the browser, try proxying via redirects and, if that doesn't work for you, there's never been a better time to learn about serverless functions.
Ben Hong's Up and Running with Serverless Functions is a great place to get started.
The code for my project is available on GitHub. You can also try out the deployed app.
Further Resources
Ania Kubรณw, Create your first Netlify Serverless Function! - YouTube
Ben Hong, Up and Running with Serverless Functions - Jamstack Explorers
Cross-Origin Resource Sharing (CORS) - MDN Web Docs
Jim Nielsen, Setup a CORS Proxy With Netlify - jim-nielsen.com
Phil Hawksworth, Exploring Netlify Redirects - Jamstack Explorers
Top comments (2)
๐ก Calling all developers and tech wizards! Areon Network presents a golden opportunity with its Hackathon. Register at hackathon.areon.network and compete for a share of the incredible $500,000 prize pool. Code your way to success! ๐๐ป #CodingCompetition #AreonNetwork
Greatissime, thanks you very much