DEV Community

Darshan Vasani
Darshan Vasani Subscriber

Posted on

๐Ÿš€ Step-by-Step: Setup + GET Method in FastAPI

๐Ÿš€ Step-by-Step: Setup + GET Method in FastAPI


๐Ÿงช 1. Set up Virtual Environment (Windows/Linux/Mac)

# Install virtualenv (if not already installed)
pip install virtualenv

# Create virtual environment
virtualenv venv

# Activate virtual environment
# Windows
venv\Scripts\activate

# macOS/Linux
source venv/bin/activate
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“ฆ 2. Install FastAPI and Uvicorn

pip install fastapi uvicorn
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“ 3. Create Project Structure

fastapi_project/
โ”‚
โ”œโ”€โ”€ main.py
โ”œโ”€โ”€ requirements.txt
โ””โ”€โ”€ venv/
Enter fullscreen mode Exit fullscreen mode

Save dependencies for later:

pip freeze > requirements.txt
Enter fullscreen mode Exit fullscreen mode

๐Ÿง  4. Create main.py with GET Methods

โœ… Using Path Parameters, Predefined Values, and Query Parameters

from fastapi import FastAPI, Query
from typing import Optional
from enum import Enum

app = FastAPI()

# ๐Ÿงฉ Predefined Path Parameter using Enum
class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"

# โœ… Basic GET Method with Path Parameter
@app.get("/items/{item_id}")
def read_item(item_id: int):
    return {"item_id": item_id}

# โœ… Path Parameter with Predefined Values
@app.get("/models/{model_name}")
def get_model(model_name: ModelName):
    if model_name == ModelName.alexnet:
        return {"model_name": model_name, "message": "Deep Learning FTW!"}
    elif model_name == ModelName.lenet:
        return {"model_name": model_name, "message": "LeNet is classic!"}
    return {"model_name": model_name, "message": "ResNet Rocks!"}

# โœ… With Query Parameters (Optional + Defaults)
@app.get("/products/")
def get_products(skip: int = 0, limit: int = 10, category: Optional[str] = Query(None, min_length=3)):
    return {
        "message": "Fetching products",
        "skip": skip,
        "limit": limit,
        "category": category or "all"
    }
Enter fullscreen mode Exit fullscreen mode

โ–ถ๏ธ 5. Run the Server

uvicorn main:app --reload
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“˜ 6. Explore Interactive Docs (Swagger)

Open in browser:

http://127.0.0.1:8000/docs  # Swagger UI
http://127.0.0.1:8000/redoc # ReDoc
Enter fullscreen mode Exit fullscreen mode

๐Ÿ” Usage Examples

  • GET /items/42 โ†’ {"item_id": 42}
  • GET /models/alexnet โ†’ Returns custom message
  • GET /products/?skip=5&limit=2&category=shoes

๐Ÿงพ Summary Notes

Feature Example Description
Path Param /items/{id} URL-based dynamic value
Enum Param /models/{model_name} Restrict value to predefined set
Query Param /products/?skip=0&limit=10 Additional filters, optional/default values
Swagger UI /docs Interactive API documentation
Virtualenv venv\Scripts\activate / source venv Isolate project dependencies

Absolutely! Here's a well-structured and beginner-to-advanced note on FastAPI Routers including why routers are important, how validation works behind the scenes using Pydantic, and how all of it comes together.


โœ… 1. Why Use Routers in FastAPI?

Routers are used to modularize your FastAPI app โ€” just like how controllers work in other frameworks (like Express.js, Django, Laravel).

โœ… Benefits:

  • ๐Ÿ”— Separation of concerns: Separate logic by features (e.g., /products, /users)
  • ๐Ÿ“ฆ Scalability: Easier to manage larger codebases
  • โ™ป๏ธ Reusability: Routers can be reused and nested
  • ๐Ÿงช Testability: Each router can be tested independently
  • ๐Ÿงผ Clean Code: Keeps your main.py small and readable

๐Ÿงฉ 2. What Is a Router in FastAPI?

A router is an instance of APIRouter, where you can define endpoints just like @app.get() but independently.

๐Ÿ’ก Example:

# routers/products.py

from fastapi import APIRouter

router = APIRouter()

@router.get("/products")
def get_products():
    return {"msg": "List of products"}
Enter fullscreen mode Exit fullscreen mode

โš™๏ธ 3. How to Register a Router

You register routers in your main app like this:

# app/main.py

from fastapi import FastAPI
from app.routers import products

app = FastAPI()
app.include_router(products.router, prefix="/api/v1", tags=["Products"])
Enter fullscreen mode Exit fullscreen mode

๐Ÿง  This will expose the route: /api/v1/products


