🌐 Live Demo: https://dadly.onrender.com
DADLY is a modern, full-stack recipe discovery web application featuring a Tinder-style swipe interface. Built for home cooks and food enthusiasts who want to discover recipes, save favorites, and get personalized recommendations based on ingredients they already have in their pantry.
- Swipe Interface: Tinder-style recipe discovery - swipe right to like, left to skip
- Pantry Management: Track ingredients you have at home
- Smart Recommendations: Recipe feed prioritizes dishes matching your pantry items
- Recipe Collections: Save and organize your favorite recipes
- User Profiles: Dietary preferences (vegetarian, vegan, keto, gluten-free) and allergy tracking
- Recipe Details: Full ingredients, instructions, prep/cook time, difficulty level
- Modern Stack: FastAPI backend + React 19 frontend with JavaScript-ready architecture
- Database: MySQL 8.0 with SQLAlchemy ORM and Alembic migrations
- Authentication: JWT-based auth with access + refresh tokens, secure token blacklist
- Docker-First: Complete docker-compose setup for local development
- CI/CD Pipeline: Automated testing (pytest) and deployment to GitHub Container Registry
- API Documentation: Auto-generated Swagger/OpenAPI docs at
/docs - Test Coverage: Comprehensive pytest suite with SQLite in-memory database
- Architecture
- Quick Start
- Development Setup
- Environment Variables
- Database
- API Documentation
- Testing
- Deployment
- Project Structure
- Contributing
- Troubleshooting
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ │ │ │ │ │
│ React Frontend │ ◄─────► │ FastAPI │ ◄─────► │ MySQL 8.0 │
│ (Vite + Tailwind) │ Backend │ │ Database │
│ Port: 5173/8080│ │ Port: 8000 │ │ Port: xxxx │
│ │ │ │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
│ │
▼ ▼
Axios HTTP SQLAlchemy ORM
JWT Tokens Alembic Migrations
- Framework: FastAPI 0.119+ (async Python web framework)
- ORM: SQLAlchemy 2.0 with declarative models
- Migrations: Alembic for database version control
- Authentication: JWT tokens via
python-jose+bcryptpassword hashing - Database: MySQL 8.0 (production) / SQLite (testing)
- Validation: Pydantic v2 schemas with email validation
- Package Management:
uv(fast Python package manager) - Server: Uvicorn ASGI server
- Framework: React 19.1 with hooks
- Build Tool: Vite 7.1 (fast HMR, optimized builds)
- Styling: Tailwind CSS 4.1
- HTTP Client: Axios with interceptors
- Routing: React Router DOM 7.9
- Icons: Lucide React + React Icons
- State Management: React Context API
- Containerization: Docker + Docker Compose
- CI: GitHub Actions (pytest on every PR)
- CD: Automated image builds to GitHub Container Registry
- Deployment: Render (Web Services) with auto-deploy
- Monitoring: Render logs + health check endpoints
- Docker Desktop 20.10+ and Docker Compose 2.0+
- Git for cloning the repository
- Node.js 18+ and npm (only if running frontend locally)
- Python 3.13 and uv (only if running backend locally)
git clone https://github.com/UFAZ-L2-CS1/DADLY.git
cd DADLYCreate a .env file in the project root (copy from examples):
# Copy backend env template
cp backend/.env.example .env
# Copy frontend env template
cp frontend/.env.example frontend/.envEdit .env with your database credentials:
# Database Configuration (required)
DB_HOST=mysql.ufazien.com
DB_PORT=3306
DB_USER=your_mysql_user
DB_PASSWORD=your_mysql_password
DB_NAME=your_database_name
# JWT Configuration (required - generate a secure random key)
JWT_SECRET_KEY=your-super-secret-key-here-use-openssl-rand-hex-32
JWT_ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=30
REFRESH_TOKEN_EXPIRE_DAYS=7
# Application Settings
LOG_LEVEL=INFO
TIMEZONE=Asia/BakuEdit frontend/.env (or use defaults):
# API URLs (use localhost for local dev)
VITE_BASE_URL=http://localhost:8000/api/v1
VITE_AUTH_URL=http://localhost:8000
NODE_ENV=development# Build and start backend + frontend in Docker
docker-compose up --buildWait 30-60 seconds for services to initialize. You'll see:
- Backend:
Application startup complete - Frontend:
ready in X ms
- Frontend: http://localhost:5173 (Vite dev server with HMR)
- Backend API: http://localhost:8000
- API Documentation: http://localhost:8000/docs (Swagger UI)
- Alternative API Docs: http://localhost:8000/redoc
Run Alembic migrations (inside backend container):
docker exec -it dadly-backend /app/.venv/bin/alembic upgrade headFetch recipes from Spoonacular API (50 recipes):
docker exec -it dadly-backend /app/.venv/bin/python fetch_spoonacular_recipes.py 50Note: Free Spoonacular API tier has 150 requests/day limit.
- Navigate to http://localhost:5173
- Click "Sign Up" or go to http://localhost:5173/auth/register
- Fill in email, name, password (min 8 chars with 1 digit)
- Login and start swiping! 🎉
Best for: Getting started quickly, consistent environment.
# Start all services with auto-reload
docker-compose up --build
# Backend logs (follow mode)
docker-compose logs -f backend
# Frontend logs
docker-compose logs -f frontend
# Stop all services
docker-compose down
# Rebuild specific service
docker-compose up --build backendBackend changes auto-reload via volume mounts + uvicorn --reload.
Frontend changes trigger Vite HMR instantly.
Best for: Active frontend development with instant feedback.
Backend in Docker:
docker-compose up backendFrontend locally (separate terminal):
cd frontend
npm install
npm run devFrontend runs at http://localhost:5173 with instant HMR (no Docker overhead).
Best for: Backend development, debugging, IDE integration.
Backend locally:
cd backend
# Install uv if not already installed
curl -LsSf https://astral.sh/uv/install.sh | sh
# Create virtual environment and install dependencies
uv sync
# Set DATABASE_URL environment variable
export DATABASE_URL="mysql+pymysql://user:pass@host:3306/dbname"
# Run migrations
uv run alembic upgrade head
# Start dev server with auto-reload
uv run uvicorn app.main:app --reload --host 0.0.0.0 --port 8000Frontend locally:
cd frontend
npm install
npm run dev| Variable | Required | Default | Description |
|---|---|---|---|
DB_HOST |
✅ Yes | - | MySQL database host (e.g., mysql.ufazien.com) |
DB_PORT |
✅ Yes | 3306 |
MySQL port |
DB_USER |
✅ Yes | - | Database username |
DB_PASSWORD |
✅ Yes | - | Database password |
DB_NAME |
✅ Yes | - | Database name |
JWT_SECRET_KEY |
✅ Yes | - | Secret key for JWT signing (generate with openssl rand -hex 32) |
JWT_ALGORITHM |
No | HS256 |
JWT signing algorithm |
ACCESS_TOKEN_EXPIRE_MINUTES |
No | 30 |
Access token lifetime in minutes |
REFRESH_TOKEN_EXPIRE_DAYS |
No | 7 |
Refresh token lifetime in days |
LOG_LEVEL |
No | INFO |
Logging level (DEBUG, INFO, WARNING, ERROR) |
TIMEZONE |
No | Asia/Baku |
Application timezone for timestamps |
| Variable | Required | Default | Description |
|---|---|---|---|
VITE_BASE_URL |
No | http://localhost:8000/api/v1 |
Backend API base URL |
VITE_AUTH_URL |
No | http://localhost:8000 |
Backend auth URL (used for auth base path) |
NODE_ENV |
No | development |
Node environment (development or production) |
# Generate a secure 256-bit key
openssl rand -hex 32
# Output: a1b2c3d4e5f6... (use this for JWT_SECRET_KEY)CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(255) UNIQUE NOT NULL,
name VARCHAR(100) NOT NULL,
hashed_password VARCHAR(255) NOT NULL,
dietary_type VARCHAR(20) DEFAULT 'none', -- none, vegetarian, vegan, gluten_free, keto
allergies TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);CREATE TABLE recipes (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(200) NOT NULL,
description TEXT,
prep_time INT NOT NULL, -- minutes
cook_time INT NOT NULL, -- minutes
difficulty VARCHAR(10) NOT NULL, -- easy, medium, hard
image_url VARCHAR(500),
instructions TEXT NOT NULL,
ingredients TEXT NOT NULL, -- JSON string array
like_count INT DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_name (name)
);CREATE TABLE user_recipe_interactions (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
recipe_id INT NOT NULL,
liked BOOLEAN NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (recipe_id) REFERENCES recipes(id),
UNIQUE KEY uq_user_recipe (user_id, recipe_id) -- Prevents duplicate likes
);CREATE TABLE pantry_items (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
ingredient_name VARCHAR(100) NOT NULL, -- Stored lowercase
quantity VARCHAR(50), -- "2 cups", "500g", etc.
added_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id),
UNIQUE KEY uq_user_ingredient (user_id, ingredient_name) -- One entry per ingredient
);DADLY uses Alembic for database version control.
# Inside backend container
docker exec -it dadly-backend /app/.venv/bin/alembic revision --autogenerate -m "Add column to users table"
# Or locally with uv (requires DATABASE_URL env var)
cd backend
export DATABASE_URL="mysql+pymysql://user:pass@host:3306/dbname"
uv run alembic revision --autogenerate -m "Add column to users table"# Apply all pending migrations
docker exec -it dadly-backend /app/.venv/bin/alembic upgrade head
# Rollback one migration
docker exec -it dadly-backend /app/.venv/bin/alembic downgrade -1
# View migration history
docker exec -it dadly-backend /app/.venv/bin/alembic history- Versions:
backend/alembic/versions/ - Config:
backend/alembic.ini
# Fetch 50 recipes from Spoonacular API
docker exec -it dadly-backend /app/.venv/bin/python fetch_spoonacular_recipes.py 50
# Fetch 100 recipes (default)
docker exec -it dadly-backend /app/.venv/bin/python fetch_spoonacular_recipes.py 100
# Fetch by category (automatic variety)
docker exec -it dadly-backend /app/.venv/bin/python fetch_spoonacular_recipes.py 50Spoonacular API Notes:
- Free tier: 150 requests/day
- Recipes are fetched by category (main course, dessert, breakfast, etc.)
- Duplicate recipes are automatically skipped
- Script in:
backend/fetch_spoonacular_recipes.py
Once the backend is running, access the Swagger UI for interactive API exploration:
http://localhost:8000/docs
Alternative documentation (ReDoc):
http://localhost:8000/redoc
All endpoints are prefixed with /api/v1.
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| POST | /auth/register |
Create new user account | No |
| POST | /auth/token |
Login (returns JWT tokens) | No |
| GET | /auth/me |
Get current user profile | Yes |
| POST | /auth/refresh |
Refresh access token | No (needs refresh token) |
| POST | /auth/logout |
Invalidate current token | Yes |
Example: Register
curl -X POST "http://localhost:8000/api/v1/auth/register" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"name": "John Doe",
"password": "SecureP@ss123",
"dietary_type": "vegetarian",
"allergies": "peanuts, shellfish"
}'Example: Login
curl -X POST "http://localhost:8000/api/v1/auth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=user@example.com&password=SecureP@ss123"
# Response:
# {
# "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
# "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
# "token_type": "bearer"
# }| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| GET | /recipes/feed |
Get personalized recipe feed (excludes liked) | Optional |
| GET | /recipes/{recipe_id} |
Get recipe details | Optional |
| POST | /recipes/{recipe_id}/like |
Like a recipe | Yes |
| DELETE | /recipes/{recipe_id}/unlike |
Unlike a recipe | Yes |
| GET | /recipes/liked |
Get user's liked recipes (paginated) | Yes |
Example: Get Recipe Feed
# Guest user (no auth)
curl "http://localhost:8000/api/v1/recipes/feed?limit=10"
# Authenticated user (pantry-prioritized)
curl "http://localhost:8000/api/v1/recipes/feed?limit=10&exclude_ids=1,2,3" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"Example: Like Recipe
curl -X POST "http://localhost:8000/api/v1/recipes/42/like" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| GET | /pantry/ |
Get user's pantry items | Yes |
| POST | /pantry/ |
Add single ingredient | Yes |
| POST | /pantry/bulk |
Add multiple ingredients | Yes |
| DELETE | /pantry/{item_id} |
Remove ingredient | Yes |
| DELETE | /pantry/ |
Clear all ingredients | Yes |
Example: Add Pantry Item
curl -X POST "http://localhost:8000/api/v1/pantry/" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ingredient_name": "tomatoes",
"quantity": "3 pieces"
}'Example: Bulk Add
curl -X POST "http://localhost:8000/api/v1/pantry/bulk" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ingredients": [
{"ingredient_name": "garlic", "quantity": "1 bulb"},
{"ingredient_name": "olive oil", "quantity": "500ml"}
]
}'| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| GET | /users/stats |
Get user statistics (likes, pantry count) | Yes |
| PUT | /users/profile |
Update user profile | Yes |
| DELETE | /users/account |
Delete user account | Yes |
Example: Get User Stats
curl "http://localhost:8000/api/v1/users/stats" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
# Response:
# {
# "total_liked_recipes": 24,
# "total_pantry_items": 12
# }- Register:
POST /auth/register→ Get user credentials - Login:
POST /auth/token→ Receiveaccess_token(30min) +refresh_token(7 days) - Authorized Requests: Include header
Authorization: Bearer <access_token> - Token Refresh: When access token expires,
POST /auth/refreshwith refresh token - Logout:
POST /auth/logoutto invalidate token (blacklist)
| Status Code | Meaning | Example |
|---|---|---|
| 400 | Bad Request | Duplicate email, invalid input |
| 401 | Unauthorized | Missing/invalid token |
| 404 | Not Found | Recipe/user not found |
| 422 | Validation Error | Pydantic schema validation failure |
| 500 | Internal Server Error | Database connection failure |
# Run all tests with verbose output
cd backend
uv run pytest tests/ -v
# Run specific test file
uv run pytest tests/test_api/test_auth.py -v
# Run tests with coverage report
uv run pytest tests/ --cov=app --cov-report=term-missing
# Run tests in watch mode (re-run on file changes)
uv run pytest-watch tests/backend/tests/
├── conftest.py # Test fixtures (SQLite in-memory DB, TestClient)
├── test_main.py # Main app tests
└── test_api/
├── test_auth.py # Authentication endpoint tests
├── test_recipes.py # Recipe feed/like/unlike tests
├── test_pantry.py # Pantry CRUD tests
└── test_users.py # User profile/stats/delete tests
client: FastAPI TestClient for HTTP requeststest_db: SQLite in-memory database (isolated per test)test_user: Pre-created test user with JWT token- Token blacklist reset: Cleared before each test to prevent interference
GitHub Actions automatically runs tests on every push/PR:
# .github/workflows/ci.yml
- Run pytest in isolated environment
- Upload coverage reports
- Fail PR if tests don't passView CI Status: Check the Build Status badge at the top of this README.
DADLY includes a one-click deployment configuration for Render.
Quick Deploy:
- Push code to GitHub
- Connect repository to
Render - Configure environment variables
- Run database migrations
- Seed recipe data
GitHub Actions automatically builds and publishes Docker images on merge to main/develop:
# .github/workflows/cd.yml
- Build backend image → ghcr.io/yourusername/dadly-backend:latest
- Build frontend image → ghcr.io/yourusername/dadly-frontend:latest- Configure environment variables (DB credentials, JWT secret)
- Set strong
JWT_SECRET_KEY(useopenssl rand -hex 32) - Apply database migrations (
alembic upgrade head) - Populate recipe database (
fetch_spoonacular_recipes.py) - Enable CORS for frontend domain (update
main.py) - Set up health check monitoring (
/api/v1/health) - Configure frontend
VITE_BASE_URLto production backend URL
Dadly/
├── backend/ # FastAPI backend application
│ ├── app/
│ │ ├── main.py # FastAPI app initialization, CORS, routers
│ │ ├── api/ # API route handlers
│ │ │ ├── auth.py # JWT authentication (register, login, refresh, logout)
│ │ │ ├── recipes.py # Recipe feed, like/unlike, details
│ │ │ ├── pantry.py # Pantry CRUD operations
│ │ │ ├── users.py # User profile, stats, account deletion
│ │ │ └── health.py # Health check endpoint
│ │ ├── models/
│ │ │ └── models.py # SQLAlchemy ORM models (User, Recipe, etc.)
│ │ ├── schemas/
│ │ │ └── schemas.py # Pydantic request/response schemas
│ │ ├── config/
│ │ │ └── config.py # Configuration (JWT, logging, timezone)
│ │ └── db/
│ │ └── database.py # SQLAlchemy session management
│ ├── alembic/ # Database migrations
│ │ └── versions/ # Migration scripts
│ ├── tests/ # Pytest test suite
│ │ ├── conftest.py # Test fixtures (SQLite in-memory)
│ │ └── test_api/ # API endpoint tests
│ ├── fetch_spoonacular_recipes.py # Recipe data seeder
│ ├── pyproject.toml # uv dependencies
│ ├── alembic.ini # Alembic configuration
│ └── Dockerfile # Backend container image
│
├── frontend/ # React + Vite frontend
│ ├── src/
│ │ ├── main.jsx # React app entry point
│ │ ├── App.jsx # Router configuration
│ │ ├── pages/ # Page components
│ │ │ ├── Home.jsx # Landing page
│ │ │ ├── SignIn.jsx # Login page
│ │ │ ├── Register.jsx # Registration page
│ │ │ ├── Recipes.jsx # Swipe interface (SwiperGame)
│ │ │ ├── Pantry.jsx # Pantry management UI
│ │ │ ├── UserPage.jsx # User profile and liked recipes
│ │ │ └── RecipeDetails.jsx # Recipe detail view
│ │ └── components/ # Reusable components
│ │ ├── SwiperGame.jsx # Tinder-style swipe logic
│ │ ├── RecipeFeed.jsx # Recipe card display
│ │ ├── Header.jsx # Navigation header
│ │ └── ...
│ ├── service/ # API integration layer
│ │ ├── AxiosInstance.jsx # Axios instances (base + auth)
│ │ ├── AuthService.js # Authentication API calls
│ │ └── Data.js # Recipe/pantry API calls
│ ├── context/
│ │ └── DataContext.js # Global state management (user, auth)
│ ├── vite.config.js # Vite configuration
│ ├── package.json # npm dependencies
│ └── Dockerfile # Frontend container image
│
├── .github/
│ ├── workflows/
│ │ ├── ci.yml # Pytest CI pipeline
│ │ └── cd.yml # Docker image publishing
│ └── copilot-instructions.md # AI assistant guidelines
│
├── docker-compose.yml # Local development orchestration
├── render.yaml # Render deployment blueprint
├── DEPLOYMENT.md # Production deployment guide
└── README.md # This file
- Hot Reload: Python files auto-reload via uvicorn
--reload+ volume mounts - Database Console:
docker exec -it dadly-backend mysql -h DB_HOST -u DB_USER -p - Alembic Console:
docker exec -it dadly-backend /app/.venv/bin/alembic --help - View Logs:
docker-compose logs -f backend
- Fastest HMR: Run frontend locally (
npm run dev) while backend in Docker - Tailwind IntelliSense: Install VS Code extension
bradlc.vscode-tailwindcss - React DevTools: Install browser extension for component inspection
- View Logs:
docker-compose logs -f frontend(Docker mode)
# Rebuild specific service after dependency changes
docker-compose up --build backend
# Recreate containers (after environment variable changes)
docker-compose up -d --force-recreate backend
# View all container logs
docker-compose logs -f
# Execute commands inside container
docker exec -it dadly-backend bash
# Stop and remove all containers
docker-compose down
# Remove volumes (WARNING: deletes data)
docker-compose down -vSymptom: Backend fails to start, "DB_HOST not set" error
Solution:
# Verify docker-compose config
docker-compose config | grep DB_HOST
# If variables show in config but fail in container, recreate (not restart)
docker-compose down backend
docker-compose up -d backendNote: Environment changes require container recreation, not just restart.
Docker Production Mode (port 8080):
# Requires rebuild
docker-compose up --build frontendLocal Development (port 5173):
# Changes appear instantly (HMR)
cd frontend && npm run devDocker Dev Mode (port 5173):
# Hot-reload enabled
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up frontend-devSee frontend/FRONTEND_DEV_GUIDE.md for detailed setup options.
Symptom: sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (2003, "Can't connect to MySQL server")
Solutions:
- Verify MySQL is running:
mysql -h DB_HOST -u DB_USER -p - Check firewall rules (allow port 3306)
- Confirm DATABASE_URL format:
mysql+pymysql://user:pass@host:3306/dbname - Test connection:
docker exec -it dadly-backend /app/.venv/bin/python -c "from app.db.database import engine; engine.connect()"
Symptom: Feed returns 0 recipes despite populated database
Causes:
- All recipes already liked by user → Use
exclude_idsquery param - Pantry filter too restrictive → Backend prioritizes matches but shows all recipes
- Database empty → Run
fetch_spoonacular_recipes.py 100
Debug:
# Check recipe count
docker exec -it dadly-backend mysql -h DB_HOST -u DB_USER -p -e "SELECT COUNT(*) FROM recipes;"
# Check user likes
docker exec -it dadly-backend mysql -h DB_HOST -u DB_USER -p -e "SELECT COUNT(*) FROM user_recipe_interactions WHERE user_id=12;"Symptom: First request takes 30+ seconds after inactivity
Explanation: Free tier instances spin down after 15 minutes of inactivity. First request triggers wake-up.
Solutions:
- Paid Plan: Upgrade to always-on instance ($7/month)
- Keep-Alive Service: Use external service to ping health check every 10 minutes
- Accept Delay: Cold starts are expected behavior on free tier
Symptom: 401 Unauthorized after 30 minutes
Solution: Implement automatic token refresh in frontend:
// frontend/service/AxiosInstance.jsx already has interceptor
// Uncomment refresh logic in response interceptor- Fork & Clone: Fork repository, clone locally
- Create Branch:
git checkout -b feature/amazing-feature - Make Changes: Edit code, add tests
- Run Tests:
cd backend && uv run pytest tests/ -v - Commit:
git commit -m 'feat: add amazing feature' - Push:
git push origin feature/amazing-feature - Pull Request: Open PR against
mainbranch
Backend:
- Follow PEP 8 style guide
- Add docstrings to all functions
- Write pytest tests for new endpoints
- Use type hints (Pydantic schemas)
- Keep routes in appropriate
/api/*.pyfiles
Frontend:
- Use functional components + hooks
- Follow existing Tailwind utility patterns
- Keep components in
/componentsor/pages - Use axios instances (not raw fetch)
- Handle loading/error states
feat: add pantry bulk import feature
fix: resolve recipe feed pagination bug
docs: update deployment guide
test: add pantry endpoint tests
chore: update dependencies
This project is licensed under the MIT License - see the LICENSE file for details.
- Spoonacular API: Recipe data provider
- FastAPI: Modern, high-performance web framework
- React: UI library for building interactive interfaces
- Tailwind CSS: Utility-first CSS framework
- Render: Cloud hosting platform
- uv: Fast Python package installer
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Developers:
- Bayram Aliyev: aliyevbayram08@gmail.com
- Nazrin Azizli: nazizli1112@gmail.com
Built with ❤️ by the DADLY Team
Happy cooking! 🍳