Circumventing CORS with Netlify Functions & Nodejs

Photo by BENCE BOROS on Unsplash

Recently I was working on a front end project, where I needed to consume a third-party API that hadn’t been properly configured with the appropriate CORS headers. Rather than track down the API author, I decided to take this as an opportunity to experiment with a serverless pattern. As someone who usually reaches for a library to assist with http requests, I figured it was also an opportunity to practice making those requests with just the nodejs standard libraries.

Why a custom proxy?

CORS limit websites from communicating with other domains without the full consent of both sites. Oftentimes it only makes sense for a a specific client to use a server, but other times a server can have an API that is useful for various sites. However, if the server is not set up properly, browsers will prevent other websites from using that API. A proxy server using serverless function can be set up so your Netlify site is consuming data from your Netlify Functions’ endpoints, not the cross-origin site. Problem solved.

Note: Circumventing CORS should be undertaken with care. But a proxy server still preserves a major protection of CORS, as discussed here.

Why serverless?

Serverless is a popular pattern that has been driven by the popularity of AWS Lambda. What’s the pattern? The idea is you don’t have your own server, not even a dyno like the case with Heroku. Rather, you just have a single function call that takes a http request and returns a response. Simple, clean. No need to worry about server down-time, or pinging your server to make sure it’s up. Also, scaling questions are handled by the provider of your serverless service, your functions are stateless and called by the surrounding infrastructure. Less state is easier to reason about.

Why Netlify Functions?

Netlify Functions is a user-friendly wrapper around AWS Lambda. So when you use it, you are getting a taste of AWS Lambda, an industry standard. You even will find yourself reading AWS Lambda docs at times, which is a great way to start getting familiar with an API: having a specific question to answer in the docs.

On the other hand, Netlify aims for a friendly experience. So a lot of the “overhead” of AWS is abstracted away. Everything just works with Netlify.

Netlify also provides the library, which lets you run your lambda functions locally, which makes them easy to test. Netlify is also rolling out the Netlify Dev platform, which will make this local testing environment even easier to interact with!

Zeit is another strong contender in the serverless space. I first explored their options but did not find the local testing options as robust. But Zeit moves fast and I expect that to no longer be true soon, if it isn’t already.

Why node?

Node is the ultimate async scripting language, so for a quick easy solution nothing beats it for me. But why basic node, why not a http request library? Because sometimes libraries abstract away so much that you can lose track of what exactly is happening under the hood. Sometimes you think there’s more magic in the library than there is, and you can become disempowered to solve problems. I wanted to counteract that trend by keeping things basic and light.

The approach

The API was a beer-rating app. It had an endpoint for creating a beer, updating the number of likes a beer had, and fetching a list of beers. I already had a Create-React-App based project structure, so to get started, I did . Then I created an api folder in my folder, and created a file (so ). This would mean my front-end could query to interact with the third-party API.

I then created a netlify.toml file in my root folder that ended up looking like this:

Everything above is unrelated to Netlify Functions, except the line, which is telling Netlify where to look for the endpoint files. You could also handle this on the Functions settings page for your Netlify site.

Finally, I went to the Functions tab on my Netlify account for my site and enabled Functions.

Node Code

First we import our libraries and set our baseURL (not the real url):

What’s going on here? I’m importing the https and the url node standard libraries. can be used to implement a full-fledged server, but in this case we're using it only to make http requests to the third-party API-similar to what would be used for in the browser. And is used to parse urls. For latest versions of node, url would not be needed to accomplish our goals, but for the version of node supported out of the box with Netlify, we need it to get to work for us.

That done, we set up our central endpoint, which handles routing for our request.

We are exporting a function that takes as arguments, , , and a . These are all provided by AWS, so you will want to check out their docs rather than Netlify's.

The argument has info about the server request, and the callback function is used to resolve the request. It takes two parameters, Error, and Response. So an error response can be triggered with , and a success can be triggered with .

The function uses regex to get any routing off the received url, and then uses the http method information and the route to determine the correct course of action. I’ll walk through the functions associated with the different actions.

GET Requests

The GET request is the simplest kind, so we handle it first. We use the standard event listener approach to making our http request. One thing to note, is that https.request takes a object as its argument, so we need to parse our endpoint using url.parse. We then spread it into an object so we can add headers as needed.

Preflight Requests: OPTIONS, PUT, and POST

I ran into trouble when trying to make PUT and POST requests, because these require a preflight request to be sent and responded to beforehand. That is why the main function above first checks to see if a request is using the OPTIONS method, and handles it if needed. This is an aspect of code that is frequently abstracted away, such as in the Express framework. So I found it instructive to have to handle this myself.

The preflight response is fairly straightforward. Just send the headers back that you will be sending on your eventual POST/PUT response.

After that, the POST and PUT requests are fairly similar, so I present them together:

And that’s essentially it! Notice, we are adding an ‘Access-Control-Allow-Origin’: ‘*’ header to our responses. In reality, this is not required, and could be gotten rid of in a production app. The whole point of using Netlify Functions is that this API will be served from our own app’s netlify domain, so CORS issues will be avoided! But including the headers does allow you to support other clients as well. Note that in many cases, you would want to specify allowable urls rather than using .

Consuming the endpoints

Since you’ve installed , you can now run to run your function. By default, it will run on localhost:9000. You can now make http requests against it using CURL, Postman, or vscode REST Client.

To access it in your front-end, you can use environmental variables, so that your front-end hits in local development, and in production.

Next steps

And with that, we’ve implemented a basic endpoint with routing and multiple method support. Some additional concepts you could explore would be setting up your netlify-lambda so it uses a proxy to support requests to in the local environment as well as production, to keep differences to a minimum.

You can also start exploring the Netlify Dev package, as this will only make this technology easier to implement and test.

Or you could run with the big dogs, and implement this directly using AWS!

Originally published at on April 19, 2019.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store