A production-ready REST API built with FastAPI, featuring comprehensive error handling, logging, testing, and database integration.
- Features
- Prerequisites
- Installation
- Configuration
- Running the API
- API Documentation
- Project Structure
- API Endpoints
- Testing
- Development
- Logging
- Error Handling
- Deployment
- Troubleshooting
- Contributing
✨ Core Features
- ⚡ FastAPI - Modern, high-performance Python web framework
- 🔒 CORS Middleware - Cross-Origin Resource Sharing enabled
- 📝 Automatic API Docs - Swagger UI and ReDoc documentation
- 🧪 Unit Tests - Comprehensive test suite with pytest
- 📊 Logging - Structured logging with file rotation
⚠️ Error Handling - Custom exception handling and middleware- 🗄️ Database Ready - SQLAlchemy integration prepared
- 🔄 Async/Await - Full async support throughout
- ✅ CRUD operations for items
- ✅ RESTful design principles
- ✅ Input validation with Pydantic
- ✅ Proper HTTP status codes
- ✅ Error responses with details
- ✅ Health check endpoint
- Python 3.8+ (3.10+ recommended)
- pip (Python package manager)
- git (for version control)
- virtualenv or venv (Python virtual environment)
python --version # Should be 3.8+
pip --version # Should be latestgit clone https://github.com/muhammedasadn/test_api.git
cd test_apiOn Linux/macOS:
python3 -m venv venv
source venv/bin/activateOn Windows:
python -m venv venv
venv\Scripts\activate# Install production dependencies
pip install -r requirements.txt
# Or install with development dependencies
pip install -r requirements-dev.txt# Copy the example environment file
cp .env.example .env
# Edit .env with your settings
# On Windows: copy .env.example .envCreate a .env file in the project root with the following variables:
# API Settings
DEBUG=True
HOST=0.0.0.0
PORT=8000
# Database (optional)
DATABASE_URL=sqlite:///./test.db
# Logging
LOG_LEVEL=INFO| Variable | Default | Description |
|---|---|---|
DEBUG |
True | Enable debug mode |
HOST |
0.0.0.0 | Server host address |
PORT |
8000 | Server port |
DATABASE_URL |
sqlite:///./test.db | Database connection string |
LOG_LEVEL |
INFO | Logging level (DEBUG, INFO, WARNING, ERROR) |
For production, update .env:
DEBUG=False
HOST=0.0.0.0
PORT=8000
DATABASE_URL=postgresql://user:password@localhost/dbname
LOG_LEVEL=WARNING# Development with auto-reload
uvicorn src.main:app --reload
# Production
uvicorn src.main:app --host 0.0.0.0 --port 8000 --workers 4python src/main.pyuvicorn src.main:app --reload --host 127.0.0.1 --port 8080INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [28720]
INFO: Started server process [28722]
INFO: Waiting for application startup.
INFO: Application startup complete.
Once the server is running, access:
- Swagger UI (Interactive): http://localhost:8000/docs
- ReDoc (Read-only): http://localhost:8000/redoc
- OpenAPI JSON: http://localhost:8000/openapi.json
Go to http://localhost:8000/docs and:
- Click on an endpoint
- Click "Try it out"
- Enter parameters
- Click "Execute"
test_api/
├── src/
│ ├── __init__.py
│ ├── main.py # FastAPI application
│ ├── config.py # Configuration module
│ ├── database.py # Database utilities
│ ├── logging_config.py # Logging configuration
│ ├── exceptions.py # Custom exceptions
│ ├── routes/
│ │ ├── __init__.py
│ │ └── items.py # Item routes
│ ├── models/
│ │ ├── __init__.py
│ │ └── schemas.py # Pydantic models
│ └── utils/
│ ├── __init__.py
│ └── helpers.py # Utility functions
│
├── tests/
│ ├── conftest.py # Pytest configuration
│ └── test_main.py # Main API tests
│
├── logs/ # Application logs
├── config.py # Root configuration
├── requirements.txt # Production dependencies
├── requirements-dev.txt # Development dependencies
├── pytest.ini # Pytest configuration
├── .env.example # Example environment file
├── .gitignore # Git ignore rules
└── README.md # This file
GET /
- Get API welcome message
- No authentication required
- Returns:
{"message": "Welcome to your API!"}
GET /health
- Check API health status
- No authentication required
- Returns:
{"status": "healthy"}
GET /api/v1/items
- Retrieve all items
- No parameters
- Returns: Array of items
Example Response:
[
{
"id": 1,
"name": "Item 1",
"description": "First item",
"price": 10.0,
"quantity": 5
}
]GET /api/v1/items/{item_id}
- Retrieve a specific item by ID
- Parameters:
item_id(integer) - Returns: Item object
Example:
curl http://localhost:8000/api/v1/items/1POST /api/v1/items
- Create a new item
- Request body: Item object
- Returns: Created item with ID
- Status: 201 (Created)
Example Request:
curl -X POST http://localhost:8000/api/v1/items \
-H "Content-Type: application/json" \
-d '{
"name": "New Item",
"description": "Item description",
"price": 19.99,
"quantity": 10
}'Example Response:
{
"id": 1,
"name": "New Item",
"description": "Item description",
"price": 19.99,
"quantity": 10
}PUT /api/v1/items/{item_id}
- Update an existing item
- Parameters:
item_id(integer) - Request body: Updated item data
- Returns: Updated item
Example:
curl -X PUT http://localhost:8000/api/v1/items/1 \
-H "Content-Type: application/json" \
-d '{
"name": "Updated Item",
"price": 29.99,
"quantity": 15
}'DELETE /api/v1/items/{item_id}
- Delete an item
- Parameters:
item_id(integer) - Returns: Empty response
- Status: 204 (No Content)
Example:
curl -X DELETE http://localhost:8000/api/v1/items/1pytest# Run specific test file
pytest tests/test_main.py
# Run specific test class
pytest tests/test_main.py::TestHealthcheck
# Run specific test function
pytest tests/test_main.py::test_health_checkpytest --cov=src tests/pip install pytest-watch
ptwtests/test_main.py::test_read_root PASSED [ 16%]
tests/test_main.py::test_health_check PASSED [ 33%]
tests/test_main.py::test_get_items PASSED [ 50%]
tests/test_main.py::test_create_item PASSED [ 66%]
tests/test_main.py::TestHealthcheck::test_health_returns_200 PASSED [ 100%]
============================== 5 passed in 0.12s ==============================
black src/ tests/flake8 src/ tests/isort src/ tests/mypy src/# Install pre-commit
pip install pre-commit
# Create .pre-commit-config.yaml
cat > .pre-commit-config.yaml << EOF
repos:
- repo: https://github.com/psf/black
rev: 23.9.1
hooks:
- id: black
- repo: https://github.com/PyCQA/flake8
rev: 6.1.0
hooks:
- id: flake8
EOF
# Install hooks
pre-commit install
# Run on all files
pre-commit run --all-files- Create endpoint in
src/routes/ - Create corresponding tests in
tests/ - Register router in
src/main.py
Example:
# src/routes/users.py
from fastapi import APIRouter
router = APIRouter(prefix="/users", tags=["users"])
@router.get("")
async def get_users():
return {"users": []}Then in src/main.py:
from src.routes import users
app.include_router(users.router)Logs are written to both console and file.
logs/
└── api.log # Application log file
| Level | Use For |
|---|---|
| DEBUG | Development and debugging |
| INFO | General informational messages |
| WARNING | Warning messages |
| ERROR | Error messages |
| CRITICAL | Critical errors |
Edit src/logging_config.py to customize:
- Log format
- Log level
- File rotation
- Handler configuration
# Real-time log view
tail -f logs/api.log
# Last 50 lines
tail -50 logs/api.log
# Search logs
grep "ERROR" logs/api.logThe API includes custom exception classes:
NotFoundException- Resource not found (404)ValidationException- Validation error (422)UnauthorizedException- Unauthorized access (401)ForbiddenException- Forbidden access (403)
{
"error": "Item not found",
"status_code": 404,
"path": "/api/v1/items/999"
}from src.exceptions import NotFoundException
@router.get("/items/{item_id}")
async def get_item(item_id: int):
if item_id not in items_db:
raise NotFoundException(f"Item {item_id} not found")
return items_db[item_id]uvicorn src.main:app --host 0.0.0.0 --port 8000 --reloadCreate Dockerfile:
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8000"]Build and run:
docker build -t test-api .
docker run -p 8000:8000 test-apiCreate /etc/systemd/system/test-api.service:
[Unit]
Description=Test API Service
After=network.target
[Service]
Type=notify
User=api_user
WorkingDirectory=/opt/test_api
ExecStart=/opt/test_api/venv/bin/uvicorn src.main:app --host 0.0.0.0 --port 8000
Restart=on-failure
[Install]
WantedBy=multi-user.targetEnable and start:
sudo systemctl enable test-api
sudo systemctl start test-api
sudo systemctl status test-apiheroku create your-api-name
git push heroku main- Launch EC2 instance
- SSH into instance
- Clone repository
- Create venv and install dependencies
- Run with gunicorn or systemd
- Connect GitHub repository
- Set environment variables
- Deploy
Problem: Address already in use: ('0.0.0.0', 8000)
Solution (Linux/macOS):
lsof -i :8000
kill -9 <PID>Solution (Windows):
netstat -ano | findstr :8000
taskkill /PID <PID> /FProblem: ModuleNotFoundError: No module named 'src'
Solution:
# Ensure you're in project root
cd test_api
# Reinstall dependencies
pip install -r requirements-dev.txtProblem: venv not activating
Solution:
# Recreate venv
rm -rf venv
python -m venv venv
source venv/bin/activate # or venv\Scripts\activate on Windows
# Reinstall
pip install -r requirements.txtProblem: ImportError when running tests
Solution:
# Ensure pytest can find modules
pip install -e .
# Or set PYTHONPATH
export PYTHONPATH=$PWD
pytestProblem: sqlite3.DatabaseError
Solution:
# Remove old database
rm test.db
# Restart application (creates new database)
uvicorn src.main:app --reloadContributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing) - Make changes and test
- Commit (
git commit -am 'Add amazing feature') - Push (
git push origin feature/amazing) - Open a Pull Request
MIT License - See LICENSE file for details
- GitHub: https://github.com/muhammedasadn/test_api
- Issues: https://github.com/muhammedasadn/test_api/issues
- Initial release
- CRUD operations
- Error handling
- Logging configuration
- Test suite
- Documentation
Built with ❤️ using FastAPI
DEBUG=True
HOST=0.0.0.0
PORT=8000
LOG_LEVEL=INFO