A modern, production-oriented FastAPI backend demonstrating clean architecture, JWT authentication, Redis-powered token revocation, SQLModel integration, asynchronous PostgreSQL access, and Alembic database migrations.
Built as a practical example of how real-world FastAPI applications are structured beyond simple CRUD operations.
- User registration
- User login with JWT authentication
- Access Token & Refresh Token flow
- Protected endpoints using OAuth2
- Token revocation (logout) using Redis blocklists
- Password hashing with Argon2
- Current authenticated user endpoint (
/me)
- Create books
- List books
- Retrieve single books
- Update books (PUT)
- Partial updates (PATCH)
- Delete books
- Request validation using Pydantic
- Async FastAPI application
- Async SQLModel ORM
- Async SQLAlchemy Engine
- Redis integration
- Alembic migrations
- Environment-based configuration
- Custom exception handling
- Application lifespan management
- Health checks
src/
├── auth/
│ ├── routes.py
│ ├── service.py
│ ├── deps.py
│ ├── schemas.py
│ ├── models.py
│ └── utils.py
│
├── books/
│ ├── routes.py
│ ├── service.py
│ ├── schemas.py
│ └── models.py
│
├── core/
│ ├── db.py
│ ├── redis.py
│ ├── settings.py
│ ├── exceptions.py
│ └── models_base.py
│
└── __init__.py
- FastAPI
- SQLModel
- SQLAlchemy Async
- Pydantic v2
- JWT
- OAuth2 Password Flow
- Passlib
- Argon2
- PostgreSQL
- Alembic
- Redis
- Python 3.12+
The application follows a layered architecture:
Request
│
▼
Routes
│
▼
Services
│
▼
Database / Redis
Responsible for:
- HTTP endpoints
- Dependency injection
- Request/response serialization
Example:
@router.post("/signin")
async def signin(...):
...Responsible for:
- Business logic
- Database operations
- Authentication workflows
Example:
class AuthService:
async def signin(...):
...Provides shared infrastructure:
- Database connection management
- Redis connection management
- Settings management
- Custom exception handling
- Shared model utilities
Client
│
▼
POST /auth/register
│
▼
Validate Input
│
▼
Hash Password
│
▼
Store User
│
▼
Return User
Client
│
▼
POST /auth/signin
│
▼
Verify Password
│
▼
Generate Access Token
Generate Refresh Token
│
▼
Return Tokens
Authorization: Bearer <access_token>
│
▼
OAuth2 Dependency
│
▼
JWT Validation
│
▼
Redis Blocklist Check
│
▼
Load Current User
│
▼
Execute Endpoint
User Logout
│
▼
Store Access Token
Store Refresh Token
inside Redis Blocklist
│
▼
Future Requests Rejected
This approach allows immediate token invalidation without waiting for token expiration.
User
├── id
├── name
├── email
├── password_hash
├── is_verified
├── created_at
└── updated_at
Book
├── id
├── title
├── author
├── publisher
├── published_date
├── page_count
├── language
├── created_at
└── updated_at
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/auth/register |
Register user |
| POST | /api/v1/auth/signin |
Login |
| GET | /api/v1/auth/me |
Current user |
| POST | /api/v1/auth/refresh-token |
Refresh access token |
| POST | /api/v1/auth/logout |
Logout |
| Method | Endpoint |
|---|---|
| GET | /api/v1/books |
| POST | /api/v1/books |
| GET | /api/v1/books/{id} |
| PUT | /api/v1/books/{id} |
| PATCH | /api/v1/books/{id} |
| DELETE | /api/v1/books/{id} |
All book endpoints require authentication.
Create a .env file:
VERSION=v1
DATABASE_URL=postgresql+asyncpg://postgres:password@localhost/light_db
JWT_SECRET_KEY=super-secret-key
JWT_ALGORITHM=HS256
ACCESS_EXPIRE_MINUTES=150
REFRESH_EXPIRE_DAYS=30
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DB=0git clone https://github.com/your-username/light-api.git
cd light-apipython -m venv venvsource venv/bin/activatevenv\Scripts\activatepip install -r requirements.txtUsing Docker:
services:
postgres:
image: postgres:17
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: light_db
ports:
- "5432:5432"
redis:
image: redis:latest
ports:
- "6379:6379"Start:
docker compose up -dCreate migration:
alembic revision --autogenerate -m "message"Apply migrations:
alembic upgrade headRollback:
alembic downgrade -1python seeder.pyThis inserts sample books if they do not already exist.
Using Uvicorn:
uvicorn src:app --reloadApplication:
http://localhost:8000
Swagger UI:
http://localhost:8000/docs
ReDoc:
http://localhost:8000/redoc
{
"success": false,
"error": {
"type": "validation_error",
"message": "Validation failed",
"details": [
{
"field": "body.email",
"message": "value is not a valid email address"
}
]
}
}GET /healthResponse:
{
"status": "ok"
}On application startup:
- Database connection is verified
- Redis connection is verified
- Application dependencies are initialized
- Argon2 password hashing
- JWT-based authentication
- Access/Refresh token separation
- Redis-backed token revocation
- OAuth2 integration
- Input validation via Pydantic
- Centralized exception handling
- Email verification
- Password reset workflow
- Role-based access control (RBAC)
- Permissions system
- Docker production deployment
- CI/CD pipelines
- Test suite (Pytest)
- Rate limiting
- API versioning strategy
- OpenTelemetry observability
- Structured logging
MIT License