Dynamic Base URLs in Axios for Staging and Production
Managing different API environments is a common requirement in modern web development. This article demonstrates how to dynamically toggle between staging and production base URLs in the Axios HTTP client using build-time environment variables, custom Axios instances, and runtime dynamic switching.
Method 1: Environment Variables (Build-Time Switching)
The most standard approach is to use environment variables provided by your bundler or runtime (such as Node.js, Vite, or Next.js).
Create your environment files:
.env.staging
VITE_API_BASE_URL=https://staging-api.example.com/v1
.env.production
VITE_API_BASE_URL=https://api.example.com/v1
Then, initialize Axios using the appropriate environment variable:
import axios from 'axios';
// Vite example (use process.env for Node/Webpack/CRA)
const baseURL = import.meta.env.VITE_API_BASE_URL || 'https://staging-api.example.com/v1';
const apiClient = axios.create({
baseURL,
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
});
export default apiClient;Method 2: Hostname-Based Dynamic Switching
If you deploy a single build artifact across multiple environments,
you can determine the base URL at runtime by inspecting
window.location.hostname.
import axios from 'axios';
const getBaseURL = () => {
const hostname = window.location.hostname;
if (hostname === 'app.example.com') {
return 'https://api.example.com/v1'; // Production
}
if (hostname === 'staging.example.com') {
return 'https://staging-api.example.com/v1'; // Staging
}
return 'http://localhost:3000/v1'; // Local Development
};
const apiClient = axios.create({
baseURL: getBaseURL(),
timeout: 10000,
});
export default apiClient;Method 3: Runtime Switching via Axios Interceptors
To allow users, testers, or administrators to switch environments directly within the application UI (e.g., via a settings toggle or local storage), update the URL dynamically using an Axios request interceptor.
import axios from 'axios';
const ENVIRONMENTS = {
staging: 'https://staging-api.example.com/v1',
production: 'https://api.example.com/v1',
};
const apiClient = axios.create({
timeout: 10000,
});
// Interceptor dynamically sets the baseURL before every request
apiClient.interceptors.request.use((config) => {
const selectedEnv = localStorage.getItem('app_env') || 'production';
config.baseURL = ENVIRONMENTS[selectedEnv] || ENVIRONMENTS.production;
return config;
});
// Helper function to change the environment
export const setEnvironment = (envName) => {
if (ENVIRONMENTS[envName]) {
localStorage.setItem('app_env', envName);
}
};
export default apiClient;Summary of Best Practices
- Use Build-Time Configurations if your deployment pipeline generates separate staging and production releases.
- Use Hostname Detection if you deploy identical Docker containers or static builds to different domains.
- Use Request Interceptors if you need in-app switching for QA testing without rebuilding or redeploying the application.