python
42 lines · 6 steps
Validating date ranges with FastAPI dependencies
A reusable dependency parses and validates a query date range before any endpoint sees it.
Explained by
highlit
1from datetime import date, timedelta
2from typing import Annotated
3
4from fastapi import APIRouter, Depends, Query
5from pydantic import BaseModel, model_validator
6
7router = APIRouter(prefix="/reports", tags=["reports"])
8
9MAX_RANGE = timedelta(days=366)
10
11
12class DateRange(BaseModel):
13 start: date
14 end: date
15
16 @model_validator(mode="after")
17 def check_range(self) -> "DateRange":
18 if self.start > self.end:
19 raise ValueError("start must not be after end")
20 if self.end - self.start > MAX_RANGE:
21 raise ValueError("range must not exceed 366 days")
22 if self.end > date.today():
23 raise ValueError("end must not be in the future")
24 return self
25
26
27def date_range(
28 start: Annotated[date, Query(description="Inclusive start date (YYYY-MM-DD)")],
29 end: Annotated[date | None, Query(description="Inclusive end date; defaults to today")] = None,
30) -> DateRange:
31 return DateRange(start=start, end=end or date.today())
32
33
34@router.get("/revenue")
35async def revenue_report(rng: Annotated[DateRange, Depends(date_range)]):
36 rows = await revenue_service.aggregate(rng.start, rng.end)
37 return {
38 "start": rng.start,
39 "end": rng.end,
40 "days": (rng.end - rng.start).days + 1,
41 "rows": rows,
42 }
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Centralizing validation in a dependency keeps every endpoint's handler focused on business logic.
- 2A Pydantic model_validator lets you enforce cross-field invariants that single-field checks can't express.
- 3Returning a validated object from a dependency gives handlers a clean, trustworthy input contract.
Related explainers
typescript
import { registerLocaleData } from '@angular/common'; import localeFr from '@angular/common/locales/fr'; import localeFrExtra from '@angular/common/locales/extra/fr'; import localeDe from '@angular/common/locales/de';
Locale-aware bootstrapping in Angular
i18n
localization
dependency-injection
Intermediate
8 steps
python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect app = FastAPI()
Building a WebSocket chat with FastAPI
websockets
broadcast
connection-management
Intermediate
9 steps
typescript
import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import * as Joi from 'joi';
Validating env config at boot in NestJS
configuration
schema-validation
environment-variables
Intermediate
8 steps
php
<?php namespace App\Services\Checkout;
Validating coupons with Laravel's Pipeline
pipeline
chain of responsibility
transactions
Intermediate
7 steps
java
@Component @Converter public class EncryptedStringConverter implements AttributeConverter<String, String> {
Transparent column encryption in Spring & JPA
encryption
aes-gcm
jpa-converter
Advanced
10 steps
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/validating-date-ranges-with-fastapi-dependencies-explained-python-57ad/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.