If you are building a FastAPI app, mypy helps you catch type bugs early—before runtime and before tests fail in surprising ways.
This guide shows:
- how to add
mypyto a project - how to run type checking
- how to exclude paths from checks
- how to enable additional useful checks
- examples from this
pytest-fastapiapplication
1) What is mypy?
mypy is a static type checker for Python.
It reads type hints (like str, int, list[ProductRead], ProductRead | None) and reports mismatches.
Example:
- returning
strfrom a function declared as-> int - passing
Nonewhere a non-optional type is required - calling methods on values that may be
None
2) Add mypy to your project
In this project, mypy is already added in pyproject.toml:
[project]
dependencies = [
"mypy>=2.1.0",
]If you’re starting fresh with uv:
uv add mypyOr with pip:
pip install mypy3) Basic mypy config
This project uses mypy.ini:
[mypy]
python_version = 3.12
strict = True
warn_return_any = True
warn_unused_configs = True
disallow_untyped_defs = True
exclude = versions/Why these options matter
python_version = 3.12: checks with Python 3.12 typing rules.strict = True: enables a strong baseline of safety checks.warn_return_any = True: warns when a function leaksAny.warn_unused_configs = True: catches config typos/unused sections.disallow_untyped_defs = True: forces every function to have type annotations.exclude = versions/: skip files underversions/(commonly Alembic migrations).
4) Run mypy checks
Run type checks for app code:
uv run mypy appCheck both app and tests:
uv run mypy app testsTip: In CI, fail the pipeline if mypy reports errors.
5) Real examples from this app
Example A: Typed service method
From app/services/product_service.py, this method is clearly typed:
def get_product(self, product_id: int) -> ProductRead:
product = self.repository.get(product_id)
if product is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Product not found")
return ProductRead.model_validate(product)Benefits:
- input is guaranteed
int - return type is guaranteed
ProductRead Noneis handled before returning
Example B: Typed route response
From app/routes/product.py:
@router.get("/", response_model=list[ProductRead])
def list_products(
service: ProductService = Depends(get_product_service),
) -> list[ProductRead]:
return service.list_products()mypy ensures the function actually returns list[ProductRead].
Example C: Optional return handling
This route handles a potentially missing record safely:
def update_product(...) -> ProductRead:
product = service.update_product(product_id, payload)
if product is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Product not found")
return productGood pattern: check None explicitly before returning.
6) How to exclude files/folders
Sometimes you want to skip generated or legacy files (for example migrations).
Current config:
exclude = versions/You can expand this using regex:
exclude = (?x)(
^versions/|
^.*/migrations/|
^tests/fixtures/
)Keep excludes minimal. Over-excluding hides real issues.
7) Add additional checks (recommended)
If you want stricter validation, add these in mypy.ini:
[mypy]
no_implicit_optional = True
check_untyped_defs = True
warn_redundant_casts = True
warn_unused_ignores = True
warn_unreachable = TrueWhat they catch:
no_implicit_optional: forces explicit| Nonecheck_untyped_defs: checks function bodies even if partially untypedwarn_redundant_casts: flags unnecessarycast(...)warn_unused_ignores: removes dead# type: ignorewarn_unreachable: catches impossible code paths
8) Useful pattern: per-module tuning
You can keep strict mode globally, then relax specific areas:
[mypy]
strict = True
[mypy-tests.*]
disallow_untyped_defs = FalseThis is useful when tests are still being gradually typed.
9) Suggested mypy workflow for this project
- Keep
strict = Trueinmypy.ini - Run
uv run mypy app testslocally before commit - Exclude only generated code (like migrations)
- Add extra warnings incrementally
- Fix new type errors immediately to avoid backlog
Final thoughts
For FastAPI projects, type hints are already close to your business logic (schemas, routes, services).
That makes mypy a high-value, low-cost quality gate.
Start with the config you already have in this app, then add stricter checks step by step.
Jobi S S
admin
Sharing technical insights, engineering concepts, and practical modern software development guides.
Community Discussion
Enjoyed this read? Show your support or share your thoughts.




