xavier collantes

FastAPI: Build your own APIs

By Xavier Collantes

Created: 5/30/2025; Updated: 5/30/2025


FastAPI Logo

FastAPI: Python Web Framework for Heavy Duty APIs

FastAPI isn't just fast by name, it's legitimately one of the fastest Python frameworks out there, rivaling Node.js and Go in performance. But speed isn't even the best part.
My favorite features:
  • Automatic API documentation (for when the frontend dev asks can you make me some documentation on your API?)
  • Error checking for API argument inputs (no need to handle errors like 405 Method Not Allowed and many others)
  • Type hint support improves code quality and reduces runtime errors
  • Built-in support for async/await ensures scalability
Image error meme

Getting Started

Let's build something to see FastAPI in action. How about a random cat fact API?

Set up your environment.

Bash
1python3 -m venv env
2source env/bin/activate
3pip install fastapi uvicorn
4
Set up your environment. hosted withby Xavier
Now let's build our cat fact API service:
🐍
Python3
1"""Backend API service that returns a random cat fact."""
2
3from fastapi import FastAPI, Request
4from pydantic import BaseModel
5import random
6import uvicorn
7
8app: FastAPI = FastAPI(title="Cat Facts API", version="1.0.0")
9
10cat_facts: dict[str] = [
11    "Cats sleep 70% of their lives.",
12    "A group of cats is called a clowder.",
13    "Cats have five toes on their front paws, but only four toes on their back paws.",
14    "The world's largest cat measured 48.5 inches long.",
15    "Cats have whiskers on the backs of their front legs as well.",
16    "A house cat can run to the speed of about 30 mph over short distances.",
17    "Cats can make over 100 different sounds.",
18    "The first cat in space was a French cat named Felicette (a.k.a. 'Astrocat') in 1963.",
19]
20
21class CatFact(BaseModel):
22    """Data type for holding fact and ID."""
23
24    cat_fact: str
25    fact_id: int
26
27
28@app.get("/", response_model=CatFact)
29async def get_random_cat_fact():
30    """Get a random cat fact because the internet needs more cats."""
31
32    # Randomly grab a fact on each call.
33    fact: str = random.choice(cat_facts)
34
35    # Fact ID is random number as well.
36    #
37    # Output:
38    # {
39    #   "cat_fact": "Cats sleep 70% of their lives.",
40    #   "fact_id": 542
41    # }
42    return CatFact(cat_fact=fact, fact_id=random.randint(1, 1000))
43
44
45if __name__ == "__main__":
46    # `host` Running on any local IP address or `localhost` aka `127.0.0.1`.
47    # `port` Some port not being used by another program on your computer.
48    # `reload` Development only. When you make changes to the code, the server
49    #       will reload with new  changes instead of shutting down this Python
50    #       code then starting back up again.
51    uvicorn.run(app, host="0.0.0.0", port=8000, reload=True)
52
snippet hosted withby Xavier
Run it and watch the magic happen:
Bash
1python main.py
2
snippet hosted withby Xavier
Next, open your browser and navigate to http://localhost:8000.
You should see a simple string in the browser with a cat fact.
Now for the bonus... in as a backend engineer (which you are now given your backend cat server) the frontend team may ask Can I please have documentation so I can see what the API endpoint is called and what arguments is takes?
Navigate to http://localhost:8000/docs and BAM! You've got interactive API documentation.

Real-World Patterns That Actually Matter

Let's graduate from cat facts to something more enterprise-y. Here's how you handle different HTTP methods like a pro:
🐍
Python3
1from pydantic import BaseModel
2from fastapi import FastAPI, HTTPException, Query
3
4app = FastAPI()
5
6class UserRequest(BaseModel):
7    name: str
8    email: str
9    age: int
10
11# GET with query parameters.
12@app.get("/users")
13async def get_users(name: str = None, age: int = Query(None, ge=0, le=120)):
14    """Get users with optional filtering."""
15    # Your database magic here.
16    return {"message": f"Finding users named {name}, age {age}"}
17
18# POST with request body.
19@app.post("/users")
20async def create_user(user: UserRequest):
21    """Create a new user."""
22    # Pydantic automatically validates the request body.
23    if user.age < 0:
24        raise HTTPException(status_code=400, detail="Age cannot be negative")
25
26    return {"message": f"Created user {user.name} with email {user.email}"}
27
snippet hosted withby Xavier

