# Mealie Development Guide for AI Agents

## Project Overview

Mealie is a self-hosted recipe manager, meal planner, and shopping list application with a FastAPI backend (Python 3.12) and Nuxt 3 frontend (Vue 3 + TypeScript). It uses SQLAlchemy ORM with support for SQLite and PostgreSQL databases.

**Development vs Production:**
- **Development:** Frontend (port 3000) and backend (port 9000) run as separate processes
- **Production:** Frontend is statically generated and served via FastAPI's SPA module (`mealie/routes/spa/`) in a single container

## Architecture & Key Patterns

### Backend Architecture (mealie/)

**Repository-Service-Controller Pattern:**
- **Controllers** (`mealie/routes/**/controller_*.py`): Inherit from `BaseUserController` or `BaseAdminController`, handle HTTP concerns, delegate to services
- **Services** (`mealie/services/`): Business logic layer, inherit from `BaseService`, coordinate repos and external dependencies
- **Repositories** (`mealie/repos/`): Data access layer using SQLAlchemy, accessed via `AllRepositories` factory
  - Get repos via dependency injection: `repos: AllRepositories = Depends(get_repositories)`
  - All repos scoped to group/household context automatically

**Route Organization:**
- Routes in `mealie/routes/` organized by domain (auth, recipe, groups, households, admin)
- Use `APIRouter` with FastAPI dependency injection
- Apply `@router.get/post/put/delete` decorators with Pydantic response models
- Route controllers use `HttpRepo` mixin for common CRUD operations (see `mealie/routes/_base/mixins.py`)

**Schemas & Type Generation:**
- Pydantic schemas in `mealie/schema/` with strict separation: `*In`, `*Out`, `*Create`, `*Update` suffixes
- Auto-exported from submodules via `__init__.py` files (generated by `task dev:generate`)
- TypeScript types auto-generated from Pydantic schemas - **never manually edit** `frontend/lib/api/types/`

**Database & Sessions:**
- Session management via `Depends(generate_session)` in FastAPI routes
- Use `session_context()` context manager in services/scripts
- SQLAlchemy models in `mealie/db/models/`, migrations in `mealie/alembic/`
- Create migrations: `task py:migrate -- "description"`

### Frontend Architecture (frontend/)

**Component Organization (strict naming conventions):**
- **Domain Components** (`components/Domain/`): Feature-specific, prefix with domain (e.g., `AdminDashboard`)
- **Global Components** (`components/global/`): Reusable primitives, prefix with `Base` (e.g., `BaseButton`)
- **Layout Components** (`components/Layout/`): Layout-only, prefix with `App` if props or `The` if singleton
- **Page Components** (`components/` with page prefix): Last resort for breaking up complex pages

**API Client Pattern:**
- API clients in `frontend/lib/api/` extend `BaseAPI`, `BaseCRUDAPI`, or `BaseCRUDAPIReadOnly`
- Types imported from auto-generated `frontend/lib/api/types/` (DO NOT EDIT MANUALLY)
- Composables in `frontend/composables/` for shared state and API logic (e.g., `use-mealie-auth.ts`)
- Use `useAuthBackend()` for authentication state, `useMealieAuth()` for user management

**State Management:**
- Nuxt 3 composables for state (no Vuex)
- Auth state via `use-mealie-auth.ts` composable
- Prefer composables over global state stores

## Essential Commands (via Task/Taskfile.yml)

**Development workflow:**
```bash
task setup              # Install all dependencies (Python + Node)
task dev:services       # Start Postgres & Mailpit containers
task py                 # Start FastAPI backend (port 9000)
task ui                 # Start Nuxt frontend (port 3000)
task docs               # Start MkDocs documentation server
```

**Code generation (REQUIRED after schema changes):**
```bash
task dev:generate       # Generate TypeScript types, schema exports, test helpers
```

**Testing & Quality:**
```bash
task py:test            # Run pytest (supports args: task py:test -- -k test_name)
task py:check           # Format + lint + type-check + test (full validation)
task py:format          # Ruff format
task py:lint            # Ruff check
task py:mypy            # Type checking
task ui:test            # Vitest frontend tests
task ui:check           # Frontend lint + test
```

**Database:**
```bash
task py:migrate -- "description"  # Generate Alembic migration
task py:postgres        # Run backend with PostgreSQL config
```

**Docker:**
```bash
task docker:prod        # Build and run production Docker compose
```

## Critical Development Practices

### Python Backend

1. **Always use `uv` for Python commands** (not `python` or `pip`):
   ```bash
   uv run python mealie/app.py
   uv run pytest tests/
   ```

2. **Type hints are mandatory:** Use mypy-compatible annotations, handle Optional types explicitly

3. **Dependency injection pattern:**
   ```python
   from fastapi import Depends
   from mealie.repos.all_repositories import get_repositories, AllRepositories

   def my_route(
       repos: AllRepositories = Depends(get_repositories),
       user: PrivateUser = Depends(get_current_user)
   ):
       recipe = repos.recipes.get_one(recipe_id)
   ```

