A robust Django REST API for managing recipes, ingredients, and user authentication. Built with Django REST Framework following Test-Driven Development (TDD) architecture and designed for scalability with Docker containerization. Production-ready with Nginx reverse proxy and uWSGI.
- User Authentication: Token-based authentication
- Recipe Management: Create, read, update, and delete recipes
- Ingredient Management: Manage recipe ingredients with quantities
- Tag System: Organize recipes with custom tags
- Image Upload: Support for recipe images
- RESTful API: Clean, RESTful API design with automatic documentation
- Docker Support: Containerized deployment with Docker and Docker Compose
- PostgreSQL: Production-ready database backend
- API Documentation: Auto-generated API docs with drf-spectacular
- TDD Architecture: Built following Test-Driven Development principles
- Production Ready: Nginx reverse proxy with uWSGI for high performance
- Backend: Django 3.2, Django REST Framework
- Database: PostgreSQL
- Authentication: Token Authentication
- Documentation: drf-spectacular
- Containerization: Docker, Docker Compose
- Image Processing: Pillow
- Server: uWSGI
- Testing: Django Test Framework (TDD approach)
- Production Server: Nginx (reverse proxy)
- Process Manager: uWSGI
- Docker and Docker Compose
- Python 3.9+ (for local development)
- PostgreSQL (for local development)
-
Clone the repository
git clone <repository-url> cd recipe-api
-
Start the application
docker-compose up --build
-
Access the application
- API: http://localhost:8000/api/
- Admin: http://localhost:8000/admin/
- API Documentation: http://localhost:8000/api/schema/swagger-ui/
-
Clone the repository
git clone <repository-url> cd recipe-api
-
Create a virtual environment
python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate
-
Install dependencies
pip install -r requirements.txt pip install -r requirements.dev.txt
-
Set up environment variables
export DEBUG=1 export DB_HOST=localhost export DB_NAME=recipe_db export DB_USER=your_username export DB_PASS=your_password
-
Run migrations
cd app python manage.py migrate -
Create a superuser
python manage.py createsuperuser
-
Start the development server
python manage.py runserver
The production setup uses a three-tier architecture:
- Nginx: Reverse proxy and static file serving
- uWSGI: Application server running Django
- PostgreSQL: Database backend
Create a .env file for production:
# Database Configuration
DB_NAME=recipe_production
DB_USER=recipe_user
DB_PASS=secure_password
# Django Configuration
DJANGO_SECRET_KEY=your-secret-key-here
DJANGO_ALLOWED_HOSTS=your-domain.com,www.your-domain.com
# Optional: SSL Configuration
SSL_CERT_PATH=/path/to/cert.pem
SSL_KEY_PATH=/path/to/key.pem-
Set up environment variables
cp .env.example .env # Edit .env with your production values -
Deploy with Docker Compose
docker-compose -f docker-compose.deploy.yml up -d --build
-
Collect static files
docker-compose -f docker-compose.deploy.yml exec app python manage.py collectstatic --noinput -
Run migrations
docker-compose -f docker-compose.deploy.yml exec app python manage.py migrate -
Create superuser
docker-compose -f docker-compose.deploy.yml exec app python manage.py createsuperuser
- API: http://your-domain.com/api/
- Admin: http://your-domain.com/admin/
- API Documentation: http://your-domain.com/api/schema/swagger-ui/
The production environment uses Nginx as a reverse proxy with the following configuration:
server {
listen 8000;
location /static {
alias /vol/static/;
}
location / {
uwsgi_pass app:9000;
include /etc/nginx/uwsgi_params;
client_max_body_size 10M;
}
}- Reverse Proxy: Routes requests to uWSGI application server
- Static File Serving: Serves Django static files directly
- Load Balancing: Ready for horizontal scaling
- Security: Unprivileged nginx user for enhanced security
- File Upload Support: 10MB max body size for image uploads
To enable HTTPS, modify the Nginx configuration:
server {
listen 443 ssl;
server_name your-domain.com;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
location /static {
alias /vol/static/;
}
location / {
uwsgi_pass app:9000;
include /etc/nginx/uwsgi_params;
client_max_body_size 10M;
}
}
server {
listen 80;
server_name your-domain.com;
return 301 https://$server_name$request_uri;
}POST /api/users/create/- Create a new userPOST /api/users/token/- Get authentication tokenGET /api/users/me/- Get current user profilePUT /api/users/me/- Update current user profile
GET /api/recipes/- List all recipesPOST /api/recipes/- Create a new recipeGET /api/recipes/{id}/- Get recipe detailsPUT /api/recipes/{id}/- Update recipeDELETE /api/recipes/{id}/- Delete recipe
GET /api/tags/- List all tagsPOST /api/tags/- Create a new tagGET /api/tags/{id}/- Get tag detailsPUT /api/tags/{id}/- Update tagDELETE /api/tags/{id}/- Delete tag
GET /api/ingredients/- List all ingredientsPOST /api/ingredients/- Create a new ingredientGET /api/ingredients/{id}/- Get ingredient detailsPUT /api/ingredients/{id}/- Update ingredientDELETE /api/ingredients/{id}/- Delete ingredient
The API uses Token Authentication. To access protected endpoints:
- Create a user account:
POST /api/users/create/ - Get an authentication token:
POST /api/users/token/ - Include the token in your requests:
Authorization: Token <your_token>
# Create a user
curl -X POST http://localhost:8000/api/users/create/ \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "testpass123", "name": "Test User"}'
# Get authentication token
curl -X POST http://localhost:8000/api/users/token/ \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "testpass123"}'
# Use the token to access protected endpoints
curl -X GET http://localhost:8000/api/recipes/ \
-H "Authorization: Token <your_token>"curl -X POST http://localhost:8000/api/recipes/ \
-H "Authorization: Token <your_token>" \
-H "Content-Type: application/json" \
-d '{
"title": "Spaghetti Carbonara",
"time_minutes": 30,
"price": 15.00,
"description": "Classic Italian pasta dish",
"link": "https://example.com/carbonara",
"tags": [{"name": "Italian"}, {"name": "Pasta"}],
"ingredients": [
{"name": "Spaghetti", "amount": 500, "unit": "g"},
{"name": "Eggs", "amount": 4, "unit": "pieces"},
{"name": "Parmesan", "amount": 100, "unit": "g"}
]
}'# Get recipes by tag
curl -X GET "http://localhost:8000/api/recipes/?tags=Italian" \
-H "Authorization: Token <your_token>"
# Get recipes by ingredient
curl -X GET "http://localhost:8000/api/recipes/?ingredients=chicken" \
-H "Authorization: Token <your_token>"This project follows Test-Driven Development (TDD) principles. All features are developed with comprehensive test coverage.
# Run all tests with Docker
docker-compose exec app python manage.py test
# Run tests locally
cd app
python manage.py test
# Run specific app tests
python manage.py test recipe
python manage.py test users
# Run tests with coverage
python manage.py test --verbosity=2- Write a failing test - Define the expected behavior
- Write minimal code - Make the test pass
- Refactor - Improve code quality while keeping tests green
- Repeat - Continue the cycle for new features
app/
βββ recipe/
β βββ tests/
β βββ test_models.py
β βββ test_views.py
β βββ test_serializers.py
βββ users/
βββ tests/
βββ test_models.py
βββ test_views.py
βββ test_serializers.py
# Start development environment
docker-compose up --build
# View logs
docker-compose logs -f
# Stop services
docker-compose down
# Rebuild containers
docker-compose up --build --force-recreate# Start production environment
docker-compose -f docker-compose.deploy.yml up --build
# Run in background
docker-compose -f docker-compose.deploy.yml up -d
# View production logs
docker-compose -f docker-compose.deploy.yml logs -f
# Stop production services
docker-compose -f docker-compose.deploy.yml down
# Restart specific service
docker-compose -f docker-compose.deploy.yml restart app# Update application
docker-compose -f docker-compose.deploy.yml pull
docker-compose -f docker-compose.deploy.yml up -d --build
# Backup database
docker-compose -f docker-compose.deploy.yml exec db pg_dump -U $DB_USER $DB_NAME > backup.sql
# Monitor resources
docker-compose -f docker-compose.deploy.yml ps
docker statsrecipe-api/
βββ app/ # Django application
β βββ core/ # Core Django settings
β βββ recipe/ # Recipe app (TDD implemented)
β β βββ tests/ # Recipe tests
β βββ users/ # User management app (TDD implemented)
β β βββ tests/ # User tests
β βββ manage.py # Django management script
βββ proxy/ # Nginx configuration
β βββ Dockerfile # Nginx Docker image
β βββ default.conf.tpl # Nginx configuration template
β βββ uwsgi_params # uWSGI parameters
β βββ run.sh # Nginx startup script
βββ scripts/ # Docker scripts
βββ docker-compose.yml # Development Docker setup
βββ docker-compose.deploy.yml # Production Docker setup
βββ Dockerfile # Django app Docker image
βββ requirements.txt # Production dependencies
βββ requirements.dev.txt # Development dependencies
βββ README.md # This file
DEBUG: Enable/disable debug modeDB_HOST: Database hostDB_NAME: Database nameDB_USER: Database userDB_PASS: Database password
DB_NAME: Production database nameDB_USER: Production database userDB_PASS: Production database passwordDJANGO_SECRET_KEY: Django secret keyDJANGO_ALLOWED_HOSTS: Comma-separated list of allowed hosts
The application uses PostgreSQL as the primary database. For development, you can use SQLite by modifying the database settings in app/core/settings.py.
The Nginx configuration is templated and automatically generated at runtime using environment variables:
LISTEN_PORT: Port Nginx listens on (default: 8000)APP_HOST: Django app hostname (default: app)APP_PORT: Django app port (default: 9000)
This project follows TDD principles. When contributing:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Write tests first - Follow TDD approach
- Implement the feature to make tests pass
- Ensure all tests are green
- Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Always write tests before implementing features
- Ensure test coverage for new functionality
- Follow the Red-Green-Refactor cycle
- Write meaningful test names that describe the expected behavior
This project is licensed under the MIT License - see the LICENSE file for details.
Omar Muhammed - omarmhd.swe@gmail.com
If you encounter any issues or have questions:
- Check the API documentation
- Review the logs:
docker-compose logs -f - Run tests to identify issues:
python manage.py test - Check Nginx logs:
docker-compose -f docker-compose.deploy.yml logs proxy - Open an issue on GitHub
Nginx not serving static files:
docker-compose -f docker-compose.deploy.yml exec app python manage.py collectstatic --noinputDatabase connection issues:
docker-compose -f docker-compose.deploy.yml exec app python manage.py wait_for_dbPermission issues:
docker-compose -f docker-compose.deploy.yml down
docker-compose -f docker-compose.deploy.yml up -d --force-recreate