File Uploads

Need to handle file uploads?
🐍
Python3
1from fastapi import FastAPI, File, UploadFile
2import boto3
3from botocore.exceptions import ClientError
4
5app = FastAPI()
6
7@app.post("/upload")
8async def upload_file(file: UploadFile = File(...)):
9    """Upload files to S3 or Supabase (both use boto3)."""
10
11    # Setup your AWS S3 bucket storage.
12    s3_client = boto3.client(
13        "s3",
14        # Get from AWS, Supabase, Firestore, or whatever storage you choose.
15        endpoint_url="https://your-project.supabase.co/storage/v1/s3",
16        aws_access_key_id="your-key",
17        aws_secret_access_key="your-secret",
18    )
19
20    try:
21        s3_client.upload_fileobj(file.file, "your-bucket", file.filename)
22        return {"message": f"Successfully uploaded {file.filename}"}
23
24    except ClientError as e:
25        raise HTTPException(status_code=500, detail=f"Upload failed: {e}")
26
snippet hosted withby Xavier

Bridging Python (Backend) and JavaScript (Frontend)

Python programmers will usually use snake_case or camelCase where JavaScript programmers will usually use camelCase for everything... so if data is moving between a Python backend and JavaScript frontend... do we all now have to choose the same case? Not with FastAPI.
Image of frontend backend meme
Working with a JavaScript frontend? FastAPI plays nice with camelCase while keeping your Python snake_case:
🐍
Python3
1from pydantic import BaseModel, ConfigDict
2import datetime
3
4def to_camel_case(string: str) -> str:
5    """Convert snake_case to camelCase."""
6    components: str = string.split('_')
7    return components[0] + ''.join(word.capitalize() for word in components[1:])
8
9class UserResponse(BaseModel):
10
11    # Config for model to transform the outputs.
12    model_config = ConfigDict(
13        # Provide function.
14        alias_generator=to_camel_case,
15        # Accept both camelCase and snake_case.
16        populate_by_name=True,
17    )
18
19    # Python uses snake_case.
20    # JavaScript gets camelCase.
21    first_name: str
22    last_name: str
23    birth_date: datetime.date
24
25@app.get("/user", response_model=UserResponse)
26async def get_user():
27    return UserResponse(
28        first_name="Winston",
29        last_name="Wolf",
30        birth_date="1994-10-14"
31    )
32
33# Frontend receives:
34# {
35#   "firstName": "Winston",
36#   "lastName": "Wolf",
37#   "birthDate": "1994-10-14"
38# }
39
snippet hosted withby Xavier

Development Workflow Without Refreshing

Enable auto-reload during development and thank me later. This will avoid stopping and starting the Python command for every change:
🐍
Python3
1if __name__ == "__main__":
2    uvicorn.run(
3        "main:app",
4        host="0.0.0.0",
5        port=8000,
6        reload=True,  # Auto-restart on file changes.
7    )
8
snippet hosted withby Xavier
Or run it from the command line:
Bash
1uvicorn main:app --reload --port 8000
2
snippet hosted withby Xavier

Why Your Python Infrastructure Needs FastAPI

Building microservices? FastAPI makes service-to-service communication a breeze with automatic request/response validation. Building a monolith? The performance characteristics mean you can handle serious traffic without the complexity of async frameworks.
The real win is developer productivity. Type hints catch errors at development time, automatic docs mean your API is self-documenting, and the async support means you can integrate with modern Python tools like SQLAlchemy 2.0, httpx, and async Redis clients.

Further Reading

Image of servers meme

Related Articles

Related by topics:

python
infrastructure
apis
Python Async Architecture: Real-World Experience

Async Python patterns are essential for scalable APIs, background processing, and real-world production systems that handle thousands of concurrent operations.

By Xavier Collantes9/3/2025
python
async
apis
+3

HomeFeedback