Handling Dev- vs Production API Calls in create-react-app
This post documents a brute-force approach I took to alternating between development and production databases in the PLURview app.
I built a simple apiAdapter.js
file in my src/
directory, which sets an API_URL
variable for my fetch()
calls. PLURview is based on create-react-app, which has the ability to distinguish between dev and production through process.env
variables.
The Problem
There were about 10 different calls to PLURview's internal API spread throughout the front-end codebase. The API URLs were hard-coded, which required a project-wide search-and-replace for every commit (local vs production).
The other problem was complexity of information when it came to researching how to accomplish this. The app is hosted on Heroku and it automatically updates from Github.
I looked into using Heroku's environment variables, which I had found useful in the past for storing API keys. All of my calls to external APIs go through the back-end, which is a private repo.
Ultimately a proper DevOps style solution appeared to be too heavy a solution for the amount of time I had to research and implement a solution.
The Solution
First, I modified my NPM scripts to set the dev environment variable:
"scripts": {
"start": "REACT_APP_STAGE=dev react-scripts start",
...
}
When you set a variable starting with REACT_APP
you'll have access to it in your app along the pattern of process.env.REACT_APP_STAGE
.
Next, in apiAdapter.js
I created a simple ternary based on the environment variable:
const API_URL = process.env.REACT_APP_STAGE === 'dev'
? 'http://localhost:3001/api/v1'
: 'https://plurview-api.herokuapp.com/api/v1';
Finally, I collected all those fetch
calls into apiAdapter.js
so I could interpolate the API_URL based on the environment:
export const fetchArtists = () => {
return fetch(`${API_URL}/artists`);
}
The adapter works the same way in the app as Redux actions. A component can import and use only the calls it needs:
import { fetchColorGuide } from '../../apiAdapter';
...
toggleDetails = () => {
fetchColorGuide()
.then(res => res.json());
Conclusion
This was a good intermediate step between ad hoc fetch()
calls and something more elegant. In the process I found I probably had more API calls going on than necessary, and some of my naming conventions could use better semantics.
The process of abstracting these calls also shined some light on components and endpoints I was no longer using, which helped me pare down the codebase a bit overall.