APIs are essential in modern web development as they enable developers to integrate external data into their applications. However, when using APIs, developers must be cautious when handling API keys. These keys authenticate requests to an API, and if they fall into the wrong hands, they can be used to make unauthorized requests and potentially incur costs or expose sensitive data.
While some API keys, like those for Google Maps, can be safely used in client-side applications due to restrictions that limit their use to specific apps, many should never be exposed to the client and should be handled securely on the server side.
For applications without a dedicated server component, developers can use a proxy server or an API Gateway to securely make API calls while hiding API keys from the client. An API Gateway serves as a middleman between the client and the external API, allowing developers to handle the API key on the server side and proxy requests to the API.
In this article, we explore how to protect API keys in React projects using Gateweaver, an open-source API Gateway designed to help developers securely manage API keys and sensitive data.
The complete source code for this guide is available on GitHub.
Set up the React App
Create a new React app using Vite
First, let's create a new react app using vite. We'll be using vite's typescript template for this tutorial. We will also be using pnpm as the package manager, but you can use npm or yarn if you prefer.
pnpm create vite@latest react-app --template react-ts
Start the React development server
Next, navigate to the newly created react-app
folder and start the development server.
cd react-app
pnpm install
pnpm dev
You should now see the react app running on http://localhost:5173.
Set up Gateweaver
Install Gateweaver CLI
Next, let's install the Gateweaver CLI tool, which we will use to configure and run the API Gateway.
pnpm add -D @gateweaver/cli
We install the CLI tool as a dev dependency as we only need it during development. For production, you will most likely want to use the Gateweaver Docker image. You can find out more about deploying Gateweaver to production in the Deployment Documentation.
Configure Gateweaver
Next, let's create a gateway
folder in our project where we will store our Gateweaver configuration files.
mkdir gateway
Inside the gateway
folder, create a new file called gateweaver.yml
. This file will contain the configuration for our API Gateway.
policyDefinitions:
cors:
origin: "${CLIENT_URL}"
endpoints:
- path: "/example"
target:
url: "https://httpbin.org/bearer"
request:
headers:
Authorization: "Bearer ${API_KEY}"
policies:
- cors
In this configuration file, we define an endpoint /example
that proxies requests to https://httpbin.org/bearer. Httpbin is a simple HTTP request and response service that can be used for API testing. We use the httpbin /bearer
endpoint, which requires an Authorization
header with a Bearer
token. We use the ${API_KEY}
syntax to reference an environment variable that will contain our API key to authenticate requests to the httpbin API. We also define a CORS policy that allows requests from ${CLIENT_URL}
.
Add server environment variables
To define the environment variables referenced in the configuration file, we can create a .env.gateweaver
file in the gateway
folder. Make sure to add this file to your .gitignore
file to avoid committing sensitive data to your repository.
API_KEY=example-api-key
CLIENT_URL=http://localhost:5173
In this file, we define the API_KEY
environment variable with our example API key and the CLIENT_URL
environment variable with the URL of our react app.
Start the Gateweaver development server
Before we start the Gateweaver development server, let's add a script to the package.json
file to make it easier to run the server.
{
"scripts": {
"gateway": "cd gateway && gateweaver start -w"
}
}
This script changes the directory to the gateway
folder and runs the gateweaver start -w
command, which starts the Gateweaver development server in watch mode on port 8080.
Now, let's start the Gateweaver development server by running the following command:
pnpm run gateway
You should be able to test the /example
endpoint by making a request to http://localhost:8080/example. The request will be proxied to https://httpbin.org/bearer with the Authorization
header set to the API key defined in the .env.gateweaver
file. The response should be the following JSON:
{
"authenticated": true,
"token": "example-api-key"
}
Update the React app to use the API Gateway
Now that we have set up Gateweaver, let's update our React app to use the API Gateway to make requests to https://httpbin.org/bearer without exposing the API key to the client.
Add client environment variables
First, let's add the gateway URL as an environment variable in our react project. To do this in a Vite project, we can create a .env.local
file in the root of our project and add the following line:
VITE_GATEWAY_URL=http://localhost:8080
Make sure to also add this file to your .gitignore
file.
Update the React app
Next, let's update the App.tsx
file to make requests to the API Gateway. We will add a button that fetches data from the Gateway's /example
endpoint and display the response on the page.
import { useState } from "react";
import "./App.css";
interface GatewayResponse {
authenticated: boolean;
token: string;
}
function App() {
const [gatewayResponse, setGatewayResponse] =
useState<GatewayResponse | null>(null);
const handleClick = async () => {
if (gatewayResponse) {
setGatewayResponse(null);
return;
}
try {
const BASE_URL = import.meta.env.VITE_GATEWAY_URL;
const response = await fetch(`${BASE_URL}/example`);
if (!response.ok) {
console.error("Failed to fetch data");
return;
}
const data = await response.json();
setGatewayResponse(data);
} catch (error) {
console.error(error);
}
};
const buttonText = gatewayResponse ? "Clear" : "Fetch data";
return (
<>
<h1>React App</h1>
<div className="card">
<button onClick={handleClick}>{buttonText}</button>
{gatewayResponse && (
<>
<h4>Response from Gateway:</h4>
<pre>{JSON.stringify(gatewayResponse, null, 2)}</pre>
</>
)}
</div>
</>
);
}
export default App;
Now, when you click the button in your React app, it will make a request to the /example
endpoint on the API Gateway. This will proxy the request to https://httpbin.org/bearer using the API key defined in the .env.gateweaver
file. Then the response from the httpbin API will be displayed on the page:
Conclusion
In this tutorial, we learned how to protect API keys in React projects using Gateweaver. We set up a simple API Gateway and configured it to proxy requests to an external API. We then used the Gateway to make requests from our React app to the external API without exposing the API key to the client.
Gateweaver provides additional policies such as rate limiting and jwt validation that you can use to further secure your application. You can find out more about Gateweaver and its features in the documentation. There you can also find information on how to deploy Gateweaver to production.
If you have found this tutorial helpful, please consider giving the Gateweaver repo a star!
What's your thoughts?
Please Register or Login to your account to be able to submit your comment.