API keys are super important for your app to communicate securely with third-party services. But wow, keeping them safe—especially in JavaScript apps that run in the browser—is no small task. Here’s a little guide to help you out:
First thing first, DO NOT store API keys on the client side. Seriously! Client-side JS can be peeked at, tinkered with, and generally messed around by anyone who knows how to open a browser console.
Instead of embedding API keys directly in your code (eek!), use environment variables. Here’s how you can do it with Node.js:
Create a .env
File: Pop this file in your project's root directory.
touch .env
Add Your API Key:
API_KEY=your_api_key_here
Install dotenv
: This nifty package helps load the stuff from your .env
file.
npm install dotenv
Load Environment Variables in Code:
require('dotenv').config();
const express = require('express');
const app = express();
const apiKey = process.env.API_KEY;
app.get('/api/some-endpoint', (req, res) => {
const axios = require('axios');
axios.get('https://api.example.com/data', {
headers: {
'Authorization': `Bearer ${apiKey}`
}
})
.then(response => {
res.json(response.data);
})
.catch(error => {
res.status(500).send('Error fetching data');
});
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Let your server be the middleman. The client pings your server -> the server uses the API key -> third-party service gets the request. This keeps your API key hidden, tucked away on the server where it’s safe.
// client.js
fetch('http://localhost:3000/api/some-endpoint')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error fetching data:', error));
Your server is the gatekeeper. Here’s how to fortify it:
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
message: 'Too many requests, try again later'
});
app.use('/api/', limiter);
Avoid storing API keys in front-end .env
files. Use them for less sensitive stuff, like base URLs.
React Example:
Create a .env
file in your React project’s root.
REACT_APP_API_BASE_URL=https://yourproxyserver.com
Access it in your React component.
import React, { useEffect, useState } from 'react';
const App = () => {
const [data, setData] = useState(null);
useEffect(() => {
fetch(`${process.env.REACT_APP_API_BASE_URL}/api/some-endpoint`)
.then(response => response.json())
.then(data => setData(data))
.catch(error => console.error('Error fetching data:', error));
}, []);
return (
<div>
<h1>Data</h1>
{data ? <pre>{JSON.stringify(data, null, 2)}</pre> : 'Loading...'}
</div>
);
};
export default App;
For the high level secret-keeping, try services like AWS Secrets Manager, Google Cloud Secret Manager, or HashiCorp Vault.
AWS Secrets Manager Example:
npm install aws-sdk
const AWS = require('aws-sdk');
const express = require('express');
const app = express();
AWS.config.update({ region: 'us-west-2' });
const secretsManager = new AWS.SecretsManager();
app.get('/api/some-endpoint', async (req, res) => {
try {
const data = await secretsManager.getSecretValue({ SecretId: 'YourSecretId' }).promise();
const apiKey = JSON.parse(data.SecretString).API_KEY;
const axios = require('axios');
const response = await axios.get('https://api.example.com/data', {
headers: {
'Authorization': `Bearer ${apiKey}`
}
});
res.json(response.data);
} catch (error) {
console.error('Error fetching secret:', error);
res.status(500).send('Error fetching data');
}
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
By following these strategies, you can keep your API keys secure and ensure your application is safe from unauthorized access. Stay safe out there!