6. API Keys, Proxy Server Strategy, and Environment Variables
Follow along with code examples here!
Fork this repository to practice deploying!
Thus far, we have not been able to deploy a project that uses an API key without likely exposing that API key to the public. Now that we have the power to build a server, we can finally do just that!
Table of Contents:
Essential Questions
By the end of this lesson, you should be able to answer these questions:
What is an API key? Why is it dangerous to include one in client-side code?
What is the proxy server strategy? How does it keep an API key secure?
What is an environment variable? How do you access environment variables in a Node application?
What is a
.envfile and why must it be listed in.gitignore?What does the
dotenvpackage do and how do you use it?
Key Concepts
API Key - a secret code that verifies your identity as a developer using an API's limited resources. Do not share these!
Environment Variable β a hidden variable stored on the host's machine (your laptop or Render.com) and accessible in Node through the
process.envobject.envfile - a file to store hidden variables like API keys. Ignored by GitHub and uploaded to Render for deployment.dotenvmodule - an npm package for importing.envfilesCross-origin requests - HTTP requests made from one domain to another domain, protocol or port.
Request Proxy In Development β faking the origin of the request in a frontend development server to match the origin of the backend server
Setup
For this lecture, you'll need an API that requires an API key - a secret code that verifies your identity as a developer using an API's limited resources.
We'll use the New York Times API because the API is quite friendly to use. Make an account on the NYT Developers Page and follow the instructions to enable the "Top Stories API".
We'll use the endpoint below to access the top stories in the "Arts" section:
Once you have an API key, clone the practice repository linked above and do the following:
Install dependencies for the
frontend/Vite projectThen, inside of
frontend/fetch-helpers, paste your API key string in the provided variable:Then, go to the
server/, install dependencies and start it:
You should see the application below served at http://localhost:8080:

What is an API Key?
An API key is a unique, secret code used by API servers to identify and authenticate a client sending a request. It acts as a digital ID or passcode that allows the API provider to verify that the request for data or services is coming from an authorized source.
Since API keys are unique to each user, we want to be careful about exposing them to the public. There are two common places that we can accidentally do this:
In client-side code
In public GitHub repositories
Q: Why is it not a good idea to share your API key? What really could go wrong?
The API key is a way to verify your identity as a developer. Some APIs will charge you for each request that you make using your API key and if someone else gets a hold of your API key, they could steal your request resources.
API Keys in Client-Side Code
Client-side/frontend code is inherently insecure because we just give the code to the user to run on their browser! If we're not careful, we may accidentally give away sensitive data.
Open http://localhost:8080 in your browser and open the developer tools. Click on the Sources tab, choose Page sources, and select the src/fetch-helpers.js file. You should see your API key:

It will also be plain to see if you view the Networks tab in the developer tools. Refresh the page and look through the requests sent by the application.
Can you find the API key?

All requests sent by the client will appear in this Network tab. As long as our frontend application is sending the fetch request, and that request includes the API key, there will be no way to hide it from this Networks tab.
So, what do we do?
NEVER send requests with API keys from client-side (frontend) applications!
Q: Doesn't the dist/ version of the frontend hide the API key?
TLDR: No. Here's why:
Recall that we can use the Vite build command to compress and minify our code making it much more difficult to read.
Our code uses this dist/ version when we deploy to a production environment.
To test this out:
Temporarily update this code to use
distno matter what.Return to the
frontendapp and runnpm run build.Restart the server.
If you look at the code now in the Sources tab, it will be much harder to find the API key (though not impossible). However, it will still be visible in the Networks tab. There is no escaping this!
The Proxy Server Strategy
Server-side/backend code is inherently more secure since the client has no visibility into what the server is doing. A server can freely send requests with API keys and the client will not be able to see anything!
We can use this to our advantage by setting up our server as a middleman (a.k.a. a proxy or proxy server):
The client can send the server a simple request such as
GET /api/storieswithout the API keyThe server will send the request to the API with the API key
When the server gets the response, it will send the data along to the client!

In order to implement this, we need to build a server application that:
Has its own API endpoint that the client can use without the client needing to know the API key.
Securely stores the API key (we'll use environment variables for this)
Fetch In the Server-Side Controller
The server-side code to fetch from the API is almost exactly the same code as the getTopStories() function in the frontend/src/fetch-helpers.js file. In the server, we'll put that logic inside of a controller that runs for GET /api/stories requests:
Now, visit http://localhost:8080/api/stories to see the fetched data! Open the Network tab again and you'll see that the API key is now hidden!
Same Origin Requests from the Frontend

Now that our server has this GET /api/stories endpoint, our frontend doesn't need to fetch directly from the API anymore.
Let's update the frontend application to use our server as a proxy instead of directly accessing the NYT API:
To test this out you may need to restart the server. Then visit the server http://localhost:8080 to see the updated frontend. Check out the Networks tab. Can you see the API key anymore?
Why is the API url just /api/stories and not http://localhost:8080/api/stories?
When we send requests to servers that we don't control, we include the full URL (e.g. https://dog.ceo/api/breeds/image/random). These kinds of requests are cross-origin because the origin of the request and the destination are not the same.
In this case, the client-side code comes from the same origin as the server we are fetching from: http://localhost:8080. So, we can use a relative path instead and our browser assumes we are making a same-origin request.
Environment Variables, Dotenv, and gitignore
There is one thing we need to clean up first β if we publish this code on GitHub, it will include our API key!
The most common way to store sensitive server-side data like API keys is with a .env file ("dot E-N-V file").
.env files have a really simple format. They are just listed in key="value" pairs
To hide this file from our GitHub repository, we must add .env to our .gitignore file. Any other developer who wishes to work on this project must provide their own .env file with their own API keys.
To use the environment variables in our server code, we'll use the dotenv module from npm.
dotenv provides a dotenv.config() method which looks for .env files and loads them into a special object process.env object:
With our API_KEY variable moved to the .env file, we can modify our controller:
In the previous lesson, we learned about the process.env.NODE_ENV value that is set by hosting services like Render. This is the exact same object that we are adding our API key to!
Deploying with Environment Variables
.env files are so widely used that most server hosting services will provide a way to securely upload .env files. That way, your deployed server will have access to .env values without needing those values to be stored on GitHub.
For example, on Render, you can add environment variables when configuring your new web service.

Complete Code
Last updated