๐Ÿ” 4. Behind the Scenes: Validation Using Pydantic

FastAPI uses Pydantic to validate:

โœ… Path Parameters

โœ… Query Parameters
โœ… Request Body (JSON/Post Data)

Example:

from pydantic import BaseModel, Field

class Product(BaseModel):
    name: str = Field(..., min_length=3)
    price: float
Enter fullscreen mode Exit fullscreen mode

Behind the scenes:

  • When a request hits the endpoint, FastAPI automatically parses and validates the incoming JSON into a Product object.
  • If data is invalid, FastAPI auto-generates a 422 error response with all validation issues.
  • You never have to manually try/except invalid input. Pydantic does it for you.

๐Ÿ“ฆ 5. Using Pydantic with Routers

# routers/products.py

from fastapi import APIRouter
from app.models.product import Product

router = APIRouter()

@router.post("/products")
def create_product(product: Product):
    return {"product": product}
Enter fullscreen mode Exit fullscreen mode
  • ๐Ÿง  When someone sends a POST request with JSON body, itโ€™s automatically parsed and validated.
  • FastAPI converts it into a Python object (Product).
  • If fields are missing or invalid, it returns a structured error.

๐Ÿงพ 6. Validation Error Response Format (Auto-generated):

{
  "detail": [
    {
      "loc": ["body", "product", "name"],
      "msg": "field required",
      "type": "value_error.missing"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ” 7. FastAPI Dependency Injection with Routers (Advanced)

You can pass dependencies into routers for:

  • Auth middleware
  • DB connection injection
  • Role-based access
router = APIRouter(
    prefix="/products",
    tags=["Products"],
    dependencies=[Depends(auth_dependency)],
)
Enter fullscreen mode Exit fullscreen mode

๐Ÿง  Summary: Why Routers + Pydantic Matter

Concept Description
Routers Help organize routes modularly, cleanly
Pydantic Models Define data schemas + validation rules for request/response
Auto Validation FastAPI + Pydantic auto-validate data before your logic runs
Error Handling Validation errors are returned with proper HTTP status + reason
Docs Integration Models are reflected in Swagger Docs for each route automatically

๐Ÿ” FastAPI Path Parameters

๐Ÿ” Predefined Values (Enum)

๐Ÿ”Ž Query Parameters

Each section includes:

  • What it is
  • How it works
  • Code examples
  • Behavior behind the scenes (Pydantic + FastAPI)

๐Ÿ“Œ 1. Path Parameters in FastAPI

โœ… What are Path Parameters?

Path parameters are dynamic parts of the URL path that act as variables.

๐Ÿง  FastAPI treats them as required parameters and maps them using Python function parameters.

๐Ÿ“˜ Syntax Example:

from fastapi import FastAPI

app = FastAPI()

@app.get("/users/{user_id}")
def read_user(user_id: int):
    return {"user_id": user_id}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ›  How it Works:

  • URL: /users/42
  • FastAPI converts "42" to an int (based on type hint)
  • If type mismatch (e.g., /users/abc), FastAPI returns a 422 error automatically.

โš™๏ธ Behind the Scenes:

FastAPI uses:

  • Python type hints to enforce type
  • Pydantic to validate the value before function execution
  • Generates OpenAPI docs automatically with correct parameter types

โœ… Optional Parameters?

No. Path parameters must be required.
If optional, you must redesign using query parameters instead.


๐ŸŽฏ 2. Predefined Values with Enums (Validation)

โœ… What are Predefined Values?

Sometimes, you want to restrict a path parameter to specific allowed values โ€” not anything.

Use Python Enums for this.


๐Ÿงฑ Example:

from enum import Enum
from fastapi import FastAPI

app = FastAPI()

class ModelName(str, Enum):
    alexnet = "alexnet"
    resnet = "resnet"
    lenet = "lenet"

@app.get("/models/{model_name}")
def get_model(model_name: ModelName):
    return {"model_name": model_name}
Enter fullscreen mode Exit fullscreen mode

๐ŸŒ Usage:

  • /models/alexnet โœ…
  • /models/invalid_model โŒ โ†’ 422 Validation Error

๐Ÿ” Why Use Enum?

  • Ensures clients use only valid values
  • Auto-validates input (via Pydantic)
  • Automatically shows options in Swagger Docs dropdown
  • Prevents typos and unexpected inputs

โš™๏ธ How it Works:

  • FastAPI internally maps the path to the Enum.
  • If not part of the Enum, it returns 422 with a message like:
{
  "detail": [
    {
      "loc": ["path", "model_name"],
      "msg": "value is not a valid enumeration member",
      "type": "type_error.enum"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“ฅ 3. Query Parameters in FastAPI

โœ… What are Query Parameters?

Parameters that appear after the ? in a URL.
Used for filtering, sorting, pagination, searching, etc.

๐Ÿ“˜ Example:

@app.get("/products")
def get_products(skip: int = 0, limit: int = 10, q: str = None):
    return {"skip": skip, "limit": limit, "q": q}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“Œ URL: /products?skip=5&limit=20&q=shoes

๐Ÿ’ก Key Points:

  • Not positional like path parameters
  • Can be optional or have default values
  • Auto-validated using type hints

๐Ÿงฐ Advanced: Use Query for extra validation

from fastapi import Query

@app.get("/search")
def search_items(q: str = Query(..., min_length=3, max_length=50)):
    return {"query": q}
Enter fullscreen mode Exit fullscreen mode
  • ... = required
  • min_length, max_length, regex supported
  • Shows up in API docs with proper validation

๐Ÿคฏ Pydantic + Query Magic:

Pydantic validates:

  • Required/Optional values
  • Data types (e.g., int, bool, float, etc.)
  • Value constraints (min/max length, regex)

And FastAPI auto-generates:

  • Swagger documentation
  • Interactive query parameter fields
  • Error responses for invalid queries

๐Ÿ“ฅ Optional Query Parameters

@app.get("/filter")
def filter_items(category: str = Query(None), in_stock: bool = Query(False)):
    return {"category": category, "in_stock": in_stock}
Enter fullscreen mode Exit fullscreen mode

๐Ÿง  Summary Table

Feature Path Parameters Predefined (Enum) Query Parameters
URL Format /items/{id} /models/{model_name} /products?skip=0&limit=10
Required โœ… Always โœ… Always โŒ Can be optional
Type Validation โœ… (int, str, etc.) โœ… Only allowed values via Enum โœ… With type hints & Query()
Auto Swagger Docs โœ… โœ… Dropdown with values โœ… Shows default and constraints
Common Use Case Dynamic routes (ID, slug) Model names, categories, user roles Filtering, searching, pagination
Validation Method Pydantic via type hints Pydantic Enum Pydantic + fastapi.Query

โœ… Default Values

โ“ Optional Parameters

๐Ÿ“Œ Using Both Together in Two Ways


๐Ÿ” 1. Using Plain Function Parameters

FastAPI automatically treats query parameters with default values as optional.

๐Ÿงช Example:

@app.get("/items/")
def read_items(q: str = None, page: int = 1, limit: int = 10):
    return {"q": q, "page": page, "limit": limit}
Enter fullscreen mode Exit fullscreen mode
Parameter Type Required? Default
q str โŒ Optional None
page int โŒ Optional 1
limit int โŒ Optional 10

๐Ÿง  Explanation:

  • No Query() is used.
  • All parameters have defaults, so FastAPI treats them as optional.
  • Validation is still applied via Python types.

๐Ÿง  2. Using Query() for More Control

Use fastapi.Query() when you want:

  • To explicitly make a param optional or required
  • Add default value
  • Add validation (min, max, regex)

โœ… Optional with Default (Best Practice):

from fastapi import Query

@app.get("/search")
def search_products(
    q: str = Query(None, min_length=2, description="Search term"),
    page: int = Query(1, ge=1, description="Page number"),
    limit: int = Query(10, le=100, description="Max results per page")
):
    return {"q": q, "page": page, "limit": limit}
Enter fullscreen mode Exit fullscreen mode
Parameter Required? Default Extras
q โŒ None min_length=2
page โŒ 1 ge=1
limit โŒ 10 le=100

โ— Required Query Parameters

Use ... in Query(...) to make it required.

@app.get("/required")
def must_pass(q: str = Query(..., min_length=3)):
    return {"q": q}
Enter fullscreen mode Exit fullscreen mode

โ›” /required โ†’ Error
โœ… /required?q=abc โ†’ OK


โœ… Optional with Pydantic Optional[] (Alternative)

from typing import Optional
from fastapi import Query

@app.get("/products")
def get_products(q: Optional[str] = Query(None)):
    return {"q": q}
Enter fullscreen mode Exit fullscreen mode

๐Ÿงพ Final Summary Table

Use Case Code Example Required? Default Notes
Basic optional param q: str = None โŒ None Simplest optional param
Required param q: str = Query(...) โœ… None Must be provided
Optional with default page: int = Query(1) โŒ 1 Custom default + validation
Optional with constraints q: str = Query(None, min_length=2) โŒ None Adds validation
Optional with Optional[] q: Optional[str] = Query(None) โŒ None More explicit for dev clarity

Top comments (0)