Skip to content

Usage

This section explains how to use FastOpenAPI to document and validate your API endpoints. Below you will find separate examples for each usage scenario, including handling of both library and framework exceptions.


Defining Endpoints

Available methods

  • @router.get(path, response_errors, response_model, tags, status_code)
  • @router.post(path, response_errors, response_model, tags, status_code)
  • @router.patch(path, response_errors, response_model, tags, status_code)
  • @router.put(path, response_errors, response_model, tags, status_code)
  • @router.delete(path, response_errors, response_model, tags, status_code)

Short descriptions

  • path - the method URL.
  • response_errors - the list of error codes for your method.
  • response_model - your Pydantic model. You can use int, float, str, bool as well.
  • tags - OpenAPI tags for your handler.
  • status_code - the success response code.

Examples for methods

from pydantic import BaseModel
from typing import Optional

# Don't use this router. It's just an example.
from fastopenapi.base_router import BaseRouter

class CreateUserRequest(BaseModel):
    name: str
    surname: str
    age: int

class UpdateUserRequest(BaseModel):
    name: Optional[str]
    surname: Optional[str]
    age: Optional[int]

class UserResponse(BaseModel):
    id: int
    name: str
    surname: str
    age: int

router = BaseRouter()

@router.get("/user/{user_id}", tags=["User"], status_code=200, response_model=UserResponse)
def get_user(user_id: int):
    # your getting logic here
    data = {}
    return UserResponse(**data)

@router.post("/user", tags=["User"], status_code=201, response_model=UserResponse)
def create_user(user_data: CreateUserRequest):
    # your creating logic here
    data = {}
    return UserResponse(**data)

@router.patch("/user/{user_id}", tags=["User"], status_code=200, response_model=UserResponse)
def update_user(user_id: int, user_data: UpdateUserRequest):
    # your updating logic here
    data = {}
    return UserResponse(**data)

@router.delete("/user/{user_id}", tags=["User"], status_code=204)
def delete_user(user_id: int):
    # your deleting logic here
    return None

Basic request (Listing Items)

@router.get("/items")
async def list_items():
    return {"items": ["foo", "bar"]}

Request with Path Parameter

@router.get("/items/{item_id}")
async def get_item(item_id: int):
    item = database.get(item_id)
    if item is None:
        # You can use framework-specific exception here 
        raise ResourceNotFoundError(f"Item {item_id} not found")
    return item

Handling Query Parameters

@router.get("/search")
async def search(q: str = "", limit: int = 10):
    if limit < 0:
        # You can use framework-specific exception here 
        raise BadRequestError("Limit must be non-negative")
    return {"query": q, "limit": limit}

Handling Request Bodies with Pydantic Models

from flask import abort
from pydantic import BaseModel


class Item(BaseModel):
    name: str
    price: float

@router.post("/items", response_model=Item, status_code=201)
def create_item(item: Item):
    try:
        save_to_database(item)
    except DatabaseError as e:
        abort(500, description="Database error")
    return item

Organizing Code with Sub-Routers

File: users.py

from fastopenapi.routers import StarletteRouter
from pydantic import BaseModel
from typing import List

user_router = StarletteRouter()

class User(BaseModel):
    id: int
    name: str

@user_router.get("/users", response_model=List[User])
async def list_users():
    return [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]

@user_router.post("/users", response_model=User, status_code=201)
async def create_user(user: User):
    save_user_to_database(user)
    return user

File: main.py

from fastopenapi.routers import StarletteRouter
from starlette.applications import Starlette
from users import user_router

app = Starlette()
main_router = StarletteRouter(app=app)
main_router.include_router(user_router, prefix="/v1")

Exception Handling Examples

Library Exceptions

from fastopenapi.error_handler import ResourceNotFoundError

@router.get("/products/{product_id}")
async def get_product(product_id: int):
    product = database.get_product(product_id)
    if product is None:
        # You can use framework-specific exception here 
        raise ResourceNotFoundError(f"Product {product_id} not found")
    return product

Framework-Specific Exceptions

Flask Example

from flask import abort

@router.get("/orders/{order_id}")
def get_order(order_id: int):
    order = get_order_from_db(order_id)
    if order is None:
        abort(404, description="Order not found")
    return order

Falcon Example

import falcon

@router.get("/invoices/{invoice_id}")
async def get_invoice(invoice_id: int):
    invoice = database.get_invoice(invoice_id)
    if invoice is None:
        raise falcon.HTTPNotFound(title="Not Found", description="Invoice not found")
    return invoice

These examples demonstrate how you can:

  • Define routes with various parameter types (path, query, body);
  • Use Pydantic for request and response validation and automatic OpenAPI schema generation;
  • Handle errors using both FastOpenAPI's built-in exception classes and framework-specific exceptions.

Project Examples

See examples for each supported framework in the examples/ directory of the repository.