OpenAI's API is a powerful tool for building AI-powered applications. However, using the API directly in a client-side application, like a React app, can expose your API key to potential attackers. To ensure the security of your API key, it is recommended to route requests through a backend server, as outlined in OpenAI's Best Practices for API Key Safety.
If you don't have a backend server or prefer not to set one up just to handle requests to the OpenAI API, you can use Gateweaver. This API Proxy allows you to route requests through a secure server without writing any backend code.
In this tutorial, we'll show you how to use OpenAI's API in a React app without exposing your API key by using Gateweaver to proxy requests to the API. We will build a simple React app where users can ask questions and receive answers, utilizing the OpenAI Node.js library client-side while securely routing API requests through Gateweaver to protect your API key.
The complete source code for this tutorial is available on GitHub.
Prerequisites
Before you begin, make sure you have the following:
- An OpenAI API key. You can get one by signing up for an account on the OpenAI website.
- You have purchased credits on OpenAI to use the API.
- Node.js installed on your machine.
- Basic knowledge of React and JavaScript.
Set up the React App
Create a new React app
First, let's create a new react app using vite. We'll be using vite's typescript template for this tutorial.
npm create vite@latest openai-react -- --template react-ts
Start the React development server
Next, navigate to the newly created openai-react
folder and start the development server:
cd openai-react
npm install
npm run 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 Proxy.
npm install -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 folder called proxy
in our project where we will store our Gateweaver configuration files.
mkdir proxy
Inside the proxy
folder, create a new file called gateweaver.yml
. This file will contain the configuration for our API Proxy.
policyDefinitions:
cors:
origin: "${CLIENT_URL}"
endpoints:
- path: "/openai"
target:
url: "https://api.openai.com/v1"
request:
headers:
Content-Type: "application/json"
Authorization: "Bearer ${OPENAI_API_KEY}"
policies:
- cors
In the gateweaver.yml
file, we define a single endpoint called /openai
that proxies requests to the OpenAI API. We set the Authorization
header to include our OpenAI API key, which is stored in an environment variable called OPEN_AI_API_KEY
, which we will define in the next step. We also include a CORS policy to allow requests from our React app.
Set up server environment variables
To define the environment variables referenced in the gateweaver.yml
file, create a .env.gateweaver
file in the proxy
folder. Make sure to add this file to your .gitignore
file to avoid committing sensitive data to your repository.
OPENAI_API_KEY=YOUR_OPENAI_API_KEY
CLIENT_URL=http://localhost:5173
In this file replace YOUR_OPENAI_API_KEY
with your actual OpenAI API key, and set the CLIENT_URL
to http://localhost:5173
to match the URL of your React app running locally.
Start the Gateweaver 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": {
"proxy": "cd proxy && gateweaver start -w"
}
}
This script changes the directory to the proxy
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 in a separate terminal window from the one running the React app:
npm run proxy
The Gateweaver server should now be running on http://localhost:8080, and be ready to proxy requests to the OpenAI API.
Use OpenAI API in React
Now that we have Gateweaver set up to proxy requests to the OpenAI API, let's update our React app to use the API through the proxy.
Set up client environment variables
First, let's add the API Proxy URL to our React app as an environment variable so we can use it later in our code. To do this in a Vite project, create a .env.local
file in the root of your project and add the following line:
VITE_PROXY_URL=http://localhost:8080
Install the OpenAI Node.js client library
Next, let's install the OpenAI Node.js client library in our React app to interact with the OpenAI API.
npm install openai
Update the React app
Now, let's update the App.tsx
file to make requests to the API Proxy. We will add a simple form where users can input a question, and when they submit the form, we will send the question to the OpenAI API through the API Proxy and display the response on the page.
Here's the updated App.tsx
file:
import { useState } from "react";
import OpenAI from "openai";
import "./App.css";
const PROXY_URL = import.meta.env.VITE_PROXY_URL;
const openai = new OpenAI({
baseURL: `${PROXY_URL}/openai`,
apiKey: "",
dangerouslyAllowBrowser: true,
});
const generateMessage = async (userInput: string): Promise<string | null> => {
const completion = await openai.chat.completions.create({
model: "gpt-3.5-turbo",
messages: [
{
role: "system",
content:
"You are a helpful assistant that is here to answer questions.",
},
{
role: "user",
content: userInput,
},
],
});
return completion.choices[0].message.content;
};
const App = () => {
const [userInput, setUserInput] = useState("");
const [message, setMessage] = useState("");
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
setError("");
try {
const response = await generateMessage(userInput);
if (!response) {
throw new Error("Failed to generate message.");
}
setMessage(response);
setUserInput("");
} catch (error) {
console.error(error);
setError("Something went wrong. Please try again.");
}
setLoading(false);
};
return (
<div>
<h1>Ask me a question</h1>
<div className="content">
{message ? (
<>
<p>{message}</p>
<button className="reset-button" onClick={() => setMessage("")}>
Ask another question
</button>
</>
) : (
<form className="question-form" onSubmit={handleSubmit}>
<input
className="input"
value={userInput}
onChange={(e) => setUserInput(e.target.value)}
/>
<button className="submit-button" disabled={!userInput || loading}>
{loading ? "Loading..." : "Ask"}
</button>
{error && <p className="error-message">{error}</p>}
</form>
)}
</div>
</div>
);
};
export default App;
In this updated App.tsx
file, we define a function called generateMessage
that sends a user's input to the OpenAI API through the API Proxy and returns the response. We then use this function in the handleSubmit
function to generate a response to the user's question and display it on the page.
Note that we set the baseURL
property of the OpenAI
client to the URL of the API Proxy. We also set the apiKey
property to an empty string, as we are not using the API key in the front end. Instead, we are sending requests through the API Proxy, which includes the API key in the request headers. We also set dangerouslyAllowBrowser
to true
to allow the client to be used in the browser, but this is okay since we are using the API Proxy to secure the API key.
Next, let's update the App.css
file to add some basic styling to our React app:
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
.content {
display: flex;
flex-direction: column;
align-items: center;
}
.question-form {
display: flex;
flex-direction: column;
align-items: center;
max-width: 500px;
width: 100%;
}
.input {
width: 100%;
}
.submit-button {
margin-top: 1rem;
background-color: #2e8555;
color: white;
opacity: 0.5;
pointer-events: none;
}
.submit-button:enabled {
opacity: 1;
pointer-events: auto;
}
.reset-button {
background-color: #2e8555;
color: white;
}
.error-message {
color: red;
}
Here is what the React app should look like now:
Further Improvements
Even though the API key is no longer exposed, attackers could still abuse the API by sending a large number of requests through the proxy server. To mitigate this risk, you can add rate limiting to the proxy server to prevent abuse. To configure rate limiting in Gateweaver, refer to the Rate Limiting Documentation.
Another way to further secure your application is by adding JWT validation to the proxy server, ensuring that only authenticated users can access the API. To configure this, refer to the JWT Documentation. You can also explore some example configurations of popular providers like Auth0, Firebase, and Supabase for guidance.
Conclusion
In this tutorial, we learned how to use OpenAI's API in a React app without exposing the API key by using Gateweaver to proxy requests to the API. We set up a simple API Proxy and configured it to forward requests to the OpenAI API, with the API key included in the request headers. We then built a React app that allows users to ask questions and receive answers from the OpenAI API securely through the API Proxy.
To find out how to deploy Gateweaver to production and learn more about its features, check out the Gateweaver Documentation. If you found this tutorial helpful, please consider giving the Gateweaver repo a star!