Server-Side API Consumption
Workplace Context
Your front-end team needs data from an external API, but there’s a problem. The API requires a secret key, which cannot be exposed in the browser. Furthermore, the API’s data structure is overly complex, and it sometimes returns data you don’t want to show to users. Your task is to build a “proxy” or “facade” endpoint on your Express server. This endpoint will securely handle the API key, fetch the data, clean it up, and then forward a simplified version to your front-end application.
Learning Objectives
By the end of this lesson, you will be able to:
- Explain the strategic advantages of server-side API consumption (e.g., security, data transformation, caching).
- Integrate
fetch
within an Express route to create a proxy for an external API. - Implement robust error handling that inspects the external API’s response and translates it into a meaningful status code for your own server’s response.
- Transform and sanitize data from an external API before sending it to the client.
Why Fetch on the Server?
While fetch
can be used in the browser, performing the request on the server offers several powerful advantages that are critical for professional applications:
- Security: The most important reason. You can securely store and use API keys, tokens, and other credentials. If you made the request from the browser, these secrets would be visible to anyone, creating a massive security risk.
- CORS Prevention: Some APIs are not configured to allow cross-origin requests from browsers (
CORS
). By making the request from your server (which doesn’t have CORS restrictions), you can bypass this issue entirely. - Data Transformation and Sanitization: You have full control to reshape the data before it reaches the client. You can remove unnecessary fields to reduce payload size, combine data from multiple sources, or remove sensitive information.
- Caching: You can implement a caching layer on your server. If you frequently request the same data, you can store it temporarily and serve it from your cache instead of hitting the external API every time, improving performance and respecting rate limits.
Building a Data Transformation Proxy
Let’s build a route that acts as a proxy to fetch user data from JSONPlaceholder. However, we won’t just pass the data through. We will transform it, sending back only the id
, name
, username
, and email
for each user.
This pattern is often called a Facade, because it provides a simpler, custom interface to a more complex underlying system.
const express = require('express');
const app = express();
const port = 3000;
app.get('/api/users', async (req, res) => {
try {
const apiResponse = await fetch('https://jsonplaceholder.typicode.com/users');
// It's crucial to check if the request was successful
if (!apiResponse.ok) {
// If not, we create an error to be caught by the catch block
throw new Error(`HTTP error! status: ${apiResponse.status}`);
}
const users = await apiResponse.json();
// Transform the data
const transformedUsers = users.map(user => ({
id: user.id,
name: user.name,
username: user.username,
email: user.email,
}));
res.json(transformedUsers);
} catch (error) {
console.error('Error fetching or transforming users:', error);
// Send a generic server error response
res.status(500).json({ message: 'Failed to fetch data from the external API.' });
}
});
app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`);
});
Advanced Error Handling: Matching Status Codes
A simple 500
error is okay, but we can do better. A professional server should provide meaningful feedback. If our server requests a resource that doesn’t exist on the external API (a 404), our server shouldn’t just crash with a generic 500 error. It should also respond with a 404 Not Found
, telling our own client what happened.
This involves inspecting the status
of the fetch
response.
const express = require('express');
const app = express();
const port = 3000;
app.get('/api/posts/:id', async (req, res) => {
const { id } = req.params;
const url = `https://jsonplaceholder.typicode.com/posts/${id}`;
try {
const apiResponse = await fetch(url);
// If the response status is 404, we send our own 404 response.
if (apiResponse.status === 404) {
return res.status(404).json({ message: `Post with id ${id} not found.` });
}
// Handle other non-successful responses that aren't 404
if (!apiResponse.ok) {
// This will catch 500s from the external API, etc.
throw new Error(`External API returned status ${apiResponse.status}`);
}
const post = await apiResponse.json();
res.json(post);
} catch (error) {
console.error('API request failed:', error.message);
// We can use a 502 Bad Gateway status to indicate that our server
// received an invalid response from the upstream server.
res.status(502).json({ message: 'Failed to get a valid response from the API.' });
}
});
app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`);
});
Activities
Activity 1: Build a Data Transformer
- Create an Express route
GET /api/comments
. - Fetch the list of all comments from
https://jsonplaceholder.typicode.com/comments
. - Instead of sending the full objects, transform the data into an array of strings, where each string is in the format
"<email> commented: <name>"
. - Implement
try...catch
for error handling.
Activity 2: Robust Error Proxy
- Create another route:
GET /api/users/:id
. - Fetch a single user from
https://jsonplaceholder.typicode.com/users/:id
. - If the external API responds with a
404
, your server should respond with a404
and a JSON object{ "error": "User not found" }
. - If the external API responds with any other error (e.g., 500), your server should respond with a
502 Bad Gateway
status and a message. - If the request is successful, send the user data back to the client.
Knowledge Check
What is a primary security reason for using fetch
on an Express server instead of in the browser?
- Select an answer to view feedback.
In a server-side fetch
call, what is the purpose of checking response.ok
?
- Select an answer to view feedback.
If an external API returns a 500 error, what is the most appropriate HTTP status for your server to send back to the client in a proxy scenario?
- Select an answer to view feedback.
Summary
In this lesson, you graduated from simply fetching data to strategically using your Express server as a secure and powerful API client. You learned the critical advantages of server-side requests, such as protecting API keys and transforming data. You can now build robust proxy and facade routes that handle external API errors gracefully, translating them into meaningful responses for your own clients. This is a key pattern used in professional microservice and monolithic architectures.