10 KiB
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 fromBaseUserControllerorBaseAdminController, handle HTTP concerns, delegate to services - Services (
mealie/services/): Business logic layer, inherit fromBaseService, coordinate repos and external dependencies - Repositories (
mealie/repos/): Data access layer using SQLAlchemy, accessed viaAllRepositoriesfactory- Get repos via dependency injection:
repos: AllRepositories = Depends(get_repositories) - All repos scoped to group/household context automatically
- Get repos via dependency injection:
Route Organization:
- Routes in
mealie/routes/organized by domain (auth, recipe, groups, households, admin) - Use
APIRouterwith FastAPI dependency injection - Apply
@router.get/post/put/deletedecorators with Pydantic response models - Route controllers use
HttpRepomixin for common CRUD operations (seemealie/routes/_base/mixins.py)
Schemas & Type Generation:
- Pydantic schemas in
mealie/schema/with strict separation:*In,*Out,*Create,*Updatesuffixes - Auto-exported from submodules via
__init__.pyfiles (generated bytask 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 inmealie/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 withBase(e.g.,BaseButton) - Layout Components (
components/Layout/): Layout-only, prefix withAppif props orTheif singleton - Page Components (
components/with page prefix): Last resort for breaking up complex pages
API Client Pattern:
- API clients in
frontend/lib/api/extendBaseAPI,BaseCRUDAPI, orBaseCRUDAPIReadOnly - 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.tscomposable - Prefer composables over global state stores
Essential Commands (via Task/Taskfile.yml)
Development workflow:
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):
task dev:generate # Generate TypeScript types, schema exports, test helpers
Testing & Quality:
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:
task py:migrate -- "description" # Generate Alembic migration
task py:postgres # Run backend with PostgreSQL config
Docker:
task docker:prod # Build and run production Docker compose
Critical Development Practices
Python Backend
-
Always use
uvfor Python commands (notpythonorpip):uv run python mealie/app.py uv run pytest tests/ -
Type hints are mandatory: Use mypy-compatible annotations, handle Optional types explicitly
-
Dependency injection pattern:
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) -
Settings & Configuration:
- Get settings:
settings = get_app_settings()(cached singleton) - Get directories:
dirs = get_app_dirs() - Never instantiate
AppSettings()directly
- Get settings:
-
Testing:
- Fixtures in
tests/fixtures/ - Use
api_clientfixture for integration tests - Follow existing patterns in
tests/integration_tests/andtests/unit_tests/
- Fixtures in
Frontend
-
Run code generation after backend schema changes:
task dev:generate -
TypeScript strict mode: All code must pass type checking
-
Component naming: Follow strict conventions (see Architecture section above)
-
API calls pattern:
const api = useUserApi(); const recipe = await api.recipes.getOne(recipeId); -
Composables for shared logic: Prefer composables in
composables/over inline code duplication -
Translations: Only modify
en-USlocale 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
-
Code generation is source of truth: After Pydantic schema changes, run
task dev:generateto update:- TypeScript types (
frontend/lib/api/types/) - Schema exports (
mealie/schema/*/__init__.py) - Test data paths and routes
- TypeScript types (
-
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
-
Pre-commit hooks: Install via
task setup:py, enforces Ruff formatting/linting -
Testing before PRs: Run
task py:checkandtask ui:checkbefore submitting PRs
Pull Request Best Practices
Before Submitting a PR
- Draft PRs are optional: Create a draft PR early if you want feedback while working, or open directly as ready when complete
- Verify code generation: If you modified Pydantic schemas, ensure
task dev:generatewas run - Follow Conventional Commits: Title your PR according to the conventional commits format (see PR template)
- 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__.pyexports 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-USlocale 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__.pyfiles - 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-USlocale 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
taskcommands instead of direct tool invocation for consistency
Key Files to Reference
Taskfile.yml- All development commands and workflowsmealie/routes/_base/base_controllers.py- Controller base classes and patternsmealie/repos/repository_factory.py- Repository factory and available reposfrontend/lib/api/base/base-clients.ts- API client base classestests/conftest.py- Test fixtures and setupdev/code-generation/main.py- Code generation entry point