One of the most common challenges in front-end development is managing environment-specific configurations. Unlike backend applications, where you can leverage environment variables easily, frontend applications run in a browser that doesn’t natively support environment variables. So often developers specify these configurations at build time and build the frontend app separately for each environment.
While this approach initially seems straightforward, it introduces a host of issues.
Separate Builds for Each Environment: You can't promote a single build across different environments (development, staging, production). You have to build the application separately for each one. It’s time-consuming and inefficient.
Deployment Risks: Managing multiple builds heightens the risk of human error. A single mistake, such as deploying a staging build to production, can lead to outages, customers losing trust, or security vulnerabilities.
What can you do to avoid this?
Instead of embedding environment variables into your frontend application at build time, you can configure your application to load these variables dynamically at runtime. This will make your deployment process more efficient and less error-prone.
Here’s how to implement dynamic configuration at runtime:
1. Use Relative Paths
Make sure your frontend knows how to reach the backend API using relative paths.
Ensure your frontend application can interact with your backend API using relative paths. This can be a subdomain or a different path. This means the frontend does not need to know the full URL of the backend API; instead, it can rely on relative URLs determined by the server configuration.
Example: If your frontend is served from https://www.example.com and your backend API is at https://api.example.com, you can configure your frontend to make requests to relative paths like /api/endpoint instead of hardcoding the full API URL. This way, the frontend automatically adjusts to the correct backend URL based on the current environment.
2. Backend API for Configuration
Set up an endpoint in your backend that provides the necessary environment variables. For example, an endpoint like `/api/config` can return the variables your frontend needs by scanning all environment variables and filtering those starting with `FE_`.
Steps for Implementation
Frontend (React):
javascript
// src/config.js
async function loadConfig() {
const response = await fetch('/api/config');
const config = await response.json();
window.appConfig = config;
}// Call loadConfig at the startup of your application
loadConfig().then(() => {
// Initialize your application with the loaded config
// For example:
ReactDOM.render(<App />, document.getElementById('root'));
});
Backend(Node.js/Express Example):
```javascript
// server.js
const express = require('express');
const app = express();app.get('/api/config', (req, res) => {
const config = {};
for (const [key, value] of Object.entries(process.env)) {
if (key.startsWith('FE_')) {
config[key] = value;
}
}
res.json(config);
});const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
```
By loading environment configurations at runtime, you can use the same build across all environments, simplifying the deployment process and reducing errors. This approach ensures that your frontend application always has the correct settings for its environment.
You can follow me on Twitter (X) as I continue to write more on K8s, DevOps, and Platform Engineering and document my startup journey.
This is really helpful, I have seen a lot of mistakes happening in production or staging due to env variable mixup.