4. **Settings & Configuration:**
   - Get settings: `settings = get_app_settings()` (cached singleton)
   - Get directories: `dirs = get_app_dirs()`
   - Never instantiate `AppSettings()` directly

5. **Testing:**
   - Fixtures in `tests/fixtures/`
   - Use `api_client` fixture for integration tests
   - Follow existing patterns in `tests/integration_tests/` and `tests/unit_tests/`

### Frontend

1. **Run code generation after backend schema changes:** `task dev:generate`

2. **TypeScript strict mode:** All code must pass type checking

3. **Component naming:** Follow strict conventions (see Architecture section above)

4. **API calls pattern:**
   ```typescript
   const api = useUserApi();
   const recipe = await api.recipes.getOne(recipeId);
   ```

5. **Composables for shared logic:** Prefer composables in `composables/` over inline code duplication

6. **Translations:** Only modify `en-US` locale files when adding new translation strings - other locales are managed via Crowdin and **must never be modified** (PRs modifying non-English locales will be rejected)

### Cross-Cutting Concerns

1. **Code generation is source of truth:** After Pydantic schema changes, run `task dev:generate` to update:
   - TypeScript types (`frontend/lib/api/types/`)
   - Schema exports (`mealie/schema/*/__init__.py`)
   - Test data paths and routes

2. **Multi-tenancy:** All data scoped to **groups** and **households**:
   - Groups contain multiple households
   - Households contain recipes, meal plans, shopping lists
   - Repositories automatically filter by group/household context

3. **Pre-commit hooks:** Install via `task setup:py`, enforces Ruff formatting/linting

4. **Testing before PRs:** Run `task py:check` and `task ui:check` before submitting PRs

## Pull Request Best Practices

### Before Submitting a PR

1. **Draft PRs are optional:** Create a draft PR early if you want feedback while working, or open directly as ready when complete
2. **Verify code generation:** If you modified Pydantic schemas, ensure `task dev:generate` was run
3. **Follow Conventional Commits:** Title your PR according to the conventional commits format (see PR template)
4. **Add release notes:** Include user-facing changes in the PR description

### What to Review

**Architecture & Patterns:**
- Does the code follow the repository-service-controller pattern?
- Are controllers delegating business logic to services?
- Are services coordinating repositories, not accessing the database directly?
- Is dependency injection used properly (`Depends(get_repositories)`, `Depends(get_current_user)`)?

**Data Scoping:**
- Are repositories correctly scoped to group/household context?
- Do route handlers properly validate group/household ownership before operations?
- Are multi-tenant boundaries enforced (users can't access other groups' data)?

**Type Safety:**
- Are type hints present on all functions and methods?
- Are Pydantic schemas using correct suffixes (`*In`, `*Out`, `*Create`, `*Update`)?
- For frontend, does TypeScript code pass strict type checking?

**Generated Files:**
- Verify `frontend/lib/api/types/` files weren't manually edited (they're auto-generated)
- Check that `mealie/schema/*/__init__.py` exports match actual schema files (auto-generated)
- If schemas changed, confirm generated files were updated via `task dev:generate`

**Code Quality:**
- Is the code readable and well-organized?
- Are complex operations documented with clear comments?
- Do component names follow the strict naming conventions (Domain/Global/Layout/Page prefixes)?
- Are composables used for shared frontend logic instead of duplication?

**Translations:**
- Were only `en-US` locale files modified for new translation strings?
- Verify no other locale files (managed by Crowdin) were touched

**Database Changes:**
- Are Alembic migrations included for schema changes?
- Are migrations tested against both SQLite and PostgreSQL?

### Review Etiquette

- Be constructive and specific in feedback
- Suggest code examples when proposing changes
- Focus on architecture and logic - formatting/linting is handled by CI
- Use "Approve" when ready to merge, "Request Changes" for blocking issues, "Comment" for non-blocking suggestions

## Common Gotchas

- **Don't manually edit generated files:** `frontend/lib/api/types/`, schema `__init__.py` files
- **Repository context:** Repos are group/household-scoped - passing wrong IDs causes 404s
- **Session handling:** Don't create sessions manually, use dependency injection or `session_context()`
- **Schema changes require codegen:** After changing Pydantic models, run `task dev:generate`
- **Translation files:** Only modify `en-US` locale files - all other locales are managed by Crowdin
- **Dev containers:** This project uses VS Code dev containers - leverage the pre-configured environment
- **Task commands:** Use `task` commands instead of direct tool invocation for consistency

## Key Files to Reference

- `Taskfile.yml` - All development commands and workflows
- `mealie/routes/_base/base_controllers.py` - Controller base classes and patterns
- `mealie/repos/repository_factory.py` - Repository factory and available repos
- `frontend/lib/api/base/base-clients.ts` - API client base classes
- `tests/conftest.py` - Test fixtures and setup
- `dev/code-generation/main.py` - Code generation entry point

## Additional Resources

- [Documentation](https://docs.mealie.io/)
- [Contributors Guide](https://nightly.mealie.io/contributors/developers-guide/code-contributions/)
- [Discord](https://discord.gg/QuStdQGSGK)
