After selecting the tech stack for RaceGriot, the next step is to build a Minimum Viable Product (MVP) – a functional version of the race finder API that can:
- Serve race data from a static JSON file
- Support basic filtering for users to search races by location
- Be accessible locally and deployed on Render for public access
- Provide a solid foundation for future enhancements, including database integration and GraphQL support
This post walks through my step-by-step process of building, testing, and deploying the MVP.
As we proceed through this process we need to make sure our code is deployed to Github. My policy is to periodically push changes/bug fixes instead of waiting until the end.
1️⃣ Setting Up FastAPI
FastAPI makes it easy to build lightweight and high-performance APIs. So let’s start by installing FastAPI and Uvicorn, a server to handle the API requests:
pip install fastapi uvicorn
Then, we create a file named main.py and initialize the API:
from fastapi import FastAPI
import json
# Initialize FastAPI application
app = FastAPI()
# Root endpoint to confirm API is running
@app.get("/")
def read_root():
return {"message": "Welcome to RaceGriot! This API provides race data from around the world."}
We can run the server locally with the following command:
uvicorn main:app --reload
The FastAPI instance is now running at http://127.0.0.1:8000/. We can navigate to it in a browser or use curl to verify it’s working.
curl http://127.0.0.1:8000/
Expected Output:
{"message": "Welcome to RaceGriot! This API provides race data from around the world."}
2️⃣ Loading Race Data from JSON
Now that FastAPI is running locally, the next step is to add real race data. Instead of a database, we use a simple JSON file to store and retrieve race information.
We create a file named races.json and structure it as follows:
{
"races": [
{
"id": 1,
"name": "Boston Marathon",
"location": {
"city": "Boston",
"state": "MA",
"country": "USA",
"zip_code": "02116"
},
"date": "2025-04-21",
"type": "Marathon",
"website": "https://www.baa.org/",
"registration_link": "https://www.baa.org/races/boston-marathon/enter"
}
]
}
We need to load the race data from our JSON. Let’s modify main.py to do that
# Function to load race data from JSON file
def load_race_data():
with open("races.json", "r") as file:
return json.load(file)
# Store data in memory
data = load_race_data()
# API endpoint to retrieve all races
@app.get("/races")
def get_races():
return data["races"]
Now, when we visit http://127.0.0.1:8000/races, we see the race data returned in JSON format.
3️⃣ Adding Filtering by Location
With race data now loaded into our API, we want to allow users to filter races by city, state, or country. Let’s add filtering to make this possible.
We update main.py as follows:
@app.get("/races")
def get_races(city: str = None, state: str = None, country: str = None):
filtered_races = data["races"]
if city:
filtered_races = [race for race in filtered_races if race["location"]["city"].lower() == city.lower()]
if state:
filtered_races = [race for race in filtered_races if race["location"]["state"].lower() == state.lower()]
if country:
filtered_races = [race for race in filtered_races if race["location"]["country"].lower() == country.lower()]
return filtered_races
We can test the filtering with this command, which directs the API to show us all the races being held in Massachusetts:
curl "http://127.0.0.1:8000/races?state=MA"
We get the following result:
[
{
"id": 1,
"name": "Boston Marathon",
"location": {
"city": "Boston",
"state": "MA",
"country": "USA",
"zip_code": "02116"
},
"date": "2025-04-21",
"type": "Marathon",
"website": "https://www.baa.org/",
"registration_link": "https://www.baa.org/races/boston-marathon/enter"
}
]
We make sure all our changes are committed and pushed to GitHub before deployment.
git add . # Stage all changes
git commit -m "MVP implementation with FastAPI and JSON data"
git push origin main # Push changes to the main branch
4️⃣ Deploying the API to Render
Now that we’ve verified that our MVP works locally, we make it public by deploying it on Render. For our MVP, we can use the free tier of Render to get started. Once that’s done, we can proceed with the following steps:
- Step 1: Create requirements.txt
and to specify what dependencies Render needs to install.
fastapi
uvicorn
- Step 2: Set Up the Render Web Service
- Log in to Render and navigate to Web Services.
- Click New Web Service and connect to the GitHub repository.
- Select the repository and configure the following:
- Runtime: Python
- Start Command:
uvicorn main:app --host 0.0.0.0 --port $PORT
- Click Deploy and wait for the process to complete.
Our API is now live on Render!
5️⃣ Testing the Deployed API
Once deployed, we can test our API by using, the command below. It’s similar to the one we ran locally, except that we’re now using the public URL for our API.
curl "https://racegriot.onrender.com/races?state=MA"
The expected response is the same JSON output as in our local test.
Conclusion
That’s it! Our MVP is now complete, RaceGriot now:
- ✅ Serves race data from a JSON file
- ✅ Supports location-based filtering
- ✅ Runs locally and is deployed on Render
With this foundation in place, the next step is enhancing the API with GraphQL support using Apollo! Stay tuned🚀.