When you work with third-party services or external APIs (like weather services, payment gateways, or any other external data source), they often provide you with an API key or some form of authentication. This key is sensitive information—like a password—that allows access to the service.
If you’re fetching data directly from the client side (for instance, a React or Vue application running in the user's browser), you would need to expose this API key to make calls to the third-party service. However, if this key is visible in the client code, anyone who views your client-side code could potentially see and misuse it.
To protect this sensitive information, it’s best practice to create a server-side API layer. This means:
- You create a separate endpoint or API on your server that handles the requests.
- Your client-side code calls your server API instead of the third-party service directly.
- The server then uses the API key (securely stored) to fetch data from the third-party service and send the necessary data back to the client.
By doing this, you keep your API key hidden and secure on the server.
Let’s say you’re building a weather app, and you want to fetch weather data from a third-party service like OpenWeatherMap, which requires an API key.
-
Without API Layer (Insecure)
// In your client-side code (JavaScript running in the browser) fetch(`https://api.openweathermap.org/data/2.5/weather?q=London&appid=YOUR_API_KEY`) .then(response => response.json()) .then(data => console.log(data));
Here,
YOUR_API_KEY
would be visible to anyone who inspects your website’s JavaScript code. -
With API Layer (Secure)
On your server (Node.js/Express example):
// server.js (or an API route file) const express = require('express'); const fetch = require('node-fetch'); const app = express(); app.get('/api/weather', async (req, res) => { try { const response = await fetch(`https://api.openweathermap.org/data/2.5/weather?q=London&appid=${process.env.OPENWEATHERMAP_API_KEY}`); const data = await response.json(); res.json(data); } catch (error) { res.status(500).json({ error: 'Failed to fetch weather data' }); } }); app.listen(3000, () => console.log('Server running on port 3000'));
Then, in your client code:
// Now the client only talks to your server’s API without knowing the actual API key fetch('/api/weather') .then(response => response.json()) .then(data => console.log(data));
In this setup, the client fetches data from '/api/weather'
on your server, which then internally calls OpenWeatherMap using the secure API key stored in your environment variables (process.env.OPENWEATHERMAP_API_KEY
), keeping it safe from the client.
Imagine you have a database with sensitive information, and you want to avoid exposing direct database queries to your client-side app.
-
Without API Layer (Insecure)
Directly embedding sensitive query logic in your client-side code, such as:
fetch(`https://your-database-url?query=SELECT * FROM users`) .then(response => response.json()) .then(data => console.log(data));
This would be a huge security risk because someone could easily inspect and modify the query.
-
With API Layer (Secure)
On your server (e.g., Node.js/Express with MongoDB or MySQL):
app.get('/api/users', async (req, res) => { try { const users = await db.collection('users').find({}).toArray(); res.json(users); } catch (error) { res.status(500).json({ error: 'Failed to fetch users' }); } });
And on the client side, you call:
fetch('/api/users') .then(response => response.json()) .then(data => console.log(data));
Here, the client only interacts with '/api/users'
, and the sensitive database logic remains on the server.
- API keys and database queries should never be exposed to the client since they are sensitive and can be misused if accessed by malicious actors.
- An API layer on the server acts as a secure middleman, keeping your secrets safe and ensuring only the necessary data is shared with the client.