diff --git a/.gitignore b/.gitignore index edd672f..c33feff 100644 --- a/.gitignore +++ b/.gitignore @@ -206,4 +206,4 @@ logs/ .black_cache/ # Documentation (internal tracking) -docs/temp/ +temp/ diff --git a/docs/Usage.md b/docs/Usage.md deleted file mode 100644 index 6e76639..0000000 --- a/docs/Usage.md +++ /dev/null @@ -1,12 +0,0 @@ -# Documentation Has Moved - -This file has been split into modular documentation. Please use the new documentation structure: - -- [Getting Started](getting-started/installation.md) - Installation and quick start -- [Guides](guides/creating-projects.md) - Step-by-step guides -- [Topics](topics/internal-apps.md) - Deep dives into concepts -- [Reference](reference/cli-reference.md) - Complete API reference -- [Advanced](advanced/architecture.md) - Architecture and extending -- [Troubleshooting](troubleshooting/common-issues.md) - Common issues and solutions - -For the complete documentation, visit [Read the Docs](https://fastappkit.readthedocs.io/). diff --git a/docs/advanced/architecture.md b/docs/advanced/architecture.md index f6bd21c..079a610 100644 --- a/docs/advanced/architecture.md +++ b/docs/advanced/architecture.md @@ -1,167 +1,156 @@ # Architecture -This document describes the internal architecture of fastappkit. +Internal architecture overview of how fastappkit works. -## System Overview +## Component Overview -``` -┌───────────────────────────────────────────────────────────────┐ -│ fastappkit │ -│ (Framework + CLI + Runtime) │ -└───────────────────────────────────────────────────────────────┘ - │ │ - │ │ - ┌──────────▼──────────┐ ┌────────▼──────────┐ - │ Developer │ │ fastappkit CLI │ - │ (project author) │ │ (codegen + mgmt) │ - └──────────┬──────────┘ └────────┬──────────┘ - │ │ - ▼ ▼ - ┌───────────────────┐ ┌───────────────────┐ - │ Internal Apps │ │ External Apps │ - │ ./apps/ │ │ pip-installed │ - │ shared timeline │ │ isolated timeline │ - └──────────┬────────┘ └─────────┬─────────┘ - │ │ - ▼ ▼ - ┌─────────────┐ ┌──────────────┐ - │ FastAPI │ │ Alembic │ - │ router │ │ per-app env │ - └──────┬──────┘ └──────┬───────┘ - │ │ - └──────────┬────────────┘ - ▼ - ┌──────────────────────────┐ - │ Runtime Engine │ - │ (loader + registry + db) │ - └──────────────────────────┘ -``` - -## Components - -### CLI +fastappkit consists of several key components: -The CLI provides commands for: +- **AppLoader**: Discovers and loads apps from configuration +- **AppResolver**: Resolves app entries to actual locations +- **ManifestLoader**: Loads external app manifests +- **EntrypointLoader**: Loads and validates entrypoint functions +- **RouterAssembler**: Mounts routers to FastAPI app +- **MigrationRunner**: Executes migrations in correct order +- **MigrationOrderer**: Determines migration execution order +- **IsolationValidator**: Validates app isolation rules -- Project creation and management -- App creation and validation -- Migration management -- Development server +## App Loading Process -### Runtime Engine +### 1. Configuration Loading -The runtime engine handles: +```python +# Load fastappkit.toml +config = load_config(project_root) +apps_list = config.get("apps", []) +``` -- App discovery and loading -- Manifest validation -- Router mounting -- Migration coordination +### 2. App Resolution -### App Loader +For each app entry: -Loads apps from configuration: +```python +# Internal app: "apps.blog" +if entry.startswith("apps."): + app_name = entry.split(".", 1)[1] + app_path = project_root / "apps" / app_name + # Verify exists and has __init__.py -1. Reads `fastappkit.toml` -2. Resolves app entries (internal vs external) -3. Loads manifests (external apps) -4. Validates apps -5. Builds registry +# External app: "payments" +else: + module = importlib.import_module(entry) + # Verify importable and has manifest +``` -### App Registry +### 3. Manifest Loading -Stores metadata about loaded apps: +For external apps: -- App name and type -- Import path -- Migrations path -- Route prefix -- Manifest data +```python +# Load fastappkit.toml from package directory +manifest = load_manifest(app_info) +# Validate required fields +``` -### Migration Runner +### 4. Entrypoint Validation -Coordinates migration execution: +```python +# Validate entrypoint is importable +entrypoint = manifest.get("entrypoint", f"{app_name}:register") +load_entrypoint(entrypoint, app_info.import_path) +``` -- Core migrations (always first) -- Internal app migrations (shared timeline) -- External app migrations (isolated) +### 5. App Registration -## App Resolution +```python +# Execute register() function +router = register(app) +# Store router for mounting +``` -The loader resolves apps in this order: +## Registration Execution -1. **Check `apps.*` pattern** (Internal apps) - - Example: `apps.blog` → Internal app - - Located in `./apps/blog/` - - Must have `__init__.py` in the directory +When `FastAppKit.create_app()` is called: -2. **Try as dotted import** (Python package name - External apps) - - Example: `fastapi_blog` → `import fastapi_blog` using `importlib.import_module()` - - If successful → External app (pip-installed) - - Must have `fastappkit.toml` manifest in package directory +1. **Create FastAPI app instance** +2. **Load all apps** via `AppLoader` +3. **Execute registrations** for each app: + ```python + for app_metadata in registry.list(): + router = execute_registration(app_metadata, app) + app_metadata.router = router + ``` +4. **Mount routers** via `RouterAssembler` -3. **Fail if:** - - Folder missing (internal apps) - - No Python package root (missing `__init__.py`) - - Cannot import module (external apps) - - No manifest (external apps only) - - Cannot determine app type +## Router Assembly -## App Loading (Fail-Fast) +After all apps are registered: -If ANY app fails to load, startup aborts. +1. **Mount routers** in order from `fastappkit.toml` +2. **Check for collisions** after all mounts +3. **Emit warnings** if collisions detected (not fatal) -Error details include: +```python +for app_metadata in registry.list(): + if app_metadata.router: + prefix = app_metadata.prefix # From manifest or default + app.include_router(app_metadata.router, prefix=prefix) +``` -- App identifier (path or name) -- Stage where failure occurred (resolve/load/register) -- Manifest snapshot -- Python traceback excerpt -- Suggested fixes +## Settings Management + +### Initialization Flow + +1. **Project defines Settings** in `core/config.py` +2. **Settings initialized** in `core/app.py`: + ```python + settings = Settings() # Loads from .env + ``` +3. **FastAppKit created** with settings: + ```python + kit = FastAppKit(settings=settings) + # Calls set_settings() internally + ``` +4. **Global access** via `get_settings()`: + ```python + settings = get_settings() # Returns global instance + ``` + +### CLI Commands + +CLI commands use `ensure_settings_loaded()`: + +```python +def ensure_settings_loaded(project_root: Path) -> None: + # Imports core.app, which initializes Settings + import core.app +``` -There is no "skip this app and continue." +This ensures settings are available for migration commands. -## Settings System +## Migration System Internals -Settings flow: +### Migration Ordering -1. **Settings Definition** (in `core/config.py`) - - Uses Pydantic `BaseSettings` - - Loads from `.env` automatically +1. **Core** (always first) +2. **Internal apps** (from `fastappkit.toml` or `[tool.fastappkit.migration.order]`) +3. **External apps** (from `fastappkit.toml`) -2. **Settings Initialization** (in `core/app.py`) - - `Settings()` instance created - - `FastAppKit` initialized with settings - - `set_settings()` called (makes settings globally available) +### Version Tables -3. **Runtime Access** - - `get_settings()` retrieves global settings - - Available throughout application +- **Core + Internal**: `alembic_version` (shared) +- **External**: `alembic_version_` (per-app) -## Migration Engine Architecture +### Migration Execution -``` -┌─────────────────────────────────────────────┐ -│ Unified Migration Runner │ -└─────────────────────────────────────────────┘ - │ - ┌──────────┴──────────┐ - ▼ ▼ -CORE MIGRATIONS APP MIGRATIONS - (per app) - internal → external - shared → isolated - │ │ - └──────────┬──────────┘ - ▼ - ┌───────────────────────┐ - │ Alembic Multi-Env API │ - └───────────────────────┘ - │ - ▼ - DATABASE -``` +For each app: +1. Build Alembic config +2. Load migration scripts +3. Check database state +4. Determine upgrade path +5. Execute migrations ## Learn More -- [Migration System](../topics/migration-system.md) - Migration architecture details -- [Extending fastappkit](extending-fastappkit.md) - How to extend fastappkit +- [Extending FastAppKit](extending-fastappkit.md) - Customization guide +- [Best Practices](best-practices.md) - Recommended patterns diff --git a/docs/advanced/best-practices.md b/docs/advanced/best-practices.md index f86f52f..01ed3d1 100644 --- a/docs/advanced/best-practices.md +++ b/docs/advanced/best-practices.md @@ -1,141 +1,125 @@ # Best Practices -Guidelines for developing with fastappkit. +Recommended patterns and practices for fastappkit projects. ## Project Organization -### Directory Structure - -Follow the standard fastappkit project structure: - -``` -myproject/ -├── core/ # Core project code -│ ├── config.py # Settings -│ ├── app.py # App factory -│ ├── models.py # Core models (optional) -│ └── db/ -│ └── migrations/ # Core migrations -├── apps/ # Internal apps -│ ├── blog/ -│ └── auth/ -├── fastappkit.toml # Project config -├── .env # Environment variables -└── main.py # Entry point -``` - -### App Organization - -- Keep apps focused on a single domain -- Use clear, descriptive app names -- Follow consistent naming conventions -- Keep app code organized (models, routers, etc.) +### App Design -## Settings Management - -### Environment Variables - -- Never commit `.env` files with sensitive data -- Use environment variables for production -- Document required environment variables -- Use different `.env` files for different environments - -### Settings Validation - -- Use Pydantic validators for custom validation -- Provide sensible defaults -- Validate required settings at startup - -## Migrations - -### Migration Best Practices - -1. **Keep migrations in VCS:** Always commit migration files -2. **Test migrations:** Test upgrades and downgrades -3. **Review SQL:** Use `preview` to review SQL before applying -4. **Small migrations:** Keep migrations focused and small -5. **Reversible:** Ensure migrations can be reversed +1. **One app per feature/domain** + - Keep apps focused on a single responsibility + - Group related functionality together + - Use clear, descriptive names -### Migration Naming +2. **Clear boundaries** + - Keep app boundaries clear + - Minimize cross-app dependencies + - Use internal apps for tightly coupled features -Use descriptive migration messages: +3. **External apps for reusability** + - Design external apps to be reusable + - Keep dependencies minimal + - Document requirements clearly -```bash -fastappkit migrate app blog makemigrations -m "Add post model with title and content" -``` - -## App Development +## Migration Strategies ### Internal Apps -- Use internal apps for project-specific features -- Share models between internal apps when appropriate -- Keep apps loosely coupled - -### External Apps +1. **Keep migrations focused** + - One migration per logical change + - Clear migration messages + - Review migrations before applying -- Keep external apps truly independent -- Document dependencies clearly -- Test external apps in isolation -- Follow semantic versioning +2. **Use migration order when needed** + - Only if internal apps have dependencies + - Document dependencies clearly -## Security +3. **Test migrations** + - Test upgrades in development + - Test downgrades to ensure reversibility + - Review SQL with `fastappkit migrate preview` -### Secrets Management +### External Apps -- Never hardcode secrets -- Use environment variables for sensitive data -- Use secure secret management in production +1. **Version migrations** + - Include migrations in package + - Version migrations with app version + - Document migration requirements -### Input Validation +2. **Test independently** + - Test migrations in external app project + - Test integration with core project + - Ensure compatibility -- Validate all inputs -- Use Pydantic models for request validation -- Sanitize user inputs +## Settings Management -## Performance +1. **Use environment variables** + - Never hardcode sensitive values + - Use `.env` for development + - Use environment variables in production -### Database Queries +2. **Validate settings** + - Use Pydantic validators + - Provide clear error messages + - Set appropriate defaults -- Use proper indexing -- Avoid N+1 queries -- Use eager loading when appropriate +3. **Document custom settings** + - Document all custom settings + - Explain purpose and usage + - Provide examples -### Caching +## Testing Strategies -- Cache expensive operations -- Use appropriate cache invalidation strategies +1. **Test apps independently** + - Test external apps in isolation + - Test internal apps with core + - Test integration -## Testing +2. **Use validation** + - Run `fastappkit app validate` regularly + - Fix validation errors immediately + - Review warnings -### Test Organization +3. **Test migrations** + - Test upgrades and downgrades + - Test in development first + - Backup before production migrations -- Keep tests organized by app -- Use fixtures for common setup -- Test both unit and integration scenarios +## Performance Considerations -### Migration Testing +1. **Lazy loading** + - Apps are loaded on startup + - Minimize startup overhead + - Use async where appropriate -- Test migrations in both directions -- Test with sample data -- Test edge cases +2. **Database connections** + - Use connection pooling + - Configure appropriately for your database + - Monitor connection usage -## Documentation +3. **Route organization** + - Keep routes organized + - Use appropriate prefixes + - Avoid deep nesting -### Code Documentation +## Security -- Document public APIs -- Use type hints -- Write clear docstrings +1. **Settings security** + - Never commit `.env` files + - Use secure secret management + - Rotate secrets regularly -### User Documentation +2. **Dependency management** + - Update dependency versions regularly + - Pin versions for production + - Review security advisories -- Keep documentation up to date -- Provide examples -- Document edge cases +3. **Input validation** + - Validate all inputs + - Use Pydantic models + - Sanitize user input ## Learn More -- [Creating Apps](../guides/creating-apps.md) - App development guide -- [Migrations](../guides/migrations.md) - Migration workflows -- [Configuration](../guides/configuration.md) - Configuration guide +- [Architecture](architecture.md) - Internal architecture +- [Extending FastAppKit](extending-fastappkit.md) - Customization guide diff --git a/docs/advanced/extending-fastappkit.md b/docs/advanced/extending-fastappkit.md index 8f127b8..a5608ea 100644 --- a/docs/advanced/extending-fastappkit.md +++ b/docs/advanced/extending-fastappkit.md @@ -1,12 +1,10 @@ -# Extending fastappkit +# Extending FastAppKit -This guide covers extending fastappkit functionality. +How to customize and extend fastappkit for your needs. -## Customizing FastAppKit +## Subclassing FastAppKit -### Subclassing FastAppKit - -You can subclass `FastAppKit` to customize app creation: +Customize the FastAPI app creation: ```python from fastappkit.core.kit import FastAppKit @@ -18,7 +16,14 @@ class CustomFastAppKit(FastAppKit): app = super().create_app() # Add custom middleware - app.add_middleware(...) + from fastapi.middleware.cors import CORSMiddleware + app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) # Customize app metadata app.title = "My Custom API" @@ -26,18 +31,21 @@ class CustomFastAppKit(FastAppKit): app.description = "Custom API description" # Add exception handlers - app.add_exception_handler(...) + @app.exception_handler(ValueError) + async def value_error_handler(request, exc): + return {"error": str(exc)} return app +# Use custom class settings = Settings() kit = CustomFastAppKit(settings=settings) app = kit.create_app() ``` -### Custom App Loaders +## Custom App Loading -You can create custom app loaders: +Override app loading behavior: ```python from fastappkit.core.loader import AppLoader @@ -47,79 +55,89 @@ class CustomAppLoader(AppLoader): def load_all(self) -> AppRegistry: registry = super().load_all() - # Custom processing + # Custom logic: filter apps, modify metadata, etc. # ... return registry ``` -## Custom Validation - -### Custom Validators +## Custom Router Assembly -Create custom validators: +Customize router mounting: ```python -from fastappkit.validation.manifest import ValidationResult - -class CustomValidator: - def validate(self, manifest: dict) -> ValidationResult: - result = ValidationResult() - - # Custom validation logic - if not manifest.get("custom_field"): - result.add_error("custom_field is required") +from fastappkit.core.router import RouterAssembler +from fastappkit.core.registry import AppRegistry +from fastapi import FastAPI - return result +class CustomRouterAssembler(RouterAssembler): + def assemble(self, app: FastAPI, registry: AppRegistry) -> None: + # Custom mounting logic + for app_metadata in registry.list(): + if app_metadata.router: + # Custom prefix logic + prefix = self._custom_prefix(app_metadata) + app.include_router(app_metadata.router, prefix=prefix) ``` -## Custom Migration Handlers - -### Custom Migration Runner +## Middleware and Exception Handlers -Extend the migration runner: +Add middleware and exception handlers in `core/app.py`: ```python -from fastappkit.migrations.runner import MigrationRunner - -class CustomMigrationRunner(MigrationRunner): - def upgrade_all(self) -> None: - # Custom pre-upgrade logic - self.before_upgrade() +from core.config import Settings +from fastappkit.core.kit import FastAppKit +from fastapi import FastAPI, Request +from fastapi.responses import JSONResponse +import time - # Run standard upgrade - super().upgrade_all() +settings = Settings() +kit = FastAppKit(settings=settings) +app = kit.create_app() - # Custom post-upgrade logic - self.after_upgrade() +# Add middleware +@app.middleware("http") +async def add_process_time_header(request: Request, call_next): + start_time = time.time() + response = await call_next(request) + process_time = time.time() - start_time + response.headers["X-Process-Time"] = str(process_time) + return response + +# Add exception handlers +@app.exception_handler(ValueError) +async def value_error_handler(request: Request, exc: ValueError): + return JSONResponse( + status_code=400, + content={"error": str(exc)} + ) +``` - def before_upgrade(self) -> None: - # Custom logic - pass +## Custom Settings Access - def after_upgrade(self) -> None: - # Custom logic - pass -``` +Create custom settings accessors: -## Plugin System +```python +from fastappkit.conf import get_settings +from core.config import Settings -While fastappkit doesn't have a formal plugin system yet, you can: +def get_database_url() -> str: + """Custom accessor for database URL.""" + settings = get_settings() + return settings.database_url -1. Create external apps that extend functionality -2. Use the settings system for configuration -3. Hook into app loading via custom loaders -4. Extend migration system via custom runners +# Use in routes +from fastapi import APIRouter -## Best Practices +router = APIRouter() -1. **Follow Isolation Rules:** Ensure your extensions respect app isolation -2. **Validate Inputs:** Always validate configuration and inputs -3. **Error Handling:** Provide clear error messages -4. **Documentation:** Document your extensions thoroughly -5. **Testing:** Test extensions thoroughly +@router.get("/db-info") +def db_info(): + db_url = get_database_url() + return {"database": db_url} +``` ## Learn More -- [Architecture](architecture.md) - Understanding the system -- [Best Practices](best-practices.md) - Development best practices +- [Best Practices](best-practices.md) - Recommended patterns +- [Architecture](architecture.md) - Internal architecture diff --git a/docs/community/index.md b/docs/community/index.md index fdb2090..bdbbcbe 100644 --- a/docs/community/index.md +++ b/docs/community/index.md @@ -42,9 +42,9 @@ View the full [LICENSE](https://github.com/vanylabs/fastappkit/blob/main/LICENSE The changelog tracks all notable changes to fastappkit across versions. The overview is maintained in the repository root, while detailed release notes for each version are available in the documentation. - [Changelog Overview](https://github.com/vanylabs/fastappkit/blob/main/CHANGELOG.md) - Complete version history and release types -- [Version 0.2.1](changelog/0.2.1.md) - Fixed migration system to prevent dropping external app tables -- [Version 0.2.0](changelog/0.2.0.md) - Enhanced external app support and configuration improvements -- [Version 0.1.9](changelog/0.1.9.md) - Documentation improvements -- [Version 0.1.0](changelog/0.1.0.md) - Detailed release notes for the initial alpha release +- [Version 0.2.1](../changelog/0.2.1.md) - Fixed migration system to prevent dropping external app tables +- [Version 0.2.0](../changelog/0.2.0.md) - Enhanced external app support and configuration improvements +- [Version 0.1.9](../changelog/0.1.9.md) - Documentation improvements +- [Version 0.1.0](../changelog/0.1.0.md) - Detailed release notes for the initial alpha release -For detailed changelog entries for each version, see the [changelog section](../changelog/) in the documentation. +For detailed changelog entries for each version, see the [Changelog section](../changelog/0.2.1.md) in the documentation. diff --git a/docs/configuration/external-app-manifest.md b/docs/configuration/external-app-manifest.md new file mode 100644 index 0000000..11a9d37 --- /dev/null +++ b/docs/configuration/external-app-manifest.md @@ -0,0 +1,320 @@ +# External App Manifest + +Complete reference for external app `fastappkit.toml` manifest files. + +## File Location + +**CRITICAL**: The manifest file must be in the **package directory**, not the project root. + +**Path**: `//fastappkit.toml` + +**Example Structure:** + +``` +payments/ +├── payments/ # Package directory +│ ├── __init__.py +│ ├── models.py +│ ├── fastappkit.toml # Manifest here (in package directory) +│ └── migrations/ +├── pyproject.toml +└── README.md +``` + +**Important Notes:** + +- **NOT** in project root +- **NOT** in `pyproject.toml` (separate file) +- Must be included when package is published to PyPI +- This ensures the manifest is available when the package is installed + +## Required Fields + +### `name` + +App name (must match package name). + +**Type**: `string` +**Required**: Yes +**Example**: `"payments"` + +```toml +name = "payments" +``` + +### `version` + +Semantic version of the app. + +**Type**: `string` +**Required**: Yes +**Example**: `"0.1.0"` + +```toml +version = "0.1.0" +``` + +### `entrypoint` + +Dotted path to the register function. + +**Type**: `string` +**Required**: Yes +**Formats**: + +- `"module:function"` - Function in module (e.g., `"payments:register"`) +- `"module:Class"` - Class with `register` method (e.g., `"payments:App"`) +- Defaults to `"module:register"` if just module name + +**Examples:** + +```toml +entrypoint = "payments:register" # Function in __init__.py +entrypoint = "payments.main:register" # Function in submodule +entrypoint = "payments:App" # Class with register method +``` + +**Function Signature:** + +```python +def register(app: FastAPI) -> APIRouter | None: + """Register this app with the FastAPI application.""" + return router # or None for manual mount +``` + +### `migrations` + +Path to migrations directory (relative to package directory). + +**Type**: `string` +**Required**: Yes +**Example**: `"migrations"` + +```toml +migrations = "migrations" +``` + +This should point to the migrations directory inside the package: + +``` +payments/ +└── payments/ + └── migrations/ # This directory + ├── env.py + └── versions/ +``` + +### `models_module` + +Dotted path to models module (recommended). + +**Type**: `string` +**Required**: Recommended (not strictly required, but highly recommended) +**Example**: `"payments.models"` + +```toml +models_module = "payments.models" +``` + +This helps fastappkit locate models for autogenerate and validation. + +## Optional Fields + +### `route_prefix` + +Router mount prefix. + +**Type**: `string` +**Required**: No +**Default**: `"/"` (e.g., `"/payments"`) + +**Examples:** + +```toml +route_prefix = "/payments" # Default behavior +route_prefix = "/api/payments" # Custom prefix +route_prefix = "" # Mount at root level +``` + +**Special Values:** + +- Empty string `""` mounts at root level +- Default is `"/"` if not specified + +## Complete Example + +```toml +[tool.fastappkit] +name = "payments" +version = "0.1.0" +entrypoint = "payments:register" +migrations = "migrations" +models_module = "payments.models" +route_prefix = "/payments" +``` + +## Validation Rules + +### Required Fields Check + +All required fields must be present: + +- `name` +- `version` +- `entrypoint` +- `migrations` + +### Entrypoint Validation + +- Entrypoint must be importable (module exists) +- Entrypoint must have correct signature: `register(app: FastAPI) -> APIRouter | None` +- Function/class must exist in the specified module + +### Migrations Directory + +- Migrations directory must exist at the specified path +- Must contain `env.py` file +- Should contain `versions/` directory + +### Models Module + +- Models module must be importable (if specified) +- Should contain SQLAlchemy models + +### Version Table Check + +- `migrations/env.py` must use correct version table: `alembic_version_` +- Should NOT use shared `alembic_version` table + +## Common Mistakes + +### ❌ Manifest in Wrong Location + +**Wrong:** + +``` +myproject/ +├── fastappkit.toml # WRONG: Project config, not manifest +└── payments/ + └── payments/ + └── (no manifest) +``` + +**Correct:** + +``` +payments/ +└── payments/ + └── fastappkit.toml # CORRECT: In package directory +``` + +### ❌ Missing Required Fields + +```toml +# WRONG: Missing entrypoint +[tool.fastappkit] +name = "payments" +version = "0.1.0" +migrations = "migrations" +``` + +**Fix**: Add all required fields. + +### ❌ Incorrect Entrypoint Format + +```toml +# WRONG: Missing colon +entrypoint = "payments.register" + +# CORRECT +entrypoint = "payments:register" +``` + +### ❌ Wrong Migrations Path + +```toml +# WRONG: Absolute path or wrong relative path +migrations = "/path/to/migrations" +migrations = "../migrations" + +# CORRECT: Relative to package directory +migrations = "migrations" +``` + +### ❌ Version Table Mismatch + +In `migrations/env.py`, ensure: + +```python +# CORRECT for external app +version_table = "alembic_version_payments" + +# WRONG: Using shared table +version_table = "alembic_version" +``` + +## Manifest Fields Reference + +| Field | Type | Required | Default | Description | +| --------------- | ------ | ----------- | ------------ | -------------------------------- | +| `name` | string | Yes | - | App name (must match package) | +| `version` | string | Yes | - | Semantic version | +| `entrypoint` | string | Yes | - | Dotted path to register function | +| `migrations` | string | Yes | - | Path to migrations directory | +| `models_module` | string | Recommended | - | Dotted path to models module | +| `route_prefix` | string | No | `/` | Router mount prefix | + +## Publishing Considerations + +When publishing an external app to PyPI: + +1. **Manifest must be in package directory** - This ensures it's included in the package +2. **Migrations must be included** - Package migrations directory with the package +3. **`__init__.py` must exist** - Package directory must be a Python package + +**Package Structure for PyPI:** + +``` +payments/ +├── payments/ +│ ├── __init__.py +│ ├── models.py +│ ├── fastappkit.toml # Included in package +│ └── migrations/ # Included in package +│ ├── env.py +│ └── versions/ +├── pyproject.toml +└── README.md +``` + +**Check `pyproject.toml` includes package data:** + +```toml +[tool.poetry] +packages = [{include = "payments"}] +# or +[tool.setuptools] +packages = ["payments"] +``` + +## Validation + +Run validation to check your manifest: + +```bash +fastappkit app validate +``` + +This checks: + +- Required fields are present +- Entrypoint is importable +- Migrations directory exists +- Models module is importable (if specified) +- Version table is correct + +## Next Steps + +- [Project Configuration](project-config.md) - How to add external apps to project +- [Creating Apps](../guides/creating-apps.md) - Detailed app creation guide +- [External Apps](../topics/external-apps.md) - Understanding external apps diff --git a/docs/configuration/index.md b/docs/configuration/index.md new file mode 100644 index 0000000..072eb50 --- /dev/null +++ b/docs/configuration/index.md @@ -0,0 +1,118 @@ +# Configuration Overview + +fastappkit has three main configuration areas. This section provides complete references for each. + +## Configuration Areas + +### 1. Project Configuration (`fastappkit.toml`) + +Project-level configuration that defines which apps are loaded and migration order. + +**Location**: Project root (where you run `fastappkit` commands) + +**Key Options:** +- `apps` array - List of apps (internal and external) +- `migration.order` - Override internal app migration order + +[→ Project Configuration Reference](project-config.md) + +### 2. Settings (`core/config.py`) + +Application settings using Pydantic's `BaseSettings`. + +**Location**: `core/config.py` in your project + +**Key Features:** +- Required settings: `database_url`, `debug` +- Custom settings: Add any fields you need +- Environment variables: Loaded from `.env` automatically +- Global access: Use `get_settings()` function + +[→ Settings Configuration Guide](settings.md) + +### 3. External App Manifest (`//fastappkit.toml`) + +Metadata for external apps (pip-installable packages). + +**Location**: Package directory (`//fastappkit.toml`) + +**Key Fields:** +- `name`, `version`, `entrypoint` (required) +- `migrations`, `models_module` (required/recommended) +- `route_prefix` (optional) + +[→ External App Manifest Reference](external-app-manifest.md) + +## Quick Reference + +| Configuration | File | Location | Purpose | +|---------------|------|----------|---------| +| **Project Config** | `fastappkit.toml` | Project root | App list, migration order | +| **Settings** | `core/config.py` | Project root | Application settings | +| **External Manifest** | `fastappkit.toml` | Package directory | External app metadata | + +## Configuration Flow + +``` +1. Project loads fastappkit.toml + └─> Reads apps list + ├─> Resolves internal apps (apps.*) + └─> Resolves external apps (package import) + +2. Each app loads its configuration + ├─> Internal apps: No manifest needed + └─> External apps: Load fastappkit.toml from package + +3. Settings loaded from core/config.py + └─> Reads .env file + └─> Environment variables override .env +``` + +## Common Configuration Tasks + +### Adding an Internal App + +Edit `fastappkit.toml`: +```toml +[tool.fastappkit] +apps = [ + "apps.blog", # Added automatically by CLI, or add manually +] +``` + +### Adding an External App + +1. Install package: `pip install -e /path/to/app` +2. Edit `fastappkit.toml`: +```toml +[tool.fastappkit] +apps = [ + "external_app_name", # Package name, not path +] +``` + +### Adding Custom Settings + +Edit `core/config.py`: +```python +class Settings(BaseSettings): + database_url: str = Field(default="sqlite:///./app.db") + debug: bool = Field(default=False) + + # Add custom settings + secret_key: str = Field(default="", alias="SECRET_KEY") +``` + +### Overriding Migration Order + +Edit `fastappkit.toml`: +```toml +[tool.fastappkit.migration] +order = ["core", "auth", "blog"] # Override internal app order +``` + +## Next Steps + +- [Project Configuration](project-config.md) - Complete `fastappkit.toml` reference +- [Settings Configuration](settings.md) - Complete `core/config.py` guide +- [External App Manifest](external-app-manifest.md) - Manifest format reference diff --git a/docs/configuration/project-config.md b/docs/configuration/project-config.md new file mode 100644 index 0000000..f9c72f0 --- /dev/null +++ b/docs/configuration/project-config.md @@ -0,0 +1,261 @@ +# Project Configuration + +Complete reference for `fastappkit.toml` project configuration. + +## File Location + +**Location**: Project root (where you run `fastappkit` commands) + +The file must be named `fastappkit.toml` and located in the project root directory. fastappkit looks for this file when running commands. + +## Basic Structure + +```toml +[tool.fastappkit] +apps = [ + "apps.blog", # Internal app + "apps.auth", # Internal app + "fastapi_payments", # External app (pip-installed package) +] +``` + +## Configuration Options + +### `apps` Array (Required) + +List of apps to load. This is the only required configuration. + +**Type**: `array[string]` +**Required**: Yes +**Default**: `[]` (empty array) + +#### Internal App Format + +Internal apps use the `apps.` pattern: + +```toml +[tool.fastappkit] +apps = [ + "apps.blog", + "apps.auth", +] +``` + +- Resolves to `./apps//` directory +- Must exist in project's `apps/` directory +- Must have `__init__.py` file +- Automatically added by CLI when creating apps + +#### External App Format + +External apps use the package name: + +```toml +[tool.fastappkit] +apps = [ + "fastapi_payments", + "my_custom_app", +] +``` + +- Must be pip-installed (importable via `importlib`) +- **CRITICAL**: Cannot use filesystem paths +- For local development: `pip install -e /path/to/app` +- Package name must match what you can `import` in Python + +#### App Resolution Order + +fastappkit resolves apps in this order: + +1. **Check `apps.*` pattern first** - If entry starts with `apps.`, treat as internal app +2. **Try as package import** - Otherwise, try to import as Python package + +This means `apps.blog` will always be treated as an internal app, even if a package named `apps.blog` exists. + +#### Examples + +```toml +[tool.fastappkit] +apps = [ + # Internal apps + "apps.blog", + "apps.auth", + "apps.payments", + + # External apps (pip-installed) + "fastapi_admin", + "fastapi_cache", +] +``` + +### `migration.order` (Optional) + +Override the order in which internal app migrations are applied. + +**Type**: `array[string]` +**Required**: No +**Default**: Order from `apps` array + +```toml +[tool.fastappkit.migration] +order = ["core", "auth", "blog"] +``` + +**Important Notes:** + +- Only affects **internal apps** (core always runs first) +- External apps are not included (they run after internal apps, in `apps` order) +- If not specified, uses order from `apps` array +- `"core"` is a special value for core migrations (always first) + +**When to Use:** + +- Internal apps have dependencies (e.g., `blog` depends on `auth`) +- You want explicit control over migration order +- App order in `apps` array doesn't match dependency order + +**Example:** + +```toml +[tool.fastappkit] +apps = [ + "apps.blog", + "apps.auth", # blog depends on auth, but listed first +] + +[tool.fastappkit.migration] +order = ["core", "auth", "blog"] # Override: auth before blog +``` + +## Complete Example + +```toml +[tool.fastappkit] +apps = [ + # Internal apps (project-specific) + "apps.blog", + "apps.auth", + "apps.payments", + + # External apps (pip-installed packages) + "fastapi_admin", + "fastapi_cache", +] + +[tool.fastappkit.migration] +order = ["core", "auth", "blog", "payments"] +``` + +## Validation Rules + +### App Entry Validation + +- **Internal apps**: Must exist in `./apps//` with `__init__.py` +- **External apps**: Must be importable (pip-installed) +- **No duplicates**: Duplicate app names are detected and warned + +### Common Mistakes + +#### ❌ Using Filesystem Path for External App + +```toml +# WRONG +apps = [ + "/path/to/external/app", # Not supported +] +``` + +**Fix**: Install package first, then use package name: +```bash +pip install -e /path/to/external/app +``` + +```toml +# CORRECT +apps = [ + "external_app_name", # Package name +] +``` + +#### ❌ Duplicate App Names + +```toml +# WARNING: Both resolve to same name "blog" +apps = [ + "apps.blog", + "myproject.blog", # If this package exists, both resolve to "blog" +] +``` + +**Fix**: Rename one of the apps or use different names. + +#### ❌ Missing `apps` Array + +```toml +[tool.fastappkit] +# Missing apps array - will error +``` + +**Fix**: Always include `apps` array (can be empty `[]`). + +#### ❌ Incorrect App Entry Format + +```toml +# WRONG +apps = [ + "blog", # Missing "apps." prefix for internal app +] + +# CORRECT for internal app +apps = [ + "apps.blog", +] + +# CORRECT for external app (if package is named "blog") +apps = [ + "blog", # OK if it's an external package +] +``` + +## Best Practices + +1. **Keep apps list organized**: Group internal apps together, then external apps +2. **Use migration.order when needed**: Only if internal apps have dependencies +3. **Validate apps**: Run `fastappkit app list` to verify all apps are resolved +4. **Document external apps**: Note which external apps are used and their versions +5. **Version control**: Commit `fastappkit.toml` to version control + +## Troubleshooting + +### App Not Found + +**Error**: `AppLoadError: Could not resolve app entry` + +**Solutions:** +- For internal apps: Check `apps//` exists with `__init__.py` +- For external apps: Ensure package is pip-installed (`pip install -e .`) +- Verify app entry format matches app type + +### Duplicate App Names + +**Warning**: `Duplicate app names detected` + +**Solutions:** +- Rename one of the apps +- Check if multiple entries resolve to same name +- Use `fastappkit app list` to see resolved names + +### Migration Order Issues + +**Problem**: Migrations fail due to dependency order + +**Solutions:** +- Use `[tool.fastappkit.migration.order]` to specify order +- Ensure dependencies are listed before dependents +- Core always runs first (no need to specify) + +## Next Steps + +- [Settings Configuration](settings.md) - Configure application settings +- [External App Manifest](external-app-manifest.md) - External app configuration +- [Creating Projects](../guides/creating-projects.md) - Project setup guide diff --git a/docs/configuration/settings.md b/docs/configuration/settings.md new file mode 100644 index 0000000..9f0602a --- /dev/null +++ b/docs/configuration/settings.md @@ -0,0 +1,412 @@ +# Settings Configuration + +Complete guide to configuring application settings in `core/config.py`. + +## Overview + +Settings are defined in `core/config.py` using Pydantic's `BaseSettings`. They automatically load from `.env` files and environment variables. + +## Settings Class Structure + +### Basic Settings + +```python +from pydantic import Field +from pydantic_settings import BaseSettings, SettingsConfigDict + +class Settings(BaseSettings): + """ + Application settings. + + Loads from .env file automatically. + """ + + database_url: str = Field(default="sqlite:///./app.db") + debug: bool = Field(default=False) + + model_config = SettingsConfigDict( + env_file=".env", + env_file_encoding="utf-8", + populate_by_name=True + ) +``` + +## Required Settings + +Settings must implement `SettingsProtocol`, which requires: + +### `database_url: str` + +Database connection string. + +**Type**: `str` +**Required**: Yes (by protocol) +**Default**: `"sqlite:///./app.db"` (in scaffolded code) +**Environment Variable**: `DATABASE_URL` + +**Examples:** +```python +# SQLite +database_url: str = Field(default="sqlite:///./app.db") + +# PostgreSQL +database_url: str = Field(default="postgresql://user:password@localhost:5432/mydb") + +# MySQL +database_url: str = Field(default="mysql://user:password@localhost:3306/mydb") +``` + +### `debug: bool` + +Debug mode flag. + +**Type**: `bool` +**Required**: Yes (by protocol) +**Default**: `False` +**Environment Variable**: `DEBUG` + +**Examples:** +```python +debug: bool = Field(default=False) +``` + +## Adding Custom Settings + +### Basic Fields + +Add any fields you need: + +```python +class Settings(BaseSettings): + database_url: str = Field(default="sqlite:///./app.db") + debug: bool = Field(default=False) + + # Custom settings + secret_key: str = Field(default="change-me", alias="SECRET_KEY") + api_key: str = Field(default="", alias="API_KEY") + max_upload_size: int = Field(default=10485760, alias="MAX_UPLOAD_SIZE") + + model_config = SettingsConfigDict( + env_file=".env", + env_file_encoding="utf-8", + populate_by_name=True + ) +``` + +### Field Validation + +Use Pydantic validators: + +```python +from pydantic import Field, field_validator + +class Settings(BaseSettings): + database_url: str = Field(default="sqlite:///./app.db") + debug: bool = Field(default=False) + + port: int = Field(default=8000, alias="PORT") + + @field_validator('port') + @classmethod + def validate_port(cls, v: int) -> int: + if not (1 <= v <= 65535): + raise ValueError('Port must be between 1 and 65535') + return v + + model_config = SettingsConfigDict( + env_file=".env", + env_file_encoding="utf-8", + populate_by_name=True + ) +``` + +### Nested Settings + +Use Pydantic models for nested configuration: + +```python +from pydantic import BaseModel, Field + +class DatabaseConfig(BaseSettings): + host: str = "localhost" + port: int = 5432 + name: str = "mydb" + user: str = "postgres" + password: str = "" + +class Settings(BaseSettings): + database_url: str = Field(default="sqlite:///./app.db") + debug: bool = Field(default=False) + + # Nested settings + database: DatabaseConfig = DatabaseConfig() + + model_config = SettingsConfigDict( + env_file=".env", + env_file_encoding="utf-8", + populate_by_name=True + ) +``` + +### Environment Variable Aliases + +Use `alias` parameter to map to environment variables: + +```python +class Settings(BaseSettings): + database_url: str = Field( + default="sqlite:///./app.db", + alias="DATABASE_URL" # Maps to DATABASE_URL env var + ) + debug: bool = Field( + default=False, + alias="DEBUG" # Maps to DEBUG env var + ) + + model_config = SettingsConfigDict( + env_file=".env", + populate_by_name=True # Allows both field name and alias + ) +``` + +With `populate_by_name=True`, you can use either: +- Field name: `database_url` or `DATABASE_URL` in `.env` +- Alias: `DATABASE_URL` in environment variables + +## `.env` File + +### Location + +The `.env` file should be in the **project root** (same directory as `fastappkit.toml`). + +### Format + +```bash +# Database Configuration +DATABASE_URL=sqlite:///./myproject.db + +# Development Settings +DEBUG=false + +# Custom Settings +SECRET_KEY=your-secret-key-here +API_KEY=your-api-key +MAX_UPLOAD_SIZE=10485760 +``` + +### Variable Naming + +- Use **uppercase with underscores** (e.g., `DATABASE_URL`) +- Match the `alias` in Field definitions +- No spaces around `=` + +### Precedence + +Settings are loaded in this order (highest to lowest priority): + +1. **Environment variables** (highest priority) +2. **`.env` file** +3. **Default values** in `Field()` (lowest priority) + +**Example:** +```bash +# .env file +DATABASE_URL=sqlite:///./app.db + +# Environment variable (overrides .env) +export DATABASE_URL=postgresql://localhost/mydb +``` + +## Accessing Settings + +### Global Accessor (Django-like) + +Use `get_settings()` function: + +```python +from fastappkit.conf import get_settings + +settings = get_settings() +db_url = settings.database_url +is_debug = settings.debug +``` + +### FastAPI Dependency Injection + +Use as a dependency: + +```python +from fastappkit.conf import get_settings +from fastapi import Depends +from core.config import Settings + +@app.get("/config") +def get_config(settings: Settings = Depends(get_settings)): + return { + "debug": settings.debug, + "database": settings.database_url + } +``` + +## Settings Initialization + +### When Settings Are Loaded + +Settings are initialized in `core/app.py` when the module is imported: + +```python +# core/app.py +from core.config import Settings +from fastappkit.core.kit import FastAppKit + +# Initialize FastAppKit with project's Settings +settings = Settings() # Loads from .env automatically +kit = FastAppKit(settings=settings) +app = kit.create_app() +``` + +### How FastAppKit Uses Settings + +When `FastAppKit` is initialized, it calls `set_settings()` which makes settings globally available via `get_settings()`. + +### CLI Commands and Settings + +CLI commands use `ensure_settings_loaded(project_root)` which imports `core.app`, causing settings to be initialized automatically. + +**CRITICAL**: Settings must be initialized in `core/app.py` before using `FastAppKit`. Without this, `get_settings()` will fail at runtime. + +## Complete Examples + +### Minimal Settings + +```python +from pydantic import Field +from pydantic_settings import BaseSettings, SettingsConfigDict + +class Settings(BaseSettings): + database_url: str = Field(default="sqlite:///./app.db") + debug: bool = Field(default=False) + + model_config = SettingsConfigDict( + env_file=".env", + env_file_encoding="utf-8", + populate_by_name=True + ) +``` + +### Extended Settings with Validation + +```python +from pydantic import Field, field_validator +from pydantic_settings import BaseSettings, SettingsConfigDict + +class Settings(BaseSettings): + database_url: str = Field(default="sqlite:///./app.db") + debug: bool = Field(default=False) + + # Custom settings + secret_key: str = Field(default="change-me", alias="SECRET_KEY") + host: str = Field(default="127.0.0.1", alias="HOST") + port: int = Field(default=8000, alias="PORT") + redis_url: str = Field(default="redis://localhost:6379", alias="REDIS_URL") + + @field_validator('port') + @classmethod + def validate_port(cls, v: int) -> int: + if not (1 <= v <= 65535): + raise ValueError('Port must be between 1 and 65535') + return v + + model_config = SettingsConfigDict( + env_file=".env", + env_file_encoding="utf-8", + populate_by_name=True, + extra="ignore", # Ignore extra fields from .env + ) +``` + +### Nested Settings + +```python +from pydantic import BaseModel, Field +from pydantic_settings import BaseSettings, SettingsConfigDict + +class EmailConfig(BaseModel): + smtp_host: str = "smtp.gmail.com" + smtp_port: int = 587 + smtp_user: str = "" + smtp_password: str = "" + from_email: str = "noreply@example.com" + +class Settings(BaseSettings): + database_url: str = Field(default="sqlite:///./app.db") + debug: bool = Field(default=False) + + # Nested settings + email: EmailConfig = EmailConfig() + + model_config = SettingsConfigDict( + env_file=".env", + env_file_encoding="utf-8", + populate_by_name=True + ) +``` + +## Settings Options Reference + +| Setting | Type | Required | Default | Env Var | Description | +|---------|------|----------|---------|---------|-------------| +| `database_url` | str | Yes | `sqlite:///./app.db` | `DATABASE_URL` | Database connection string | +| `debug` | bool | Yes | `False` | `DEBUG` | Debug mode flag | + +## Common Custom Settings Examples + +- **`secret_key`**: Secret key for encryption/signing +- **`host` / `port`**: Server host and port +- **`redis_url`**: Redis connection string +- **`email_settings`**: Nested email configuration +- **`logging_level`**: Logging verbosity +- **`cors_origins`**: CORS allowed origins +- **`api_rate_limit`**: Rate limiting configuration + +## Common Issues + +### Settings Not Initialized + +**Error**: Runtime errors when calling `get_settings()` + +**Solution**: Ensure `core/app.py` initializes Settings and FastAppKit: +```python +settings = Settings() +kit = FastAppKit(settings=settings) +app = kit.create_app() +``` + +### Missing `.env` File + +**Problem**: Settings use defaults, may fail if no default for required field + +**Solution**: Create `.env` file in project root with required settings + +### Wrong `DATABASE_URL` + +**Error**: Database connection errors + +**Solution**: +- Verify `DATABASE_URL` in `.env` file +- Check database is running and accessible +- Verify connection string format + +### External App Using Wrong `DATABASE_URL` + +**Problem**: External app uses its own `.env` when developing independently, but should use core's when integrated + +**Solution**: +- When developing independently: Use external app's `.env` +- When integrated: External app uses core project's `DATABASE_URL` (from core's `.env`) + +## Next Steps + +- [Project Configuration](project-config.md) - `fastappkit.toml` reference +- [External App Manifest](external-app-manifest.md) - External app configuration +- [Creating Projects](../guides/creating-projects.md) - Project setup guide diff --git a/docs/getting-started/core-concepts.md b/docs/getting-started/core-concepts.md index 105a16c..216dd5f 100644 --- a/docs/getting-started/core-concepts.md +++ b/docs/getting-started/core-concepts.md @@ -52,64 +52,112 @@ myproject/ ``` !!! note "Core Models" -Core models are optional and separate from apps. They don't require creating an app. Place all your core models in `core/models.py`. Core models are for shared infrastructure tables (sessions, logs, queues, etc.) that are used across the entire project, not app-specific features (use internal apps for those). + Core models are optional and separate from apps. They don't require creating an app. Place all your core models in `core/models.py`. Core models are for shared infrastructure tables (sessions, logs, queues, etc.) that are used across the entire project, not app-specific features (use internal apps for those). -## App Loading Flow +## How Apps Are Discovered and Loaded -The app loading process follows these steps: +1. **Configuration**: Apps are listed in `fastappkit.toml`: + ```toml + [tool.fastappkit] + apps = [ + "apps.blog", # Internal app + "fastapi_payments", # External app (pip-installed) + ] + ``` -``` -┌──────────────────────────────────────────────┐ -│ fastappkit Runtime │ -└──────────────────────────────────────────────┘ - │ - ▼ - ┌────────────────┐ - │ Read config │ from fastappkit.toml - └────────────────┘ - │ - ▼ - ┌───────────────────┐ - │ Discover apps │ - │ - internal dirs │ - │ - pip pkgs │ - └───────────────────┘ - │ - ▼ - ┌────────────────────┐ - │ Validate manifests │ - │ + isolation rules │ - └────────────────────┘ - │ - ▼ - ┌──────────────────────────┐ - │ Build App Registry │ - │ - name │ - │ - type (internal/ext) │ - │ - metadata │ - │ - routes/models/etc │ - └──────────────────────────┘ - │ - ▼ - ┌──────────────────────────┐ - │ Mount routers │ - └──────────────────────────┘ -``` +2. **Resolution**: fastappkit resolves each entry: + - `apps.*` pattern → Internal app (checks `./apps//`) + - Package name → External app (imports via `importlib`) + +3. **Loading**: For each app: + - Load manifest (external apps only) + - Validate structure and isolation + - Execute `register()` function + - Mount router (if returned) + +4. **Registration**: Apps provide a `register(app: FastAPI)` function that: + - Can return `APIRouter` (auto-mounted) + - Can return `None` (manual mount) + - Receives the FastAPI app instance + +## Migration System Overview + +fastappkit provides unified migration management: + +### Core Migrations + +- Located in `core/db/migrations/` +- For core-level schema changes +- Uses `alembic_version` table + +### Internal App Migrations + +- **Shared** with core migrations (same directory: `core/db/migrations/`) +- All internal apps use the same migration timeline +- Autogenerate sees all internal app models + core models +- Uses shared `alembic_version` table + +### External App Migrations + +- **Isolated** in each app's package directory (`//migrations/`) +- Each external app has its own migration timeline +- Autogenerate sees only that app's models +- Uses per-app version table (`alembic_version_`) + +### Migration Order + +Migrations run in this order: + +1. **Core** (always first) +2. **Internal apps** (order from `fastappkit.toml` or `[tool.fastappkit.migration.order]`) +3. **External apps** (order from `fastappkit.toml`) + +## Router Mounting Basics + +Apps can provide routers in two ways: + +1. **Auto-mount**: Return `APIRouter` from `register()` function + - fastappkit mounts it with prefix from manifest (`route_prefix`) + - Default prefix: `/` + - Can be empty string `""` to mount at root + +2. **Manual mount**: Return `None` from `register()` function + - App handles mounting itself in `register()` + - More control over mounting logic + +Route collisions are detected and warnings emitted (not fatal). + +## Settings System Basics + +Settings are defined in `core/config.py` using Pydantic's `BaseSettings`: + +- **Required settings**: `database_url` and `debug` (from `SettingsProtocol`) +- **Custom settings**: Add any fields you need +- **Environment variables**: Loaded from `.env` file automatically +- **Global access**: Use `get_settings()` function (Django-like) +- **Initialization**: Settings are initialized in `core/app.py` when `FastAppKit` is created + +Settings are loaded in this order: -!!! warning "Fail-Fast Behavior" -If ANY app fails to load, the entire application startup aborts. This ensures that configuration errors are caught early rather than causing runtime issues. +1. Environment variables (highest priority) +2. `.env` file +3. Default values in `Field()` (lowest priority) -## Key Features +## Key Differences: Internal vs External Apps -- **Clean project structure** with automatic app discovery -- **Shared migrations** for internal apps, isolated migrations for external apps -- **Automatic router mounting** with configurable prefixes -- **Fail-fast validation** and error handling -- **Support for both monorepo and multi-repo workflows** +| Feature | Internal Apps | External Apps | +|---------|---------------|---------------| +| **Location** | `./apps//` | Pip-installed package | +| **Base Class** | `core.models.Base` | Own `Base` (DeclarativeBase) | +| **Migrations** | Shared with core | Isolated per app | +| **Dependencies** | Can import from core/internal | Cannot import from core/internal | +| **Manifest** | Not required | Required (`fastappkit.toml`) | +| **Development** | Part of project | Independent (can be separate repo) | +| **Publishing** | Not meant for PyPI | Can publish to PyPI | -## Learn More +## Next Steps -- [Internal Apps](../topics/internal-apps.md) - Deep dive into internal apps -- [External Apps](../topics/external-apps.md) - Understanding external apps -- [Migration System](../topics/migration-system.md) - How migrations work -- [App Isolation](../topics/app-isolation.md) - Isolation rules and constraints +- [Usage Scenarios](../usage/index.md) - Choose your development approach +- [Creating Projects](../guides/creating-projects.md) - Detailed project setup +- [Creating Apps](../guides/creating-apps.md) - Detailed app creation +- [Configuration](../configuration/index.md) - All configuration options diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index 9031449..d10bd47 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -2,6 +2,11 @@ fastappkit requires Python 3.11 or higher. It is tested with Python 3.11 and 3.12. +## Prerequisites + +- Python 3.11 or higher +- pip or Poetry for package management + ## Install with pip ```bash @@ -22,11 +27,11 @@ After installation, verify that fastappkit is correctly installed: fastappkit --version ``` -This should display the installed version number. +This should display the installed version number (e.g., `fastappkit 0.2.1`). ## Requirements -fastappkit has the following dependencies: +fastappkit has the following dependencies (automatically installed): - Python 3.11+ (tested with Python 3.11 and 3.12) - FastAPI 0.120.0+ @@ -50,3 +55,35 @@ fastappkit --version ``` See the [Contributing Guide](../community/index.md#contributing) for more details on setting up a development environment. + +## Troubleshooting + +### Installation Fails + +If installation fails, check: + +- Python version: `python --version` (must be 3.11+) +- pip/poetry is up to date: `pip install --upgrade pip` or `poetry self update` +- Virtual environment is activated (if using one) + +### Command Not Found + +If `fastappkit` command is not found after installation: + +- Ensure the Python environment where fastappkit was installed is in your PATH +- If using a virtual environment, activate it: `source venv/bin/activate` (Linux/Mac) or `venv\Scripts\activate` (Windows) +- If using Poetry, use `poetry run fastappkit` or activate the Poetry shell: `poetry shell` + +### Version Check Fails + +If `fastappkit --version` shows an error: + +- Verify installation: `pip show fastappkit` or `poetry show fastappkit` +- Reinstall if needed: `pip install --upgrade fastappkit` + +## Next Steps + +Once installed, proceed to: + +- [Quick Start](quickstart.md) - Create your first project +- [Core Concepts](core-concepts.md) - Understand how fastappkit works diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md index b552923..955f307 100644 --- a/docs/getting-started/quickstart.md +++ b/docs/getting-started/quickstart.md @@ -18,6 +18,7 @@ myproject/ ├── core/ │ ├── config.py # Settings (loads from .env) │ ├── app.py # create_app() factory +│ ├── models.py # SQLAlchemy Base class │ └── db/ │ └── migrations/ # Core migrations ├── apps/ # Internal apps directory @@ -58,9 +59,40 @@ fastappkit core dev Your API is now running at `http://127.0.0.1:8000` with routes mounted at `/blog/`. +## What Gets Created + +### Project Structure + +- **`core/`**: Core application code + - `config.py`: Settings class (loads from `.env`) + - `app.py`: FastAPI app factory + - `models.py`: SQLAlchemy Base class + - `db/migrations/`: Migration directory +- **`apps/`**: Internal apps directory +- **`fastappkit.toml`**: Project configuration (apps list) +- **`.env`**: Environment variables (auto-created) +- **`main.py`**: Application entry point + +### App Structure + +- **`apps/blog/`**: Your first app + - `__init__.py`: Registration function + - `models.py`: SQLAlchemy models + - `router.py`: FastAPI routes + ## Next Steps -- Read the [Core Concepts](core-concepts.md) to understand how fastappkit works +Now that you have a working project: + +- Read [Core Concepts](core-concepts.md) to understand how fastappkit works - Follow the [Creating Apps Guide](../guides/creating-apps.md) for detailed app development - Explore the [Migration System](../topics/migration-system.md) for database management - Check the [CLI Reference](../reference/cli-reference.md) for all available commands +- See [Usage Scenarios](../usage/index.md) for different development approaches + +## Important Notes + +- **All commands must be run from project root** (where `fastappkit.toml` is located) +- **Update dependency versions** in `pyproject.toml` from `*` to specific ranges for production +- **Configure `.env`** file with your `DATABASE_URL` and other settings +- Internal apps are automatically added to `fastappkit.toml` by the CLI diff --git a/docs/guides/configuration.md b/docs/guides/configuration.md deleted file mode 100644 index b24472f..0000000 --- a/docs/guides/configuration.md +++ /dev/null @@ -1,259 +0,0 @@ -# Configuration - -This guide covers configuring fastappkit projects and apps. - -## Project Configuration - -fastappkit uses `fastappkit.toml` for project configuration. This is separate from `pyproject.toml`. - -### Basic Configuration - -```toml -[tool.fastappkit] -apps = [ - "apps.blog", # Internal app - "apps.auth", # Internal app - "fastapi_payments", # External app (pip-installed package) -] -``` - -### App Entry Formats - -- `apps.` → Internal app (located in `./apps//`) -- `` → External app (pip-installed package, must be importable) - -### Migration Order - -You can override internal app order: - -```toml -[tool.fastappkit.migration] -order = ["core", "auth", "blog"] -``` - -## Settings - -Settings are defined in `core/config.py` using Pydantic's `BaseSettings`: - -```python -from pydantic import Field -from pydantic_settings import BaseSettings, SettingsConfigDict - -class Settings(BaseSettings): - """ - Application settings. - - Loads from .env file automatically. - """ - - database_url: str = Field( - default="sqlite:///./app.db", - alias="DATABASE_URL" - ) - debug: bool = Field( - default=False, - alias="DEBUG" - ) - - model_config = SettingsConfigDict( - env_file=".env", - env_file_encoding="utf-8", - populate_by_name=True - ) -``` - -### Extending Settings - -The scaffolded code includes minimal settings. You can extend it by adding more fields: - -```python -from pydantic import Field - -class Settings(BaseSettings): - database_url: str = Field( - default="sqlite:///./app.db", - alias="DATABASE_URL" - ) - debug: bool = Field( - default=False, - alias="DEBUG" - ) - - # Add your custom settings here - secret_key: str = Field( - default="change-me-in-production", - alias="SECRET_KEY" - ) - host: str = Field(default="127.0.0.1", alias="HOST") - port: int = Field(default=8000, alias="PORT") - - model_config = SettingsConfigDict( - env_file=".env", - env_file_encoding="utf-8", - populate_by_name=True, - extra="ignore", # Ignore extra fields from .env - ) -``` - -### Customization Options - -- **Add Custom Settings:** Add fields to `Settings` class with `Field()` instances -- **Validation:** Use Pydantic validators: `@field_validator('database_url')` -- **Default Values:** Set defaults in `Field()` instances -- **Nested Settings:** Use Pydantic models for nested configuration -- **Environment-Specific:** Override in `.env` or via environment variables (uppercase names are supported via aliases) - -### Accessing Settings - -**Runtime accessor (Django-like):** - -```python -from fastappkit.conf import get_settings - -settings = get_settings() -db_url = settings.database_url -``` - -**FastAPI dependency injection:** - -```python -from fastappkit.conf import get_settings -from fastapi import Depends -from core.config import Settings - -def handler(settings: Settings = Depends(get_settings)): - return settings.debug -``` - -### Environment Variables - -The `.env` file is auto-scaffolded and automatically loaded: - -```bash -DATABASE_URL=sqlite:///./myproject.db -DEBUG=false -``` - -You can add additional environment variables as needed for your custom settings. - -### Settings Initialization - -Settings are initialized in `core/app.py` when the module is imported: - -```python -# core/app.py (generated by fastappkit) -from core.config import Settings -from fastappkit.core.kit import FastAppKit - -# Initialize FastAppKit with project's Settings -# This sets settings globally via set_settings() in FastAppKit.__init__ -settings = Settings() # Loads from .env automatically via BaseSettings -kit = FastAppKit(settings=settings) -app = kit.create_app() -``` - -The `Settings()` class automatically loads from `.env` via Pydantic's `BaseSettings`. When `FastAppKit` is initialized, it calls `set_settings()` which makes the settings globally available via `get_settings()`. - -**For CLI/Migration Commands:** - -CLI commands use `ensure_settings_loaded(project_root)` which imports `core.app`, causing the settings to be initialized automatically. - -### FastAPI App Customization - -You can customize the FastAPI app by modifying `core/app.py` or by subclassing `FastAppKit`: - -**Option 1: Modify core/app.py directly** - -```python -# core/app.py -from core.config import Settings -from fastappkit.core.kit import FastAppKit - -settings = Settings() -kit = FastAppKit(settings=settings) -app = kit.create_app() - -# Customize FastAPI app -app.title = "My Custom API" -app.version = "1.0.0" -app.description = "Custom API description" -``` - -**Option 2: Subclass FastAppKit** - -```python -from fastappkit.core.kit import FastAppKit -from fastapi import FastAPI -from core.config import Settings - -class CustomFastAppKit(FastAppKit): - def create_app(self) -> FastAPI: - app = super().create_app() - # Add custom middleware, exception handlers, etc. - app.add_middleware(...) - app.title = "My Custom API" - return app - -settings = Settings() -kit = CustomFastAppKit(settings=settings) -app = kit.create_app() -``` - -## External App Manifest - -External apps must declare metadata in `fastappkit.toml` located inside the package directory. - -**Location:** `//fastappkit.toml` - -**Example:** - -```toml -name = "blog" -version = "0.1.0" -entrypoint = "blog:register" -migrations = "migrations" -models_module = "blog.models" -route_prefix = "/blog" -``` - -!!! important "Manifest Requirements" -The manifest file is `fastappkit.toml`, not `pyproject.toml`. It must be located in the package directory (where `__init__.py` is). This ensures it's included when the package is published to PyPI. No fallback - `fastappkit.toml` is required for external apps. - -See the [Manifest Reference](../reference/manifest-reference.md) for complete details. - -## Dependency Versions - -!!! warning "Default Dependency Versions" -When creating a new project or external app, dependency versions in `pyproject.toml` are set to `*` (any version) by default. This provides maximum flexibility but may lead to compatibility issues. - -### Recommendations - -**For Production Projects:** - -- Update dependency versions to specific ranges (e.g., `>=0.120.0,<0.130`) -- Pin exact versions for critical dependencies -- Test thoroughly after updating versions - -**For External Apps:** - -- Match dependency versions with the core project for compatibility -- Use compatible version ranges that work with the core project's versions -- Document minimum required versions in your app's README - -**Example:** - -```toml -[tool.poetry.dependencies] -python = ">=3.11,<4.0" -fastapi = ">=0.120.0,<0.130" # Specific range instead of * -sqlalchemy = ">=2.0,<3.0" -alembic = ">=1.17.2,<1.18" -``` - -!!! note "CLI Warning" -The CLI will display a warning message when creating projects or external apps, reminding you to update dependency versions according to your needs. - -## Learn More - -- [Configuration Reference](../reference/configuration-reference.md) - Complete configuration options -- [Manifest Reference](../reference/manifest-reference.md) - External app manifest schema diff --git a/docs/guides/creating-apps.md b/docs/guides/creating-apps.md index d2acd5b..a19a903 100644 --- a/docs/guides/creating-apps.md +++ b/docs/guides/creating-apps.md @@ -1,39 +1,33 @@ # Creating Apps -This guide covers creating and managing apps in fastappkit projects. +Detailed guide to creating internal and external apps. ## Internal Apps -Internal apps are project-specific modules that live in your project's `apps/` directory. - -### Create an Internal App +### Command ```bash fastappkit app new ``` -This command must be run from the project root directory (where `fastappkit.toml` is located). +**IMPORTANT**: Must be run from project root (where `fastappkit.toml` is located). ### What Gets Created ``` apps// -├── __init__.py # register(app: FastAPI) function -├── models.py # SQLAlchemy models -└── router.py # FastAPI routers +├── __init__.py # register() function +├── models.py # SQLAlchemy models (imports Base from core.models) +└── router.py # FastAPI router ``` -!!! important "Migration Directory" -Internal apps do NOT have their own `migrations/` directory. They use the core's migration directory (`core/db/migrations/`). - -### Entrypoint Patterns +### Structure Explanation -fastappkit supports two patterns for registering routers: +#### `apps//__init__.py` -#### Pattern 1: Return Router (Recommended) +Contains the `register()` function: ```python -# apps/blog/__init__.py from fastapi import APIRouter, FastAPI from fastappkit.conf import get_settings @@ -43,213 +37,307 @@ router = APIRouter() def list_posts(): return [{"id": 1, "title": "Hello"}] -def register(app: FastAPI) -> APIRouter: +def register(app: FastAPI) -> APIRouter | None: """Register this app with the FastAPI application.""" - settings = get_settings() # Access settings - # Return router - fastappkit will mount it with prefix from manifest + settings = get_settings() + return router # Return router for auto-mount, or None for manual mount +``` + +**Signature**: `register(app: FastAPI) -> APIRouter | None` +- Return `APIRouter` for auto-mount +- Return `None` for manual mount (app handles mounting itself) + +#### `apps//models.py` + +SQLAlchemy models: + +```python +from core.models import Base +from sqlalchemy import Column, Integer, String + +class Post(Base): + __tablename__ = "posts" + + id = Column(Integer, primary_key=True) + title = Column(String) + content = Column(String) +``` + +**CRITICAL**: Must import `Base` from `core.models`, not create own. + +#### `apps//router.py` + +FastAPI router: + +```python +from fastapi import APIRouter + +router = APIRouter() + +@router.get("/") +def read_root(): + return {"message": "Hello from blog app"} +``` + +### Registration Function + +The `register()` function is called by fastappkit when loading apps: + +```python +def register(app: FastAPI) -> APIRouter | None: + """ + Register this app with the FastAPI application. + + Args: + app: FastAPI application instance + + Returns: + APIRouter if app should auto-mount router, None for manual mount + """ + # Access settings if needed + settings = get_settings() + + # Return router for auto-mount return router + + # Or return None for manual mount + # app.include_router(router, prefix="/custom") + # return None ``` -#### Pattern 2: Mount Router Yourself +### Models + +**CRITICAL**: Internal apps must import `Base` from `core.models`: ```python -# apps/blog/__init__.py -from fastapi import APIRouter, FastAPI -from fastappkit.conf import get_settings +from core.models import Base # CORRECT +``` + +**NOT**: +```python +from sqlalchemy.orm import DeclarativeBase +class Base(DeclarativeBase): # WRONG for internal apps + pass +``` + +**Allowed**: +- Can import models from other internal apps +- Can import from `core.models` +- Cannot import from external apps + +### Router Creation + +Create routes in `router.py`: + +```python +from fastapi import APIRouter +from apps.blog.models import Post router = APIRouter() @router.get("/posts") def list_posts(): - return [{"id": 1, "title": "Hello"}] + return {"posts": []} -def register(app: FastAPI) -> None: - """Register this app with the FastAPI application.""" - settings = get_settings() # Access settings - # Mount router yourself - fastappkit will skip mounting - app.include_router(router, prefix="/blog") - # Can also use custom prefix, tags, dependencies, etc. - # app.include_router(router, prefix="/api/blog", tags=["blog"]) +@router.post("/posts") +def create_post(): + return {"message": "Post created"} ``` -### Customization Options - -- **Access Settings:** Use `get_settings()` to access configuration -- **Custom Prefix:** Override manifest `route_prefix` by mounting yourself -- **Router Tags:** Add tags when mounting: `app.include_router(router, tags=["blog"])` -- **Dependencies:** Add dependencies: `app.include_router(router, dependencies=[Depends(...)])` -- **Startup/Shutdown Events:** Register events: `@app.on_event("startup")` -- **Middleware:** Add middleware in `register()`: `app.add_middleware(...)` -- **Background Tasks:** Register background tasks: `app.add_task(...)` -- **Exception Handlers:** Add exception handlers: `app.add_exception_handler(...)` +**IMPORTANT**: Automatically added to `fastappkit.toml` by CLI. ## External Apps -External apps are reusable packages that can be installed via pip and plugged into any fastappkit project. - -### Create an External App +### Command ```bash fastappkit app new --as-package ``` -The `--as-package` flag is required for external apps. - -This command must be run from the project root directory (where `fastappkit.toml` is located). +Can be run anywhere (not required to be in project root). ### What Gets Created ``` / -├── / # Package directory (nested structure) -│ ├── __init__.py -│ ├── models.py -│ ├── router.py +├── / # Package directory +│ ├── __init__.py # register() function +│ ├── models.py # SQLAlchemy models (own Base class) +│ ├── router.py # FastAPI router +│ ├── config.py # Settings (for independent development) │ ├── fastappkit.toml # Manifest (in package directory) -│ └── migrations/ # Migrations (in package directory) -│ ├── env.py # Alembic env (isolated) +│ └── migrations/ # Isolated migrations +│ ├── env.py │ ├── script.py.mako │ └── versions/ -├── pyproject.toml # Package metadata (Poetry/pip) -├── alembic.ini # For independent development +├── pyproject.toml # Package metadata +├── alembic.ini # For independent development +├── .env # Environment variables (for independent development) └── README.md ``` -### External App Manifest +### Structure Explanation + +#### `//__init__.py` + +Same `register()` function signature as internal apps. + +#### `//models.py` + +**CRITICAL**: External apps must use own `Base` class: + +```python +from sqlalchemy.orm import DeclarativeBase +from sqlalchemy import Column, Integer, String + +# CRITICAL: External apps must use own Base class +class Base(DeclarativeBase): + pass + +class Payment(Base): + __tablename__ = "payments" + + id = Column(Integer, primary_key=True) + amount = Column(Integer) +``` + +**NOT**: +```python +from core.models import Base # WRONG for external apps +``` -External apps must declare metadata in `fastappkit.toml` located inside the package directory: +**Restricted**: +- Cannot import from core or internal apps +- Isolation enforced by validation -**Location:** `//fastappkit.toml` +#### `//fastappkit.toml` -**Example:** +Manifest file (in package directory): ```toml -name = "blog" +[tool.fastappkit] +name = "payments" version = "0.1.0" -entrypoint = "blog:register" +entrypoint = "payments:register" migrations = "migrations" -models_module = "blog.models" -route_prefix = "/blog" +models_module = "payments.models" +route_prefix = "/payments" ``` -!!! important "Manifest Location" -The manifest is stored in `fastappkit.toml` inside the package directory (`//fastappkit.toml`), not in `pyproject.toml`. This ensures it's included when the package is published to PyPI. Internal apps do not need a manifest file - their metadata is inferred from the directory structure. +**Location**: Must be in package directory, not project root. -### Required Manifest Fields +#### `//migrations/` -- `name`: App name (string) -- `version`: Semantic version (string) -- `entrypoint`: Dotted path to register function (e.g., `"blog:register"`) -- `migrations`: Path to migrations directory (relative to package directory, typically `"migrations"`) -- `models_module`: Dotted path to models module (recommended) +Isolated migrations directory: +- Has its own `env.py` with isolated version table +- Version table: `alembic_version_` +- Not shared with core or other apps -### Optional Manifest Fields +### Independent Development Setup -- `route_prefix`: Router prefix (default: `/`) +#### 1. Install Package -See the [Manifest Reference](../reference/manifest-reference.md) for complete details. +**CRITICAL**: External apps MUST be pip-installed: -## Using External Apps +```bash +cd +pip install -e . +``` -External apps must be installed via `pip` (editable or non-editable) and then added to `fastappkit.toml`: +#### 2. Configure `.env` -### Step 1: Install the External App +Edit `.env` file in external app directory: ```bash -# For published packages -pip install external-app-name +DATABASE_URL=sqlite:///./payments.db +DEBUG=false +``` -# For local development (editable install) -pip install -e /path/to/external-app +#### 3. Use Alembic Directly -# Or add to requirements.txt/pyproject.toml dependencies -``` +**CRITICAL**: External apps cannot use `fastappkit migrate`. Use Alembic directly: -### Step 2: Add to `fastappkit.toml` +```bash +alembic revision --autogenerate -m "initial" +alembic upgrade head +``` -Edit `fastappkit.toml` and add the app to the `apps` list: +#### 4. Test Independently -```toml -[tool.fastappkit] -apps = [ - "apps.blog", # Internal app - "external_app_name", # External app (package name) -] +```bash +uvicorn main:app --reload ``` -### App Resolution +Uses external app's `.env` and `DATABASE_URL`. -The resolver tries methods in this order: +## Best Practices -1. **Internal app pattern**: `"apps."` - checks if entry starts with `apps.` and resolves to `./apps//` -2. **Package name** (dotted import): `"external_app_name"` - tries to import as Python module using `importlib.import_module()` +### Naming Conventions -!!! important "Pip Installation Required" -External apps must be pip-installed. Filesystem paths are not supported. For local development, use `pip install -e /path/to/app` to install the package in editable mode. +- Use lowercase with underscores: `blog`, `user_auth`, `payment_processing` +- Alphanumeric, hyphens, and underscores only +- Avoid Python keywords +- Keep names descriptive but concise -## Listing Apps +### App Organization -List all apps in your project: +**Internal Apps:** +- One app per feature/domain +- Group related functionality together +- Use clear, descriptive names -```bash -fastappkit app list [--verbose] [--debug] [--quiet] -``` +**External Apps:** +- Design for reusability +- Keep dependencies minimal +- Document requirements clearly -### Options +### When to Use Internal vs External -- `--verbose, -v`: Show detailed information (import path, migrations path, etc.) -- `--debug`: Show debug information -- `--quiet, -q`: Suppress output +**Use Internal Apps When:** +- Feature is project-specific +- You need shared migrations +- You want tight integration with core -This command must be run from the project root directory. +**Use External Apps When:** +- Component is reusable across projects +- You want isolated migrations +- You plan to publish to PyPI +- Component should be independent -### Output +## Common Issues -Shows all apps (internal and external) with: +### Missing `__init__.py` -- Name -- Type (internal/external) -- Route prefix +**Error**: `AppLoadError: App directory is not a Python package` -**Verbose output includes:** +**Solution**: Ensure `apps//__init__.py` exists (created by CLI, but verify if manual). -- Import path -- Filesystem path (if applicable) -- Migrations path -- Manifest details +### External App Not Pip-Installed -## Validating Apps +**Error**: `ImportError` or `AppLoadError: Could not resolve app entry` -Validate an app's structure and configuration: +**Solution**: Install package: `pip install -e .` (even for local development). -```bash -fastappkit app validate [--json] -``` +### Wrong `Base` Class in External App + +**Error**: Isolation validation error -### Options +**Solution**: External apps must use own `Base` class (DeclarativeBase), not `core.models.Base`. -- `--json`: Output results as JSON (CI-friendly) +### Missing Manifest for External App -This command must be run from the project root directory. +**Error**: `AppLoadError: Failed to load manifest` -### What Gets Validated +**Solution**: Ensure `fastappkit.toml` exists in package directory (`//fastappkit.toml`). -- Manifest presence and correctness (required fields, format) -- Entrypoint importability and signature -- Migration folder existence (external apps) -- Version table configuration (external apps) -- Isolation rules (external apps cannot import internal apps) -- Duplicate app names (warns if multiple entries resolve to same name) +### Duplicate App Names -### Output +**Warning**: `Duplicate app names detected` -- Human-readable by default (shows errors and warnings) -- JSON format with `--json` flag (includes `valid`, `errors`, `warnings` arrays) -- Exit code 1 if validation fails (useful for CI/CD) +**Solution**: Rename one of the apps or check if multiple entries resolve to same name. -## Learn More +## Next Steps -- [Internal Apps](../topics/internal-apps.md) - Deep dive into internal apps -- [External Apps](../topics/external-apps.md) - Understanding external apps -- [App Isolation](../topics/app-isolation.md) - Isolation rules and constraints -- [Router Mounting](../topics/router-mounting.md) - How routers are mounted +- [Migrations](migrations.md) - Create and manage migrations +- [Development Workflow](development-workflow.md) - Day-to-day development +- [External App Manifest](../configuration/external-app-manifest.md) - Manifest reference diff --git a/docs/guides/creating-projects.md b/docs/guides/creating-projects.md index 045992e..d9c8587 100644 --- a/docs/guides/creating-projects.md +++ b/docs/guides/creating-projects.md @@ -1,86 +1,235 @@ # Creating Projects -This guide covers creating and managing fastappkit projects. +Detailed guide to creating new fastappkit projects. -## Create a New Project - -Use the `core new` command to create a new project: +## Command ```bash fastappkit core new [--project-root ] [--description ] ``` -### Options +## Options + +### `--project-root ` + +Directory to create project in. -- `--project-root`: Directory to create project in (default: current working directory) -- `--description`: Project description +**Default**: Current working directory -### Behavior +**Example:** +```bash +fastappkit core new myproject --project-root /path/to/projects +``` -If `--project-root` is not specified, the project is created in the current working directory. The project name becomes a subdirectory. +### `--description ` -### What Gets Created +Project description. -The command creates: +**Example:** +```bash +fastappkit core new myproject --description "My API project" +``` -- Project directory structure -- `fastappkit.toml` configuration file -- `.env` file with `DATABASE_URL` placeholder -- Core application files (`core/app.py`, `core/config.py`, etc.) -- `main.py` entry point +## What Gets Created -### Project Structure +### Directory Structure ``` myproject/ ├── core/ -│ ├── config.py # Settings (loads from .env) +│ ├── __init__.py +│ ├── config.py # Settings (BaseSettings from .env) +│ ├── models.py # SQLAlchemy Base class │ ├── app.py # create_app() factory -│ ├── models.py # Core models (optional) │ └── db/ │ └── migrations/ # Core migrations -├── apps/ # Internal apps directory +│ ├── env.py +│ ├── script.py.mako +│ └── versions/ +├── apps/ # Internal apps directory (empty) ├── fastappkit.toml # Project configuration ├── .env # Environment variables -└── main.py # Entry point +├── .gitignore +├── main.py # Entry point +├── pyproject.toml # Package metadata +└── README.md ``` -## Running Development Server +### File Descriptions + +#### `core/config.py` + +Settings class using Pydantic's `BaseSettings`: +- Loads from `.env` file automatically +- Required: `database_url`, `debug` +- Can be extended with custom settings + +#### `core/models.py` + +SQLAlchemy Base class (DeclarativeBase): +- Used by internal apps for models +- Can add core models here (shared infrastructure) + +#### `core/app.py` -Start the development server: +FastAPI app factory: +- Initializes `Settings` and `FastAppKit` +- Creates and configures FastAPI app +- Can be customized (middleware, exception handlers, etc.) + +#### `core/db/migrations/` + +Alembic migration directory: +- Shared by core and internal apps +- Contains `env.py` (Alembic configuration) +- `versions/` directory for migration files + +#### `fastappkit.toml` + +Project configuration: +- Lists apps (internal and external) +- Optional migration order override +- Initially empty `apps` array + +#### `.env` + +Environment variables: +- Auto-created with defaults +- `DATABASE_URL` and `DEBUG` settings +- Add custom settings as needed + +#### `main.py` + +Application entry point: +- Imports `app` from `core.app` +- Can be run directly: `python main.py` +- Or with uvicorn: `uvicorn main:app --reload` + +## Post-Creation Steps + +### 1. Install Dependencies ```bash -fastappkit core dev [--host ] [--port ] [--reload] [--verbose] [--debug] [--quiet] [] +cd myproject +poetry install +# or +pip install -e . ``` -### Options +### 2. Update Dependency Versions -- `--host, -h`: Host to bind to (default: `127.0.0.1`) -- `--port, -p`: Port to bind to (default: `8000`) -- Additional uvicorn options: All other arguments are forwarded to uvicorn (e.g., `--workers`, `--log-level`, `--access-log`) -- `--reload`: Enable auto-reload on code changes -- `--verbose, -v`: Enable verbose output (overrides global setting) -- `--debug`: Enable debug output (overrides global setting, includes stack traces) -- `--quiet, -q`: Suppress output (overrides global setting) +**IMPORTANT**: Dependency versions in `pyproject.toml` default to `*`. Update them: -!!! note "Project Root Required" -This command must be run from the project root directory (where `fastappkit.toml` is located). +```toml +[tool.poetry.dependencies] +python = ">=3.11,<4.0" +fastapi = ">=0.120.0,<0.130" # Specific range instead of * +sqlalchemy = ">=2.0,<3.0" +alembic = ">=1.17.2,<1.18" +``` -### Behavior +### 3. Configure Settings -- Loads all apps from `fastappkit.toml` -- Mounts routers with automatic prefixes -- Starts uvicorn server -- Hot-reloads on code changes (if `--reload` is set) -- With `--reload`, uses import string (`main:app`) for proper reloading -- Without `--reload`, creates app object directly for faster startup +Edit `.env` file: -## Project Configuration +```bash +DATABASE_URL=sqlite:///./myproject.db +DEBUG=false +``` + +Add any custom settings to `core/config.py` if needed. + +### 4. First Migration (Optional) + +If you have core models, create a migration: + +```bash +fastappkit migrate core -m "initial" +fastappkit migrate all +``` + +## Customization + +### Modifying `core/app.py` + +Add middleware, exception handlers, etc.: + +```python +from core.config import Settings +from fastappkit.core.kit import FastAppKit +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware + +settings = Settings() +kit = FastAppKit(settings=settings) +app = kit.create_app() -Projects are configured via `fastappkit.toml`. See the [Configuration Guide](configuration.md) for details. +# Customize FastAPI app +app.title = "My Custom API" +app.version = "1.0.0" +app.description = "Custom API description" + +# Add middleware +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) +``` + +### Adding Custom Settings + +Edit `core/config.py`: + +```python +from pydantic import Field +from pydantic_settings import BaseSettings, SettingsConfigDict + +class Settings(BaseSettings): + database_url: str = Field(default="sqlite:///./app.db") + debug: bool = Field(default=False) + + # Add custom settings + secret_key: str = Field(default="", alias="SECRET_KEY") + api_key: str = Field(default="", alias="API_KEY") + + model_config = SettingsConfigDict( + env_file=".env", + env_file_encoding="utf-8", + populate_by_name=True + ) +``` + +### Custom FastAPI App Settings + +Modify `core/app.py` to customize the FastAPI app instance: + +```python +app = kit.create_app() + +# Customize app metadata +app.title = "My API" +app.version = "1.0.0" +app.description = "API description" +app.terms_of_service = "https://example.com/terms" +app.contact = { + "name": "API Support", + "email": "support@example.com", +} + +# Add custom OpenAPI tags +app.openapi_tags = [ + { + "name": "users", + "description": "User management operations", + }, +] +``` ## Next Steps -- [Create your first app](creating-apps.md) -- [Configure your project](configuration.md) -- [Set up migrations](../topics/migration-system.md) +- [Creating Apps](creating-apps.md) - Create your first app +- [Migrations](migrations.md) - Manage database schema +- [Configuration](../configuration/index.md) - Configure your project diff --git a/docs/guides/deployment.md b/docs/guides/deployment.md index 47b0571..0dcbbb7 100644 --- a/docs/guides/deployment.md +++ b/docs/guides/deployment.md @@ -1,25 +1,12 @@ # Deployment -This guide covers deploying fastappkit applications to production. +Production deployment guide for fastappkit applications. -## Production Considerations +## Pre-Deployment Checklist -### Environment Variables - -Ensure all required environment variables are set in your production environment: +### 1. Update Dependency Versions -```bash -DATABASE_URL=postgresql://user:password@host:5432/dbname -DEBUG=false -# Add your custom settings here -``` - -!!! warning "Security" -Never commit `.env` files with sensitive data. Use environment variables or secure secret management systems in production. - -### Dependency Versions - -Update dependency versions in `pyproject.toml` from `*` to specific ranges for production: +**CRITICAL**: Update dependency versions in `pyproject.toml` from `*` to specific ranges: ```toml [tool.poetry.dependencies] @@ -29,7 +16,27 @@ sqlalchemy = ">=2.0,<3.0" alembic = ">=1.17.2,<1.18" ``` -### Database Migrations +### 2. Configure Production Settings + +Edit `core/config.py` for production: + +```python +class Settings(BaseSettings): + database_url: str = Field(default="") # No default in production + debug: bool = Field(default=False) # Always False in production + + # Production-specific settings + secret_key: str = Field(alias="SECRET_KEY") # Required, no default + allowed_hosts: list[str] = Field(default=["*"], alias="ALLOWED_HOSTS") + + model_config = SettingsConfigDict( + env_file=".env", + env_file_encoding="utf-8", + populate_by_name=True + ) +``` + +### 3. Run Migrations Always run migrations before deploying: @@ -37,8 +44,66 @@ Always run migrations before deploying: fastappkit migrate all ``` -!!! tip "Migration Strategy" -Run migrations in a separate step before deploying application code. This ensures the database schema is ready before the new code runs. +### 4. Validate Apps + +Validate all apps: + +```bash +fastappkit app validate # For each app +``` + +## Production Settings + +### Database Configuration + +Use production database: + +```bash +# .env or environment variables +DATABASE_URL=postgresql://user:password@host:5432/production_db +``` + +### Security Settings + +```bash +DEBUG=false +SECRET_KEY= +ALLOWED_HOSTS=api.example.com,www.example.com +``` + +### Environment Variables + +Set all required environment variables in production: +- `DATABASE_URL` +- `DEBUG` +- `SECRET_KEY` +- Any custom settings + +!!! warning "Security" + Never commit `.env` files with sensitive data. Use environment variables or secure secret management systems in production. + +## Running Migrations in Production + +### Migration Strategy + +Run migrations in a separate step before deploying application code: + +```bash +# Step 1: Run migrations +fastappkit migrate all + +# Step 2: Deploy application code +# (restart server, etc.) +``` + +This ensures the database schema is ready before the new code runs. + +### Migration Safety + +- **Backup database** before running migrations +- **Test migrations** in staging first +- **Review SQL** with `fastappkit migrate preview` +- **Monitor** migration execution ## Deployment Options @@ -68,6 +133,16 @@ COPY . . CMD ["sh", "-c", "fastappkit migrate all && fastappkit core dev --host 0.0.0.0 --port 8000"] ``` +**Better approach** (separate migration step): + +```dockerfile +# Run migrations separately +CMD ["fastappkit", "migrate", "all"] + +# Then start server +CMD ["fastappkit", "core", "dev", "--host", "0.0.0.0", "--port", "8000"] +``` + ### Systemd Service Create a systemd service file: @@ -82,6 +157,7 @@ Type=simple User=www-data WorkingDirectory=/path/to/your/project Environment="DATABASE_URL=postgresql://..." +Environment="DEBUG=false" ExecStart=/path/to/venv/bin/fastappkit core dev --host 0.0.0.0 --port 8000 Restart=always @@ -91,66 +167,68 @@ WantedBy=multi-user.target ### Process Managers -Use process managers like Supervisor or systemd to manage your application process and ensure it restarts on failure. +Use process managers like Supervisor or systemd to: +- Manage application process +- Ensure restart on failure +- Handle logging +- Manage multiple workers -## Production Server +### Cloud Platforms -For production, use a production ASGI server instead of the development server: +#### Heroku ```bash -# Using uvicorn directly -uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4 - -# Or use gunicorn with uvicorn workers -gunicorn main:app -w 4 -k uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000 +# Procfile +web: fastappkit core dev --host 0.0.0.0 --port $PORT +release: fastappkit migrate all ``` -!!! note "Development Server" -The `fastappkit core dev` command is intended for development only. Use a production ASGI server for production deployments. - -## Monitoring and Logging +#### AWS/GCP/Azure -Configure proper logging for production: +- Use container services (ECS, Cloud Run, Container Instances) +- Set environment variables in platform settings +- Run migrations as separate deployment step +- Use managed databases (RDS, Cloud SQL, etc.) -```python -# core/config.py -import logging -from pydantic import Field -from pydantic_settings import BaseSettings, SettingsConfigDict +## Monitoring and Maintenance -class Settings(BaseSettings): - database_url: str = Field(alias="DATABASE_URL") - debug: bool = Field(default=False, alias="DEBUG") - log_level: str = Field(default="INFO", alias="LOG_LEVEL") +### Health Checks - model_config = SettingsConfigDict( - env_file=".env", - populate_by_name=True - ) +Add health check endpoint: -# Configure logging -logging.basicConfig( - level=getattr(logging, settings.log_level), - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' -) +```python +# core/app.py or in an app +@app.get("/health") +def health_check(): + return {"status": "healthy"} ``` -## Health Checks +### Logging -Add health check endpoints: +Configure production logging: ```python -# core/app.py or in an app -from fastapi import APIRouter - -router = APIRouter() +import logging -@router.get("/health") -def health_check(): - return {"status": "healthy"} +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", + handlers=[ + logging.FileHandler("app.log"), + logging.StreamHandler() + ] +) ``` -## Learn More +### Database Maintenance + +- Regular backups +- Monitor database performance +- Review migration history +- Plan for schema changes + +## Next Steps -- [Configuration Guide](configuration.md) - Production configuration -- [Best Practices](../advanced/best-practices.md) - Production best practices +- [Configuration](../configuration/index.md) - Production configuration +- [Troubleshooting](../troubleshooting/common-issues.md) - Production issues +- [Best Practices](../advanced/best-practices.md) - Recommended patterns diff --git a/docs/guides/development-workflow.md b/docs/guides/development-workflow.md new file mode 100644 index 0000000..67e0505 --- /dev/null +++ b/docs/guides/development-workflow.md @@ -0,0 +1,235 @@ +# Development Workflow + +Day-to-day development practices for fastappkit projects. + +## Starting Development Server + +### Basic Command + +```bash +fastappkit core dev +``` + +**IMPORTANT**: Must be run from project root. + +### Options + +- `--host `: Host to bind to (default: `127.0.0.1`) +- `--port `: Port to bind to (default: `8000`) +- `--reload`: Enable auto-reload on code changes + +### Uvicorn Options Forwarding + +All additional arguments are forwarded to uvicorn: + +```bash +fastappkit core dev --workers 4 +fastappkit core dev --log-level debug +fastappkit core dev --access-log +``` + +**IMPORTANT**: Server uses core project's `DATABASE_URL` from `.env`. + +## Development Cycle + +### Typical Workflow + +1. **Create/Modify Models** + ```python + # apps/blog/models.py + class Post(Base): + __tablename__ = "posts" + title = Column(String) + ``` + +2. **Create Migration** + ```bash + fastappkit migrate app blog makemigrations -m "Add post model" + ``` + +3. **Apply Migration** + ```bash + fastappkit migrate all + ``` + +4. **Test Endpoints** + ```bash + fastappkit core dev + # Visit http://127.0.0.1:8000/docs + ``` + +5. **Iterate** + - Make changes + - Create migrations + - Test + - Repeat + +## Testing + +### Manual Testing with Development Server + +```bash +fastappkit core dev +``` + +- Visit `http://127.0.0.1:8000/docs` for Swagger UI +- Visit `http://127.0.0.1:8000/redoc` for ReDoc +- Test endpoints with curl or httpx + +### Testing External Apps Independently + +```bash +cd +uvicorn main:app --reload +``` + +- Uses external app's `.env` and `DATABASE_URL` +- Test app in isolation before integrating + +### Integration Testing + +After adding external app to core project: + +```bash +cd +fastappkit core dev +``` + +- Uses core project's `DATABASE_URL` +- Test all apps together + +## Debugging + +### Using `--debug` Flag + +```bash +fastappkit core dev --debug +``` + +Shows: +- Stack traces +- Detailed error messages +- Full exception information + +### Logging Configuration + +Configure logging in `core/app.py`: + +```python +import logging + +logging.basicConfig( + level=logging.DEBUG if settings.debug else logging.INFO, + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" +) +``` + +### Common Issues + +#### App Not Loading + +**Error**: `AppLoadError: Failed to load app` + +**Solution**: Run validation: +```bash +fastappkit app validate +``` + +#### Route Collisions + +**Warning**: `Route collision detected` + +**Solution**: Check `route_prefix` in manifests: +```toml +# fastappkit.toml (external app) +route_prefix = "/api/blog" +``` + +#### Import Errors + +**Error**: `ModuleNotFoundError` + +**Solution**: +- Check `__init__.py` files exist +- Verify dependencies are installed +- Check Python path includes project root + +#### Settings Not Loaded + +**Error**: Runtime errors when calling `get_settings()` + +**Solution**: Verify `core/app.py` initializes Settings: +```python +settings = Settings() +kit = FastAppKit(settings=settings) +app = kit.create_app() +``` + +## Critical Requirements + +### All Commands from Project Root + +**CRITICAL**: All commands must be run from project root (where `fastappkit.toml` is located). + +**Commands affected:** +- `fastappkit app new` +- `fastappkit app list` +- `fastappkit app validate` +- `fastappkit migrate *` +- `fastappkit core dev` + +### External Apps Must be Pip-Installed + +**CRITICAL**: External apps must be pip-installed before use: +```bash +pip install -e /path/to/app +``` + +### Settings Must be Initialized + +**CRITICAL**: Settings must be initialized in `core/app.py`: +```python +settings = Settings() +kit = FastAppKit(settings=settings) +``` + +### Database Must be Accessible + +**CRITICAL**: Check `DATABASE_URL` in `.env`: +```bash +DATABASE_URL=postgresql://user:password@localhost:5432/mydb +``` + +## Best Practices + +1. **Run migrations before starting server** + ```bash + fastappkit migrate all + fastappkit core dev + ``` + +2. **Use `--reload` for development** + ```bash + fastappkit core dev --reload + ``` + +3. **Validate apps regularly** + ```bash + fastappkit app validate + ``` + +4. **Test external apps independently first** + ```bash + cd + uvicorn main:app --reload + ``` + +5. **Check for route collisions** + - Review `route_prefix` in manifests + - Ensure unique prefixes + +## Next Steps + +- [Deployment](deployment.md) - Production deployment +- [Troubleshooting](../troubleshooting/common-issues.md) - Common problems +- [CLI Reference](../reference/cli-reference.md) - All commands diff --git a/docs/guides/migrations.md b/docs/guides/migrations.md index 17be095..ce22545 100644 --- a/docs/guides/migrations.md +++ b/docs/guides/migrations.md @@ -1,118 +1,123 @@ # Migrations -This guide covers working with database migrations in fastappkit. +Complete migration workflow guide for fastappkit. -## Migration Architecture +## Migration System Overview -fastappkit uses a **unified migration runner** that handles: +fastappkit provides unified migration management across core, internal apps, and external apps. -1. **Core migrations** — shared infrastructure (sessions, logs, etc.) -2. **Internal app migrations** — project-wide schema changes -3. **External app migrations** — isolated plugin schemas +### Core Migrations -## Version Tables +- **Location**: `core/db/migrations/` +- **Purpose**: Core-level schema changes +- **Version Table**: `alembic_version` (shared) -- **Core & Internal Apps:** Use shared `alembic_version` table -- **External Apps:** Use per-app tables (`alembic_version_`) +### Internal App Migrations -## Core Migrations +- **Location**: `core/db/migrations/` (shared with core) +- **Purpose**: Internal app schema changes +- **Version Table**: `alembic_version` (shared with core) +- **Autogenerate**: Sees all internal app models + core models + +### External App Migrations -Core migrations are for project-level schema changes and shared infrastructure. +- **Location**: `//migrations/` (isolated per app) +- **Purpose**: External app schema changes +- **Version Table**: `alembic_version_` (per-app) +- **Autogenerate**: Sees only that app's models + +## Core Migrations -### Generate Core Migration +### Create Migration ```bash fastappkit migrate core -m "message" ``` -**Options:** - -- `-m, --message`: Migration message (required) - -!!! note "Unified Operations" -Core migrations are generated separately, but preview/upgrade/downgrade operations use unified commands (see below) since core and internal app migrations share the same directory and version table. - -### Core Models +**IMPORTANT**: Must be run from project root. -Core migrations can work with or without models. Core models are **separate from apps** — you don't need to create an app to use them. They're for shared infrastructure tables (sessions, logs, job queues, etc.) that are used across your entire project. +**When to Use:** +- Core-level schema changes +- Shared infrastructure tables (sessions, logs, etc.) -**Where to Place Core Models:** +**Uses**: Core project's `DATABASE_URL` from `.env`. -Place all your core models in `core/models.py`: +## Internal App Migrations -```python -# core/models.py -from sqlalchemy import Column, Integer, String, DateTime -from sqlalchemy.ext.declarative import declarative_base -from datetime import datetime +### Create Migration -Base = declarative_base() +```bash +fastappkit migrate app makemigrations -m "message" +``` -class Session(Base): - __tablename__ = "sessions" +**IMPORTANT**: Must be run from project root. - id = Column(Integer, primary_key=True) - session_key = Column(String, unique=True, index=True) - created_at = Column(DateTime, default=datetime.utcnow) +**CRITICAL**: Shared migration directory with core (`core/db/migrations/`). -class ApiLog(Base): - __tablename__ = "api_logs" +**Migration Order:** +- From `fastappkit.toml` apps order, or +- From `[tool.fastappkit.migration.order]` if specified - id = Column(Integer, primary_key=True) - endpoint = Column(String) - method = Column(String) -``` +**Autogenerate Behavior:** +- Sees all internal app models +- Sees all core models +- Can detect cross-app relationships -**How Core Models Are Discovered:** +**Cannot Use Per-App Commands:** +- `upgrade`/`downgrade` per-app not supported +- Use unified commands: `fastappkit migrate upgrade` -The migration system looks for core models in `core/models.py`: +## External App Migrations -1. Tries to import `core.models` (looks for `Base` or `metadata` attribute) -2. If not found, uses empty `MetaData` (you can still create manual migrations) +### Creating Migrations -!!! important "Core Models Location" -Core models must be in `core/models.py` — this is the only supported location. Core migrations are located in `core/db/migrations/`. Uses shared `alembic_version` table (same as internal apps). Core models are **optional** — core migrations work without them. Core models are for **shared infrastructure**, not app-specific features (use internal apps for those). +**CRITICAL**: External apps cannot create migrations from core project. Use Alembic directly: -## Internal App Migrations +```bash +cd +alembic revision --autogenerate -m "message" +``` -Internal app migrations share the project's migration timeline with core migrations. +**Uses**: External app's `.env` and `DATABASE_URL` when developing independently. -### Generate Internal App Migration +### Applying from Core ```bash -fastappkit migrate app makemigrations -m "message" +fastappkit migrate app upgrade ``` -**Options:** +**Uses**: Core project's `DATABASE_URL` when integrated. -- `-m, --message`: Migration message (required) +**IMPORTANT**: +- External apps cannot create migrations from core project +- Must have `migrations/env.py` with correct version table (`alembic_version_`) -This command must be run from the project root directory. +## Unified Commands -!!! important "Migration Location" -Internal app migrations are created in `core/db/migrations/versions/`, NOT in `apps//migrations/`. Internal apps share the project's migration timeline with core migrations. **Internal apps can only create migrations** - they cannot use `preview`, `upgrade`, or `downgrade` actions. For preview/upgrade/downgrade operations, use the unified commands below (which operate on all core + internal app migrations together). +### Apply All Migrations -**Autogenerate Behavior:** +```bash +fastappkit migrate all +``` -- Uses full project metadata (all internal apps + core models) -- Good for cross-app relationships and shared schema -- Migration files are shared across all internal apps and core +**IMPORTANT**: Must be run from project root. -## Unified Migration Operations +**Execution Order:** +1. Core migrations (always first) +2. Internal apps (skipped - already included in core migrations) +3. External apps (in `fastappkit.toml` order) -Since core and internal app migrations share the same directory (`core/db/migrations/`) and version table, use these unified commands for preview, upgrade, and downgrade: +**Recommended**: Use this command for normal workflows. -### Preview SQL +### Upgrade Core + Internal ```bash -fastappkit migrate preview [--revision ] +fastappkit migrate upgrade [--revision ] ``` -### Apply Migrations +**IMPORTANT**: Must be run from project root. -```bash -fastappkit migrate upgrade [--revision ] -``` +Applies core and internal app migrations (shared directory). ### Downgrade @@ -120,141 +125,171 @@ fastappkit migrate upgrade [--revision ] fastappkit migrate downgrade ``` -**Options:** - -- `--revision, -r`: Specific revision (default: `head` for upgrade/preview) +Downgrades core and internal app migrations. -!!! note "Project Root Required" -These commands must be run from the project root directory. They operate on all migrations in `core/db/migrations/versions/`, which includes both core migrations and internal app migrations. - -## External App Migrations - -External apps have isolated migrations that are developed independently. - -### Development (in external app directory) +### Preview SQL ```bash -cd / -alembic revision --autogenerate -m "message" -alembic upgrade head +fastappkit migrate preview [--revision ] ``` -### From Core Project +Shows SQL that would be executed without applying. + +## Migration Workflow Examples + +### Adding a New Model -```bash -# Apply existing migrations (migrations must exist in external app) -fastappkit migrate app upgrade [--revision ] +1. **Add model to `apps/blog/models.py`:** + ```python + from core.models import Base + from sqlalchemy import Column, Integer, String + + class Post(Base): + __tablename__ = "posts" + id = Column(Integer, primary_key=True) + title = Column(String) + ``` -# Preview SQL -fastappkit migrate app preview [--revision ] +2. **Create migration:** + ```bash + fastappkit migrate app blog makemigrations -m "Add post model" + ``` -# Downgrade -fastappkit migrate app downgrade -``` +3. **Review migration** (optional): + ```bash + fastappkit migrate preview + ``` -**Options:** +4. **Apply migration:** + ```bash + fastappkit migrate all + ``` -- `--revision, -r`: Specific revision (default: `head` for upgrade/preview) +### Modifying Existing Model -This command must be run from the project root directory. +1. **Modify model:** + ```python + class Post(Base): + __tablename__ = "posts" + id = Column(Integer, primary_key=True) + title = Column(String) + content = Column(String) # Added field + ``` -!!! important "External App Limitations" -External apps **cannot create migrations** from the core project (`makemigrations` is not supported for external apps). They must be developed independently in their own directory using standard Alembic commands. External apps can only use `upgrade`, `downgrade`, and `preview` actions from the core project. +2. **Create migration:** + ```bash + fastappkit migrate app blog makemigrations -m "Add content to post" + ``` -**Error Handling:** +3. **Apply:** + ```bash + fastappkit migrate all + ``` -- If no migration files found, shows helpful error with instructions -- If revision not found, provides tips for independent development -- Uses core project's `DATABASE_URL` (not external app's `alembic.ini`) +### Adding Relationships -## Apply All Migrations +1. **Add relationship:** + ```python + from sqlalchemy import ForeignKey + from sqlalchemy.orm import relationship + from apps.auth.models import User -Apply all migrations in the correct order: + class Post(Base): + __tablename__ = "posts" + id = Column(Integer, primary_key=True) + author_id = Column(Integer, ForeignKey("users.id")) + author = relationship("User", back_populates="posts") + ``` -```bash -fastappkit migrate all -``` +2. **Create migration:** + ```bash + fastappkit migrate app blog makemigrations -m "Add author relationship" + ``` -This command must be run from the project root directory. +3. **Apply:** + ```bash + fastappkit migrate all + ``` -**Execution Order:** +## Migration Commands Quick Reference -1. Core migrations (always first) -2. Internal apps (in config order) - skipped (already included in core migrations) -3. External apps (in config order) +| Command | Purpose | Applies To | +|---------|---------|------------| +| `fastappkit migrate core -m "msg"` | Create core migration | Core only | +| `fastappkit migrate app makemigrations -m "msg"` | Create app migration | Internal only | +| `fastappkit migrate all` | Apply all migrations | All | +| `fastappkit migrate upgrade` | Apply core + internal | Core + Internal | +| `fastappkit migrate app upgrade` | Apply app migration | External | +| `fastappkit migrate preview` | Preview SQL | Core + Internal | +| `fastappkit migrate downgrade ` | Downgrade | Core + Internal | -!!! tip "Recommended Command" -This is the **recommended command** for normal workflows. Internal apps are skipped because they share core's migration directory. Shows progress for each step. Fails fast if any migration fails. +## Troubleshooting -## Migration Safety +### Migration Conflicts -### Downgrades +**Problem**: Multiple migrations modify same table -Downgrades are explicit operations: +**Solution**: +- Review migration files +- Merge changes if needed +- Ensure migrations are in correct order -```bash -# For core + internal apps (unified command) -fastappkit migrate downgrade +### Downgrade Issues -# For external apps -fastappkit migrate app downgrade -``` +**Problem**: Downgrade fails or causes data loss -**Safety checks:** +**Solution**: +- Review downgrade SQL: `fastappkit migrate preview` +- Backup database before downgrading +- Test downgrades in development first -- Dry-run inspection -- Foreign key dependency validation -- External apps can only revert their own schema +### External App Migration Problems -## Migration Ordering +#### Revision Not Found -Migrations run in this order: +**Error**: `Can't locate revision identified by '...'` -1. **Core** (always first) -2. **Internal apps** (order from `fastappkit.toml`) -3. **External apps** (order from `fastappkit.toml`) +**Solution**: +- Check migration files exist in external app's `migrations/versions/` +- Verify external app was developed independently +- Ensure migrations are included in package -You can override internal app order in config: +#### Wrong Version Table -```toml -[tool.fastappkit.migration] -order = ["core", "auth", "blog"] -``` +**Error**: Version table conflicts -## Autogenerate Behavior +**Solution**: +- Check `migrations/env.py` has `alembic_version_` +- Should NOT use shared `alembic_version` table -### Internal Apps +#### Database Connection Fails -Autogenerate sees: +**Error**: Connection errors when applying migrations -- All internal app models -- All core models -- Cross-app relations +**Solution**: +- Check `DATABASE_URL` (uses core's when integrated) +- Verify database is running and accessible +- Check connection string format -Good for teams working on shared schema. +### Migration Directory Not Found -### External Apps +**Error**: `Core migrations directory not found` -Autogenerate sees: +**Solution**: +- Verify project structure +- Ensure project was created correctly +- Check `core/db/migrations/` exists -- Only that app's own models -- No internal app models -- No other external apps' models -- No core models +### All Commands Must Run from Project Root -If an external app tries to import foreign models → validation error. +**CRITICAL**: All migration commands must be run from project root (where `fastappkit.toml` is located). -## Best Practices +**Error**: `Configuration file not found` -1. Keep migration files in VCS and package them with external apps -2. Run `makemigrations` locally and test upgrades in staging -3. Use per-app version tables for external apps (automatic) -4. Prefer explicit `models_module` in manifest -5. Avoid cross-app model imports in external apps -6. Review SQL via `preview` before applying migrations -7. Test downgrades to ensure migrations are reversible +**Solution**: `cd` to project root before running commands. -## Learn More +## Next Steps -- [Migration System](../topics/migration-system.md) - Deep dive into migration architecture -- [CLI Reference](../reference/cli-reference.md) - Complete migration command reference +- [Development Workflow](development-workflow.md) - Day-to-day development +- [Migration System](../topics/migration-system.md) - Deep dive into migrations +- [CLI Reference](../reference/cli-reference.md) - Complete command reference diff --git a/docs/index.md b/docs/index.md index 8fad5a7..4c1a48c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -4,96 +4,45 @@ ## What is fastappkit? -fastappkit is a toolkit for building FastAPI projects with a modular app architecture. It enables: +fastappkit enables you to organize your FastAPI code into logical, reusable components—both within your project and as installable packages. It provides: -- **Internal apps** — project-specific feature modules (like Django apps) -- **External apps** — reusable, pluggable packages (like WordPress plugins) -- **Unified migrations** — coordinated database schema management -- **Automatic router mounting** — seamless integration of app routes -- **App validation and isolation** — ensure apps don't conflict with each other +- **Internal apps**: Project-specific modules (like Django apps) that live in your `apps/` directory +- **External apps**: Reusable packages you can install via pip and plug into any fastappkit project -## Why fastappkit? - -Building large FastAPI applications often means either cramming everything into a single file or manually organizing modules without clear conventions. fastappkit solves this by introducing a structured app system where you can organize your code into logical, reusable components—both within your project and as installable packages. - -Whether you're building a monolith that needs better organization or creating reusable components for multiple projects, fastappkit provides the structure and tooling to make it happen. - -## Who can use this? - -- **FastAPI developers** building applications that are outgrowing a single-file structure -- **Teams** that need consistent project organization across multiple developers -- **Developers** creating reusable FastAPI components they want to share -- **Anyone** who appreciates Django's app system but prefers FastAPI's performance and modern Python features +Both types get automatic router mounting, unified migration management, and validation to prevent conflicts. ## Key Features -fastappkit provides several important capabilities: - -**Project Structure** - -- Clean project layout with automatic app discovery -- Consistent organization that scales as your project grows - -**Migration Management** - -- Shared migrations for internal apps -- Isolated migrations for external apps -- Coordinated execution order - -**Router Integration** - -- Automatic router mounting with configurable prefixes -- Seamless route integration without manual wiring - -**Validation & Safety** - -- Fail-fast validation and error handling -- App isolation checks to prevent conflicts -- Manifest validation for external apps - -**Workflow Support** - -- Works with both monorepo and multi-repo workflows -- Simple CLI for project and app management +- **Modular Architecture**: Organize code into apps (internal and external) +- **Automatic Router Mounting**: Apps register routes automatically +- **Unified Migrations**: Coordinated database schema management +- **App Isolation**: Validation ensures apps don't conflict +- **CLI Tools**: Simple commands for project and app management +- **Settings Management**: Django-like settings system with environment variables ## Quick Start -Get started with fastappkit in minutes: - -### 1. Create a new project +Get started in minutes: ```bash +# Install +pip install fastappkit + +# Create a new project fastappkit core new myproject cd myproject -``` -This creates a complete FastAPI project structure with core application setup, database configuration, migration system, and app directory structure. - -### 2. Create an internal app - -```bash +# Create an app fastappkit app new blog -``` -This creates a new internal app in `apps/blog/` with models, router, and registers it in `fastappkit.toml`. - -### 3. Run migrations - -```bash +# Run migrations fastappkit migrate all -``` -This runs all migrations in the correct order: core → internal apps → external apps. - -### 4. Start the development server - -```bash +# Start development server fastappkit core dev ``` -Your FastAPI application is now running at `http://127.0.0.1:8000`! - -For detailed installation instructions, see the [Installation Guide](getting-started/installation.md). +Your FastAPI app is now running at `http://127.0.0.1:8000` with routes mounted at `/blog/`. ## Documentation Overview @@ -103,14 +52,34 @@ For detailed installation instructions, see the [Installation Guide](getting-sta - [Quick Start](getting-started/quickstart.md) - Create your first project - [Core Concepts](getting-started/core-concepts.md) - Understand internal and external apps +### Usage Scenarios + +- [Usage Overview](usage/index.md) - Choose your development path +- [Scaffolding Only](usage/scaffolding-only.md) - Just project structure +- [Internal Apps](usage/internal-apps.md) - Build with internal apps +- [Full Ecosystem](usage/full-ecosystem.md) - Internal + external apps + +### Configuration + +- [Configuration Overview](configuration/index.md) - All configuration options +- [Project Configuration](configuration/project-config.md) - `fastappkit.toml` reference +- [Settings](configuration/settings.md) - `core/config.py` guide +- [External App Manifest](configuration/external-app-manifest.md) - Manifest format + ### Guides - [Creating Projects](guides/creating-projects.md) - Set up new fastappkit projects - [Creating Apps](guides/creating-apps.md) - Build internal and external apps - [Migrations](guides/migrations.md) - Manage database schema changes -- [Configuration](guides/configuration.md) - Configure your projects +- [Development Workflow](guides/development-workflow.md) - Day-to-day development - [Deployment](guides/deployment.md) - Deploy fastappkit applications +### Reference + +- [CLI Reference](reference/cli-reference.md) - All available commands +- [Configuration Reference](reference/configuration-reference.md) - Complete config options +- [API Reference](reference/api-reference.md) - Programmatic API + ### Topics - [Internal Apps](topics/internal-apps.md) - Project-specific modules @@ -119,13 +88,6 @@ For detailed installation instructions, see the [Installation Guide](getting-sta - [App Isolation](topics/app-isolation.md) - Ensuring apps don't conflict - [Router Mounting](topics/router-mounting.md) - Automatic route integration -### Reference - -- [CLI Reference](reference/cli-reference.md) - All available commands -- [Configuration Reference](reference/configuration-reference.md) - Configuration options -- [Manifest Reference](reference/manifest-reference.md) - App manifest format -- [API Reference](reference/api-reference.md) - Programmatic API - ### Advanced - [Architecture](advanced/architecture.md) - How fastappkit works internally @@ -137,62 +99,19 @@ For detailed installation instructions, see the [Installation Guide](getting-sta - [Common Issues](troubleshooting/common-issues.md) - Solutions to frequent problems - [Debugging](troubleshooting/debugging.md) - Debugging techniques -## Core Concepts - -### Internal Apps - -Internal apps are project-specific modules that live in your project's `apps/` directory. They share the same migration system and database connection. - -**Key points:** +## Who is this for? -- Internal apps import `Base` from `core.models` for SQLAlchemy models -- The `register()` function can return `Optional[APIRouter]` - return a router for fastappkit to mount, or mount it yourself and return `None` - -### External Apps - -External apps are reusable packages that can be installed via pip and plugged into any fastappkit project. - -**Key points:** - -- External apps must use their own `Base` class (isolated metadata) - cannot import from core -- The `register()` function can return `Optional[APIRouter]` - return a router for fastappkit to mount, or mount it yourself and return `None` -- External apps must have `fastappkit.toml` manifest in the package directory - -### Migrations - -fastappkit provides unified migration management: - -- **Core migrations**: Project-level schema changes -- **Internal app migrations**: Shared migration directory for all internal apps -- **External app migrations**: Isolated migrations per external app - -## Example Project Structure - -``` -myproject/ -├── core/ -│ ├── config.py # Settings (loads from .env) -│ ├── models.py # SQLAlchemy Base class (DeclarativeBase) -│ ├── app.py # create_app() factory -│ └── db/ -│ └── migrations/ # Core migrations -├── apps/ # Internal apps directory -│ └── blog/ -│ ├── models.py # Imports Base from core.models -│ └── router.py -├── fastappkit.toml # Project configuration -├── .env # Environment variables -└── main.py # Entry point -``` +- **FastAPI developers** building applications that are outgrowing a single-file structure +- **Teams** that need consistent project organization across multiple developers +- **Developers** creating reusable FastAPI components they want to share +- **Anyone** who appreciates Django's app system but prefers FastAPI's performance and modern Python features ## Community -fastappkit is an open-source project. We welcome contributions and value our community. - - [Contributing](community/index.md#contributing) - How to contribute - [Code of Conduct](community/index.md#code-of-conduct) - Community standards - [Security Policy](community/index.md#security-policy) - Reporting vulnerabilities -- [License](community/index.md#license) - MIT License +- [Changelog](changelog/0.2.1.md) - Version history (see [Changelog section](changelog/0.2.1.md) for all versions) ## Support @@ -202,5 +121,4 @@ fastappkit is an open-source project. We welcome contributions and value our com ## Acknowledgments -- Inspired by Django's app system -- Built with [FastAPI](https://fastapi.tiangolo.com/), [SQLAlchemy](https://www.sqlalchemy.org/), [Alembic](https://alembic.sqlalchemy.org/), and [Typer](https://typer.tiangolo.com/) +Inspired by Django's app system. Built with [FastAPI](https://fastapi.tiangolo.com/), [SQLAlchemy](https://www.sqlalchemy.org/), [Alembic](https://alembic.sqlalchemy.org/), and [Typer](https://typer.tiangolo.com/). diff --git a/docs/reference/api-reference.md b/docs/reference/api-reference.md index de37dc1..a049a9e 100644 --- a/docs/reference/api-reference.md +++ b/docs/reference/api-reference.md @@ -1,202 +1,269 @@ # API Reference -Programmatic API reference for fastappkit. +Programmatic API documentation for fastappkit. -## Core API +## FastAppKit Class -### FastAppKit +Main class that manages settings and app loading. -Main class for creating FastAPI applications. +### `FastAppKit.__init__` + +Initialize FastAppKit with settings. + +**Signature:** + +```python +def __init__(self, settings: SettingsProtocol) -> None +``` + +**Parameters:** + +- `settings`: Settings instance from project's `core.config` module + +**Example:** ```python -from fastappkit.core.kit import FastAppKit from core.config import Settings +from fastappkit.core.kit import FastAppKit settings = Settings() kit = FastAppKit(settings=settings) -app = kit.create_app() ``` -#### Methods - -**`create_app() -> FastAPI`** +**Notes:** -Create and configure FastAPI application. +- Calls `set_settings()` internally to make settings globally available +- Settings must implement `SettingsProtocol` (have `database_url` and `debug`) -- Loads all apps from `fastappkit.toml` -- Validates app manifests -- Mounts routers with automatic prefixes -- Returns configured FastAPI application +### `FastAppKit.create_app` -## Settings API - -### get_settings() +Create and configure FastAPI application. -Get the current settings instance. +**Signature:** ```python -from fastappkit.conf import get_settings - -settings = get_settings() -db_url = settings.database_url +def create_app(self) -> FastAPI ``` -### set_settings() +**Returns:** -Set the global settings instance. +- `FastAPI`: Configured FastAPI application + +**Example:** ```python -from fastappkit.conf import set_settings from core.config import Settings +from fastappkit.core.kit import FastAppKit settings = Settings() -set_settings(settings) +kit = FastAppKit(settings=settings) +app = kit.create_app() ``` -### ensure_settings_loaded() +**What it does:** -Ensure settings are loaded from project. +1. Creates FastAPI app instance +2. Loads apps via `AppLoader` +3. Executes app registrations +4. Mounts routers +5. Returns configured app -```python -from fastappkit.conf import ensure_settings_loaded -from pathlib import Path +## Settings Functions + +### `get_settings` + +Get the global settings instance. + +**Signature:** -project_root = Path.cwd() -ensure_settings_loaded(project_root) +```python +def get_settings() -> SettingsProtocol ``` -## App Loading API +**Returns:** -### AppLoader +- `SettingsProtocol`: Current settings instance -Load and register apps from configuration. +**Example:** ```python -from fastappkit.core.loader import AppLoader -from pathlib import Path +from fastappkit.conf import get_settings -project_root = Path.cwd() -loader = AppLoader(project_root) -registry = loader.load_all() +settings = get_settings() +db_url = settings.database_url +is_debug = settings.debug ``` -#### Methods +**Notes:** + +- Returns the settings instance set by `FastAppKit.__init__` +- Raises error if settings not initialized +- Django-like global accessor pattern -**`load_all() -> AppRegistry`** +### `set_settings` + +Set the global settings instance. + +**Signature:** + +```python +def set_settings(settings: SettingsProtocol) -> None +``` -Load all apps from `fastappkit.toml` and return registry. +**Parameters:** -### AppRegistry +- `settings`: Settings instance to set globally -Registry of loaded apps. +**Example:** ```python -from fastappkit.core.registry import AppRegistry, AppMetadata +from fastappkit.conf import set_settings +from core.config import Settings + +settings = Settings() +set_settings(settings) +``` + +**Notes:** -registry: AppRegistry = loader.load_all() +- Called automatically by `FastAppKit.__init__` +- Usually not called directly -# Get app metadata -app_metadata: AppMetadata = registry.get("blog") +## Settings Protocol -# Check if app exists -if "blog" in registry: - ... +Protocol that settings classes must implement. -# Iterate over apps -for name, metadata in registry.items(): - ... +### `SettingsProtocol` + +```python +class SettingsProtocol(Protocol): + database_url: str + debug: bool ``` -## Manifest API +**Required Attributes:** -### ManifestLoader +- `database_url: str` - Database connection string +- `debug: bool` - Debug mode flag -Load app manifests. +**Example Implementation:** ```python -from fastappkit.core.manifest import ManifestLoader +from pydantic_settings import BaseSettings +from fastappkit.conf import SettingsProtocol -loader = ManifestLoader() -manifest = loader.load("blog") +class Settings(BaseSettings, SettingsProtocol): + database_url: str = "sqlite:///./app.db" + debug: bool = False ``` -## Validation API +## App Registration -### ManifestValidator +### Register Function Signature -Validate app manifests. +Apps provide a `register()` function: + +**Signature:** ```python -from fastappkit.validation.manifest import ManifestValidator, ValidationResult +def register(app: FastAPI) -> APIRouter | None: + """Register this app with the FastAPI application.""" + return router # or None +``` -validator = ManifestValidator() -result: ValidationResult = validator.validate(manifest_data) +**Parameters:** -if result.is_valid: - print("Manifest is valid") -else: - for error in result.errors: - print(f"Error: {error}") -``` +- `app`: FastAPI application instance -### IsolationValidator +**Returns:** -Validate app isolation rules. +- `APIRouter | None`: Return router for auto-mount, or `None` for manual mount + +**Example:** ```python -from fastappkit.validation.isolation import IsolationValidator, ValidationResult +from fastapi import APIRouter, FastAPI +from fastappkit.conf import get_settings -validator = IsolationValidator() -result: ValidationResult = validator.validate(app_metadata) +router = APIRouter() -if not result.is_valid: - for error in result.errors: - print(f"Isolation violation: {error}") +@router.get("/posts") +def list_posts(): + return [] + +def register(app: FastAPI) -> APIRouter: + settings = get_settings() + return router # Auto-mount ``` -### MigrationValidator +## Programmatic Usage Examples -Validate migration configuration. +### Basic Usage ```python -from fastappkit.validation.migrations import MigrationValidator, ValidationResult - -validator = MigrationValidator() -result: ValidationResult = validator.validate(app_metadata) +# core/app.py +from core.config import Settings +from fastappkit.core.kit import FastAppKit -if not result.is_valid: - for error in result.errors: - print(f"Migration error: {error}") +settings = Settings() +kit = FastAppKit(settings=settings) +app = kit.create_app() ``` -## Migration API +### Custom FastAPI App + +```python +from core.config import Settings +from fastappkit.core.kit import FastAppKit +from fastapi import FastAPI + +settings = Settings() +kit = FastAppKit(settings=settings) +app = kit.create_app() -### MigrationRunner +# Customize app +app.title = "My Custom API" +app.version = "1.0.0" +``` -Run migrations. +### Subclassing FastAppKit ```python -from fastappkit.migrations.runner import MigrationRunner -from fastappkit.core.registry import AppMetadata +from fastappkit.core.kit import FastAppKit +from fastapi import FastAPI +from core.config import Settings -runner = MigrationRunner() -runner.upgrade_all() # Run all migrations -runner.upgrade_app(app_metadata) # Run app migrations -``` +class CustomFastAppKit(FastAppKit): + def create_app(self) -> FastAPI: + app = super().create_app() + # Add custom middleware, exception handlers, etc. + app.title = "My Custom API" + return app -### MigrationPreview +settings = Settings() +kit = CustomFastAppKit(settings=settings) +app = kit.create_app() +``` -Preview migration SQL. +### Using Settings in Routes ```python -from fastappkit.migrations.preview import MigrationPreview +from fastappkit.conf import get_settings +from fastapi import APIRouter, Depends +from core.config import Settings + +router = APIRouter() -preview = MigrationPreview() -sql = preview.preview(app_metadata, revision="head") -print(sql) +@router.get("/config") +def get_config(settings: Settings = Depends(get_settings)): + return { + "debug": settings.debug, + "database": settings.database_url + } ``` ## Learn More -- [Architecture](../advanced/architecture.md) - System architecture -- [Extending fastappkit](../advanced/extending-fastappkit.md) - Extension guide +- [Configuration](../configuration/settings.md) - Settings configuration guide +- [Creating Apps](../guides/creating-apps.md) - App registration guide +- [Extending FastAppKit](../advanced/extending-fastappkit.md) - Customization guide diff --git a/docs/reference/cli-reference.md b/docs/reference/cli-reference.md index 1d9f57c..50c3b94 100644 --- a/docs/reference/cli-reference.md +++ b/docs/reference/cli-reference.md @@ -50,6 +50,12 @@ fastappkit core new myproject --project-root /path/to/projects fastappkit core new myproject --description "My API project" ``` +**Notes:** + +- Creates complete project structure +- Generates `fastappkit.toml`, `core/`, `apps/`, etc. +- Creates `.env` file with defaults + ### `fastappkit core dev` Run the development server. @@ -57,7 +63,7 @@ Run the development server. **Syntax:** ```bash -fastappkit core dev [--host ] [--port ] [--reload] [--verbose] [--debug] [--quiet] [] +fastappkit core dev [--host ] [--port ] [--reload] [] ``` **Options:** @@ -65,13 +71,8 @@ fastappkit core dev [--host ] [--port ] [--reload] [--verbose] [--de - `--host, -h `: Host to bind to (default: `127.0.0.1`) - `--port, -p `: Port to bind to (default: `8000`) - `--reload`: Enable auto-reload on code changes -- `--verbose, -v`: Enable verbose output (overrides global setting) -- `--debug`: Enable debug output (overrides global setting, includes stack traces) -- `--quiet, -q`: Suppress output (overrides global setting) - ``: Additional options are forwarded to uvicorn (e.g., `--workers`, `--log-level`, `--access-log`) -**Note:** This command must be run from the project root directory (where `fastappkit.toml` is located). All additional arguments are forwarded to uvicorn, allowing you to use any uvicorn option. - **Examples:** ```bash @@ -83,6 +84,12 @@ fastappkit core dev --log-level debug fastappkit core dev --access-log ``` +**Notes:** + +- Must be run from project root directory (where `fastappkit.toml` is located) +- All additional arguments are forwarded to uvicorn +- Uses core project's `DATABASE_URL` from `.env` + ## App Commands ### `fastappkit app new` @@ -103,8 +110,6 @@ fastappkit app new [--as-package] - `--as-package`: Create as external package (required for external apps) -**Note:** This command must be run from the project root directory (where `fastappkit.toml` is located). - **Examples:** ```bash @@ -112,6 +117,12 @@ fastappkit app new blog fastappkit app new payments --as-package ``` +**Notes:** + +- Must be run from project root directory (for internal apps) +- Internal apps are automatically added to `fastappkit.toml` +- External apps must be pip-installed: `pip install -e .` + ### `fastappkit app list` List all apps in the project. @@ -128,8 +139,6 @@ fastappkit app list [--verbose] [--debug] [--quiet] - `--debug`: Show debug information - `--quiet, -q`: Suppress output -**Note:** This command must be run from the project root directory. - **Examples:** ```bash @@ -137,6 +146,11 @@ fastappkit app list fastappkit app list --verbose ``` +**Notes:** + +- Must be run from project root directory +- Shows app name, type (internal/external), and route prefix + ### `fastappkit app validate` Validate an app's structure and configuration. @@ -155,8 +169,6 @@ fastappkit app validate [--json] - `--json`: Output results as JSON (CI-friendly) -**Note:** This command must be run from the project root directory. - **Examples:** ```bash @@ -164,6 +176,12 @@ fastappkit app validate blog fastappkit app validate payments --json ``` +**Notes:** + +- Must be run from project root directory +- Validates manifest, isolation, and migrations +- Returns exit code 1 if validation fails + ## Migration Commands ### `fastappkit migrate core` @@ -180,14 +198,17 @@ fastappkit migrate core -m - `-m, --message `: Migration message (required) -**Note:** This command must be run from the project root directory. - **Examples:** ```bash fastappkit migrate core -m "Add session table" ``` +**Notes:** + +- Must be run from project root directory +- Creates migration in `core/db/migrations/versions/` + ### `fastappkit migrate app` Manage app migrations. @@ -201,75 +222,61 @@ fastappkit migrate app [options] **Arguments:** - ``: App name (required) -- ``: Migration action (required) - -**Actions:** - -- `makemigrations`: Generate new migration (internal apps only) -- `upgrade`: Apply migrations (external apps only) -- `downgrade`: Revert migrations (external apps only) -- `preview`: Show SQL without executing (external apps only) +- ``: Action to perform (required) + - `makemigrations` - Create migration (internal apps only) + - `upgrade` - Apply migrations (external apps) + - `downgrade` - Downgrade migrations (external apps) + - `preview` - Preview SQL (external apps) **Options:** -- `-m, --message `: Migration message (required for `makemigrations`) -- `--revision, -r `: Specific revision (default: `head` for upgrade/preview) - -**Note:** This command must be run from the project root directory. - -**Limitations:** - -- Internal apps can only use `makemigrations` action. For preview/upgrade/downgrade, use unified commands (`fastappkit migrate preview/upgrade/downgrade`). -- External apps cannot use `makemigrations` from the core project. Migrations must be created in the external app's own directory using `alembic` directly. - -**Error Scenarios:** - -- If external app has no migration files, the command will fail with instructions on how to create migrations independently. -- If revision is not found, the command will fail with helpful error messages. -- If app is not found in registry, the command will fail with app name error. +- `--revision, -r `: Specific revision (for upgrade/downgrade/preview) +- `-m, --message `: Migration message (for makemigrations) **Examples:** ```bash -# Internal app - create migration +# Internal app: create migration fastappkit migrate app blog makemigrations -m "Add post model" -# External app - upgrade +# External app: apply migrations fastappkit migrate app payments upgrade -# External app - upgrade to specific revision -fastappkit migrate app payments upgrade --revision abc123 +# External app: preview SQL +fastappkit migrate app payments preview --revision head -# External app - preview SQL -fastappkit migrate app payments preview - -# External app - downgrade (revision required) -fastappkit migrate app payments downgrade abc123 +# External app: downgrade +fastappkit migrate app payments downgrade --revision abc123 ``` -### `fastappkit migrate preview` +**Notes:** -Preview SQL for core + internal app migrations (dry-run). +- Must be run from project root directory +- `makemigrations` only works for internal apps +- External apps must create migrations independently using `alembic` + +### `fastappkit migrate all` + +Apply all migrations in order: core + internal apps → external apps. **Syntax:** ```bash -fastappkit migrate preview [--revision ] +fastappkit migrate all ``` -**Options:** - -- `--revision, -r `: Specific revision (default: `head`) - -**Note:** This command must be run from the project root directory. - **Examples:** ```bash -fastappkit migrate preview -fastappkit migrate preview --revision abc123 +fastappkit migrate all ``` +**Notes:** + +- Must be run from project root directory +- Execution order: Core → Internal apps (skipped, already in core) → External apps +- Recommended command for normal workflows + ### `fastappkit migrate upgrade` Apply core + internal app migrations. @@ -277,14 +284,12 @@ Apply core + internal app migrations. **Syntax:** ```bash -fastappkit migrate upgrade [--revision ] +fastappkit migrate upgrade [--revision ] ``` **Options:** -- `--revision, -r `: Specific revision (default: `head`) - -**Note:** This command must be run from the project root directory. +- `--revision, -r `: Specific revision (default: `head`) **Examples:** @@ -293,9 +298,14 @@ fastappkit migrate upgrade fastappkit migrate upgrade --revision abc123 ``` +**Notes:** + +- Must be run from project root directory +- Applies migrations from shared directory (`core/db/migrations/`) + ### `fastappkit migrate downgrade` -Revert core + internal app migrations. +Downgrade core + internal app migrations. **Syntax:** @@ -307,53 +317,61 @@ fastappkit migrate downgrade - ``: Revision to downgrade to (required) -**Note:** This command must be run from the project root directory. - **Examples:** ```bash fastappkit migrate downgrade abc123 ``` -### `fastappkit migrate all` +**Notes:** + +- Must be run from project root directory +- Downgrades migrations from shared directory + +### `fastappkit migrate preview` -Apply all migrations in the correct order. +Preview SQL for core + internal app migrations. **Syntax:** ```bash -fastappkit migrate all +fastappkit migrate preview [--revision ] ``` -**Note:** This command must be run from the project root directory. - -**Execution Order:** +**Options:** -1. Core migrations (always first) -2. Internal apps (in config order) - skipped (already included in core migrations) -3. External apps (in config order) +- `--revision, -r `: Specific revision (default: `head`) **Examples:** ```bash -fastappkit migrate all +fastappkit migrate preview +fastappkit migrate preview --revision abc123 ``` -## Command Reference Table - -| Command | Description | Options | -| ------------------------------------ | ---------------------- | ------------------------------------------------------------------------------------------- | -| `fastappkit core new ` | Create new project | `--project-root`, `--description` | -| `fastappkit core dev` | Run development server | `--host`, `--port`, `--reload`, `--verbose`, `--debug`, `--quiet`, plus any uvicorn options | -| `fastappkit app new ` | Create internal app | `--as-package` | -| `fastappkit app list` | List all apps | `--verbose`, `--debug`, `--quiet` | -| `fastappkit app validate ` | Validate app | `--json` | -| `fastappkit migrate core` | Core migrations | `-m, --message` | -| `fastappkit migrate app ` | App migrations | `makemigrations`, `upgrade`, `downgrade`, `preview` | -| `fastappkit migrate preview` | Preview SQL | `--revision` | -| `fastappkit migrate upgrade` | Upgrade migrations | `--revision` | -| `fastappkit migrate downgrade ` | Downgrade migrations | (revision required) | -| `fastappkit migrate all` | Apply all migrations | (no options) | +**Notes:** + +- Must be run from project root directory +- Shows SQL without applying migrations +- Useful for reviewing changes before applying + +## Command Summary + +| Command | Purpose | Must Run From Project Root | +|---------|---------|----------------------------| +| `fastappkit core new ` | Create new project | No | +| `fastappkit core dev` | Run development server | Yes | +| `fastappkit app new ` | Create internal app | Yes | +| `fastappkit app new --as-package` | Create external app | No | +| `fastappkit app list` | List all apps | Yes | +| `fastappkit app validate ` | Validate app | Yes | +| `fastappkit migrate core -m "msg"` | Create core migration | Yes | +| `fastappkit migrate app makemigrations -m "msg"` | Create app migration | Yes (internal only) | +| `fastappkit migrate app upgrade` | Apply app migration | Yes (external) | +| `fastappkit migrate all` | Apply all migrations | Yes | +| `fastappkit migrate upgrade` | Apply core + internal | Yes | +| `fastappkit migrate downgrade ` | Downgrade | Yes | +| `fastappkit migrate preview` | Preview SQL | Yes | ## Learn More diff --git a/docs/reference/configuration-reference.md b/docs/reference/configuration-reference.md index 60d5780..e2ddc83 100644 --- a/docs/reference/configuration-reference.md +++ b/docs/reference/configuration-reference.md @@ -1,57 +1,56 @@ # Configuration Reference -Complete reference for fastappkit configuration options. +Quick lookup for all fastappkit configuration options. -## Project Configuration +## Project Configuration (`fastappkit.toml`) -fastappkit uses `fastappkit.toml` for project configuration. This file is separate from `pyproject.toml`. +### Location -### Basic Structure +Project root (where you run `fastappkit` commands). + +### Schema ```toml [tool.fastappkit] apps = [ - "apps.blog", # Internal app - "apps.auth", # Internal app - "fastapi_payments", # External app (pip-installed package) + "apps.blog", # Internal app + "fastapi_payments", # External app ] + +[tool.fastappkit.migration] +order = ["core", "auth", "blog"] # Optional: override internal app order ``` -### App Entry Formats +### Options -- `apps.` → Internal app (located in `./apps//`) -- `` → External app (pip-installed package, must be importable) +| Option | Type | Required | Default | Description | +|--------|------|----------|---------|-------------| +| `apps` | `array[string]` | Yes | `[]` | List of app entries | +| `migration.order` | `array[string]` | No | apps order | Migration execution order (internal apps only) | -### Migration Order +### App Entry Formats -Override the order in which internal app migrations are applied: +- **Internal app**: `apps.` (resolves to `./apps//`) +- **External app**: `` (must be pip-installed, importable) -```toml -[tool.fastappkit.migration] -order = ["core", "auth", "blog"] -``` - -If not specified, internal apps are processed in the order they appear in the `apps` list. +## Settings Configuration (`core/config.py`) -## Settings Configuration +### Required Settings -Settings are defined in `core/config.py` using Pydantic's `BaseSettings`. +| Setting | Type | Required | Default | Env Var | Description | +|---------|------|----------|---------|---------|-------------| +| `database_url` | `str` | Yes | `sqlite:///./app.db` | `DATABASE_URL` | Database connection string | +| `debug` | `bool` | Yes | `False` | `DEBUG` | Debug mode flag | -### Basic Settings +### Settings Class Structure ```python from pydantic import Field from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): - database_url: str = Field( - default="sqlite:///./app.db", - alias="DATABASE_URL" - ) - debug: bool = Field( - default=False, - alias="DEBUG" - ) + database_url: str = Field(default="sqlite:///./app.db", alias="DATABASE_URL") + debug: bool = Field(default=False, alias="DEBUG") model_config = SettingsConfigDict( env_file=".env", @@ -60,76 +59,133 @@ class Settings(BaseSettings): ) ``` -### Extended Settings +### Common Custom Settings -```python -from pydantic import Field +- `secret_key: str` - Secret key for encryption/signing +- `host: str` - Server host +- `port: int` - Server port +- `redis_url: str` - Redis connection string +- `email_settings: EmailConfig` - Nested email configuration +- `logging_level: str` - Logging verbosity -class Settings(BaseSettings): - database_url: str = Field( - default="sqlite:///./app.db", - alias="DATABASE_URL" - ) - debug: bool = Field( - default=False, - alias="DEBUG" - ) +### Environment Variables - # Custom settings - secret_key: str = Field( - default="change-me-in-production", - alias="SECRET_KEY" - ) - host: str = Field(default="127.0.0.1", alias="HOST") - port: int = Field(default=8000, alias="PORT") +- **Location**: `.env` file in project root +- **Format**: `KEY=value` (uppercase with underscores) +- **Precedence**: Environment variables > `.env` file > defaults - # Nested settings - class DatabaseConfig(BaseSettings): - host: str = "localhost" - port: int = 5432 - name: str = "mydb" +## External App Manifest (`//fastappkit.toml`) - database: DatabaseConfig = DatabaseConfig() +### Location - model_config = SettingsConfigDict( - env_file=".env", - env_file_encoding="utf-8", - populate_by_name=True, - extra="ignore", - ) +Package directory (`//fastappkit.toml`). + +### Schema + +```toml +[tool.fastappkit] +name = "payments" +version = "0.1.0" +entrypoint = "payments:register" +migrations = "migrations" +models_module = "payments.models" +route_prefix = "/payments" ``` +### Fields + +| Field | Type | Required | Default | Description | +|-------|------|----------|---------|-------------| +| `name` | `string` | Yes | - | App name (must match package) | +| `version` | `string` | Yes | - | Semantic version | +| `entrypoint` | `string` | Yes | - | Dotted path to register function | +| `migrations` | `string` | Yes | - | Path to migrations directory | +| `models_module` | `string` | Recommended | - | Dotted path to models module | +| `route_prefix` | `string` | No | `/` | Router mount prefix | + +### Entrypoint Formats + +- `"module:function"` - Function in module (e.g., `"payments:register"`) +- `"module:Class"` - Class with `register` method (e.g., `"payments:App"`) +- Defaults to `"module:register"` if just module name + +## Quick Reference Tables + +### Project Configuration Options + +| Section | Option | Type | Required | Description | +|---------|--------|------|----------|-------------| +| `[tool.fastappkit]` | `apps` | `array[string]` | Yes | List of apps | +| `[tool.fastappkit.migration]` | `order` | `array[string]` | No | Internal app migration order | + ### Settings Options -**BaseSettings Configuration:** +| Setting | Type | Required | Default | Env Var | +|---------|------|----------|---------|---------| +| `database_url` | `str` | Yes | `sqlite:///./app.db` | `DATABASE_URL` | +| `debug` | `bool` | Yes | `False` | `DEBUG` | -- `env_file`: Path to `.env` file (default: `.env`) -- `env_file_encoding`: Encoding for `.env` file (default: `utf-8`) -- `case_sensitive`: Whether environment variable names are case-sensitive (default: `False`) -- `extra`: How to handle extra fields (`"ignore"`, `"forbid"`, `"allow"`) +### Manifest Fields -### Environment Variables +| Field | Type | Required | Default | +|-------|------|----------|---------| +| `name` | `string` | Yes | - | +| `version` | `string` | Yes | - | +| `entrypoint` | `string` | Yes | - | +| `migrations` | `string` | Yes | - | +| `models_module` | `string` | Recommended | - | +| `route_prefix` | `string` | No | `/` | -Settings are loaded from `.env` file and environment variables: +## Examples -```bash -# .env -DATABASE_URL=postgresql://user:password@localhost:5432/mydb -DEBUG=false -SECRET_KEY=your-secret-key-here +### Complete Project Configuration + +```toml +[tool.fastappkit] +apps = [ + "apps.blog", + "apps.auth", + "fastapi_payments", +] + +[tool.fastappkit.migration] +order = ["core", "auth", "blog"] ``` -Environment variables take precedence over `.env` file values. +### Complete Settings Configuration -## External App Manifest +```python +from pydantic import Field +from pydantic_settings import BaseSettings, SettingsConfigDict + +class Settings(BaseSettings): + database_url: str = Field(default="sqlite:///./app.db", alias="DATABASE_URL") + debug: bool = Field(default=False, alias="DEBUG") -External apps must declare metadata in `fastappkit.toml` located inside the package directory. + secret_key: str = Field(default="", alias="SECRET_KEY") + redis_url: str = Field(default="redis://localhost:6379", alias="REDIS_URL") + + model_config = SettingsConfigDict( + env_file=".env", + env_file_encoding="utf-8", + populate_by_name=True + ) +``` -**Location:** `//fastappkit.toml` +### Complete Manifest Configuration -See the [Manifest Reference](manifest-reference.md) for complete details. +```toml +[tool.fastappkit] +name = "payments" +version = "0.1.0" +entrypoint = "payments:register" +migrations = "migrations" +models_module = "payments.models" +route_prefix = "/api/payments" +``` ## Learn More -- [Configuration Guide](../guides/configuration.md) - Configuration guide -- [Manifest Reference](manifest-reference.md) - External app manifest schema +- [Project Configuration](../configuration/project-config.md) - Complete `fastappkit.toml` reference +- [Settings Configuration](../configuration/settings.md) - Complete `core/config.py` guide +- [External App Manifest](../configuration/external-app-manifest.md) - Manifest format reference diff --git a/docs/reference/manifest-reference.md b/docs/reference/manifest-reference.md deleted file mode 100644 index a386472..0000000 --- a/docs/reference/manifest-reference.md +++ /dev/null @@ -1,102 +0,0 @@ -# Manifest Reference - -Complete reference for external app manifest files. - -## Location - -External apps must declare metadata in `fastappkit.toml` located inside the package directory: - -**Path:** `//fastappkit.toml` - -For example, if your package is named `myapp`, the manifest should be at: - -``` -myapp/ -└── myapp/ - ├── __init__.py - ├── fastappkit.toml # Manifest here - └── ... -``` - -!!! important "Manifest Requirements" - The manifest file is `fastappkit.toml`, not `pyproject.toml`. It must be located in the package directory (where `__init__.py` is). This ensures it's included when the package is published to PyPI. No fallback - `fastappkit.toml` is required for external apps. - -## Schema - -### Required Fields - -#### `name` - -- **Type:** `string` -- **Description:** App name (must match package name) -- **Example:** `"blog"` - -#### `version` - -- **Type:** `string` -- **Description:** Semantic version (e.g., `"0.1.0"`) -- **Example:** `"0.1.0"` - -#### `entrypoint` - -- **Type:** `string` -- **Description:** Dotted path to register function (e.g., `"blog:register"`) -- **Formats:** - - `"blog:register"` - Function in module - - `"blog:App"` - Class (must have `register` method) - - `"blog"` - Defaults to `"blog:register"` - - `"blog.main:register"` - Function in submodule -- **Example:** `"blog:register"` - -#### `migrations` - -- **Type:** `string` -- **Description:** Path to migrations directory (relative to package directory, typically `"migrations"`) -- **Example:** `"migrations"` - -#### `models_module` - -- **Type:** `string` -- **Description:** Dotted path to models module (recommended) -- **Example:** `"blog.models"` - -### Optional Fields - -#### `route_prefix` - -- **Type:** `string` -- **Description:** Router prefix (default: `/`) -- **Example:** `"/api/blog"` -- **Special:** Use empty string `""` to mount at root level - -## Example - -```toml -name = "blog" -version = "0.1.0" -entrypoint = "blog:register" -migrations = "migrations" -models_module = "blog.models" -route_prefix = "/blog" -``` - -## Validation - -The manifest is validated when: - -- Loading apps at startup -- Running `fastappkit app validate ` - -### Validation Checks - -- Required fields are present -- Field types are correct -- Entrypoint is importable -- Entrypoint has correct signature -- Migrations directory exists -- Models module is importable (if specified) - -## Learn More - -- [External Apps](../topics/external-apps.md) - Understanding external apps -- [Creating Apps](../guides/creating-apps.md) - How to create external apps diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css new file mode 100644 index 0000000..5fb4c3b --- /dev/null +++ b/docs/stylesheets/extra.css @@ -0,0 +1,78 @@ +/* Custom styles for mkdocs readthedocs theme */ + +/* Bottom sidebar section - Repository info and navigation */ +/* Only style colors and appearance, don't change positioning */ +.rst-versions, +.rst-current-version { + background-color: #1f1f1f !important; + border-top: 1px solid #3a3a3a !important; + color: #ffffff !important; +} + +.rst-versions .rst-other-versions { + color: #cccccc !important; +} + +.rst-versions .rst-other-versions dd a { + color: #4a9eff !important; +} + +.rst-versions .rst-other-versions dd a:hover { + color: #6bb3ff !important; +} + +/* Repository name/link styling */ +.rst-current-version { + background-color: #1f1f1f !important; + color: #ffffff !important; + padding: 10px 15px !important; +} + +.rst-current-version .fa-book, +.rst-current-version .icon-book { + color: #4a9eff !important; +} + +/* Previous/Next navigation at bottom */ +.rst-footer-buttons { + background-color: #1f1f1f !important; + border-top: 1px solid #3a3a3a !important; + padding: 10px 15px !important; +} + +.rst-footer-buttons a { + color: #4a9eff !important; + text-decoration: none !important; +} + +.rst-footer-buttons a:hover { + color: #6bb3ff !important; + text-decoration: underline !important; +} + +/* Ensure proper spacing and alignment */ +.rst-versions, +.rst-current-version, +.rst-footer-buttons { + font-size: 14px !important; + line-height: 1.5 !important; +} + +/* Fix any overflow issues - but don't change positioning */ +.rst-versions { + overflow-y: auto !important; + max-height: 200px !important; +} + +/* Style the version selector if present */ +.rst-versions select { + background-color: #2a2a2a !important; + color: #ffffff !important; + border: 1px solid #3a3a3a !important; + padding: 5px 10px !important; + border-radius: 3px !important; +} + +.wy-nav-side { + padding-bottom: 50px !important; +} diff --git a/docs/topics/app-isolation.md b/docs/topics/app-isolation.md index 76a3da0..4fa16cb 100644 --- a/docs/topics/app-isolation.md +++ b/docs/topics/app-isolation.md @@ -1,9 +1,11 @@ # App Isolation -fastappkit enforces isolation rules to ensure apps don't conflict with each other and maintain clear boundaries. +Isolation rules and validation that ensure apps don't conflict with each other. ## Isolation Rules +fastappkit enforces isolation rules to ensure apps don't conflict with each other and maintain clear boundaries. + ``` ┌──────────────┐ cannot depend ┌──────────────┐ │ Internal App │ ───────────────────>│ External App │ @@ -20,74 +22,177 @@ fastappkit enforces isolation rules to ensure apps don't conflict with each othe **Allowed:** -- Can depend on other internal apps -- Can modify shared tables -- Participate in shared migration timeline (use core's migration directory) -- No manifest file required (metadata inferred from structure) +- Can depend on other internal apps +- Can import from `core.models` +- Can modify shared tables +- Participate in shared migration timeline (use core's migration directory) +- No manifest file required (metadata inferred from structure) **Restricted:** -- Cannot depend on external apps +- Cannot depend on external apps ## External Apps **Allowed:** -- Independent schema evolution (isolated migrations) -- Can be published to PyPI -- Must be pip-installed (no filesystem path support) -- Must have `fastappkit.toml` manifest in package directory +- Independent schema evolution (isolated migrations) +- Can be published to PyPI +- Must be pip-installed (no filesystem path support) +- Must have `fastappkit.toml` manifest in package directory +- Can import from `fastappkit` public API **Restricted:** -- Cannot depend on internal apps -- Cannot touch other apps' tables -- Cannot modify core or internal tables -- Cannot create migrations from core project (must use Alembic directly) +- Cannot depend on internal apps +- Cannot import from `core.*` or `apps.*` +- Cannot touch other apps' tables +- Cannot modify core or internal tables +- Cannot create migrations from core project (must use Alembic directly) +- Must use own `Base` class (DeclarativeBase), not `core.models.Base` ## Validation fastappkit validates isolation rules when: -- Loading apps at startup -- Running `fastappkit app validate ` +- Loading apps at startup +- Running `fastappkit app validate ` ### Isolation Checks The isolation validator checks: -1. **Import Analysis:** Scans Python files for imports -2. **Dependency Detection:** Identifies imports from other apps -3. **Rule Enforcement:** Validates against isolation rules -4. **Error Reporting:** Provides clear error messages for violations +1. **Import Analysis**: Scans Python files for imports +2. **Dependency Detection**: Identifies imports from other apps +3. **Rule Enforcement**: Validates against isolation rules + +### What Gets Checked + +**Manifest Validation:** +- Required fields are present +- Field types are correct +- Entrypoint is importable +- Entrypoint has correct signature + +**Isolation Validation:** +- External apps: No imports from `core.*` or `apps.*` +- External apps: Uses own Base class (not `core.models.Base`) +- Import analysis of Python files -### Example Violation +**Migration Validation:** +- Migrations directory exists +- `migrations/env.py` exists (external apps) +- Version table is correct (`alembic_version_` for external apps) -If an external app tries to import from an internal app: +### Common Violations + +#### External App Importing from Core ```python -# external_app/models.py -from apps.blog.models import Post # Violation: External apps cannot import internal apps +# WRONG: External app +from core.models import Base # Violation! -class Comment(Base): - post_id = Column(Integer, ForeignKey("posts.id")) +# CORRECT: External app +from sqlalchemy.orm import DeclarativeBase +class Base(DeclarativeBase): + pass ``` -Validation will fail with: +#### External App Importing from Internal App + +```python +# WRONG: External app +from apps.blog.models import Post # Violation! +# CORRECT: External app +# Don't import from internal apps ``` -Isolation violation: External app 'external_app' cannot import from internal app 'apps.blog' + +#### External App Using Shared Base + +```python +# WRONG: External app +from core.models import Base # Violation! + +# CORRECT: External app +from sqlalchemy.orm import DeclarativeBase +class Base(DeclarativeBase): + pass +``` + +#### Missing Manifest + +**Error**: `AppLoadError: Failed to load manifest` + +**Solution**: Ensure `fastappkit.toml` exists in package directory. + +#### Wrong Version Table + +**Error**: Version table conflicts + +**Solution**: External apps must use `alembic_version_`, not `alembic_version`. + +## Why Isolation Matters + +### Prevents Schema Conflicts + +- External apps can't modify core/internal tables +- Each external app has isolated migrations +- Clear boundaries prevent conflicts + +### Enables Independent Development + +- External apps can be developed separately +- Can be published to PyPI +- Can be versioned independently + +### Allows Publishing to PyPI + +- External apps are self-contained +- No dependencies on project-specific code +- Can be shared across projects + +### Maintains Clear Boundaries + +- Clear separation of concerns +- Predictable behavior +- Easier to reason about + +## Breaking Scenarios + +### External App Imports from Internal App + +**Error**: Isolation validation error + +**Fix**: Remove import, redesign if needed. + +### External App Uses Shared Base + +**Error**: Migration conflicts, version table issues + +**Fix**: Use own Base class (DeclarativeBase). + +### Missing Isolation Checks + +**Problem**: Runtime errors, unexpected behavior + +**Fix**: Always run `fastappkit app validate ` before using apps. + +## Running Validation + +```bash +fastappkit app validate ``` -## Best Practices +**Must be run from project root.** -1. Keep external apps truly independent -2. Use well-defined interfaces for cross-app communication -3. Validate apps before deploying -4. Document dependencies clearly +**Output:** +- Errors: Must be fixed +- Warnings: Should be reviewed +- JSON output: `--json` flag for CI ## Learn More -- [Internal Apps](internal-apps.md) - Internal app constraints -- [External Apps](external-apps.md) - External app constraints -- [Creating Apps](../guides/creating-apps.md) - App creation guide +- [Creating Apps](../guides/creating-apps.md) - App creation guide +- [External Apps](external-apps.md) - External app details +- [Internal Apps](internal-apps.md) - Internal app details diff --git a/docs/topics/external-apps.md b/docs/topics/external-apps.md index 2f22fb7..93067d3 100644 --- a/docs/topics/external-apps.md +++ b/docs/topics/external-apps.md @@ -1,15 +1,15 @@ # External Apps -External apps are reusable packages that can be installed via pip and plugged into any fastappkit project. +Deep dive into external apps - reusable packages that can be installed via pip and plugged into any fastappkit project. ## Characteristics -- Installed via `pip install ` (must be pip-installed, no filesystem paths) -- Independent Python packages -- Must be schema-independent -- Cannot depend on internal apps -- Use per-app version tables (`alembic_version_`) -- Perfect for reusable plugins +- Installed via `pip install ` (must be pip-installed, no filesystem paths) +- Independent Python packages +- Must be schema-independent +- Cannot depend on internal apps +- Use per-app version tables (`alembic_version_`) +- Perfect for reusable plugins ## Structure @@ -33,11 +33,12 @@ External apps are reusable packages that can be installed via pip and plugged in External apps must declare metadata in `fastappkit.toml` located inside the package directory. -**Location:** `//fastappkit.toml` +**Location**: `//fastappkit.toml` **Example:** ```toml +[tool.fastappkit] name = "blog" version = "0.1.0" entrypoint = "blog:register" @@ -46,7 +47,7 @@ models_module = "blog.models" route_prefix = "/blog" ``` -See the [Manifest Reference](../reference/manifest-reference.md) for complete details. +See the [Manifest Reference](../reference/configuration-reference.md) for complete details. ## Models @@ -55,6 +56,7 @@ External apps must use their own `Base` class (isolated metadata): ```python # //models.py from sqlalchemy.orm import DeclarativeBase +from sqlalchemy import Column, Integer, String class Base(DeclarativeBase): pass @@ -90,6 +92,8 @@ def register(app: FastAPI) -> APIRouter: return router ``` +**Signature**: Same as internal apps: `register(app: FastAPI) -> APIRouter | None` + ## Installation External apps must be pip-installed: @@ -129,13 +133,49 @@ From the core project, you can apply existing migrations: fastappkit migrate app upgrade ``` +**Version Table**: Each external app uses `alembic_version_` (isolated). + ## Publishing External apps can be published to PyPI. The `fastappkit.toml` manifest is included in the package, ensuring it's available when installed. +**Requirements:** +- Manifest must be in package directory +- Migrations must be included in package +- `__init__.py` must exist in package directory + +## Best Practices + +1. **Design for reusability** + - Keep dependencies minimal + - Avoid project-specific code + - Document requirements clearly + +2. **Isolation** + - Use own Base class + - Don't import from core or internal apps + - Keep migrations isolated + +3. **Versioning** + - Use semantic versioning + - Document breaking changes + - Match dependency versions with core project + +4. **Documentation** + - Clear README + - Installation instructions + - Usage examples + +## Limitations + +- **Must be pip-installed** (no filesystem paths) +- **Cannot depend on internal apps** (isolation rule) +- **Isolated migrations** (cannot share with core) +- **Independent development** (migrations created separately) + ## Learn More -- [Creating Apps](../guides/creating-apps.md) - How to create external apps -- [Migration System](migration-system.md) - How migrations work for external apps -- [App Isolation](app-isolation.md) - Isolation rules and constraints -- [Manifest Reference](../reference/manifest-reference.md) - Complete manifest schema +- [Creating Apps](../guides/creating-apps.md) - How to create external apps +- [Migration System](migration-system.md) - How migrations work for external apps +- [App Isolation](app-isolation.md) - Isolation rules and constraints +- [Manifest Reference](../configuration/external-app-manifest.md) - Complete manifest schema diff --git a/docs/topics/internal-apps.md b/docs/topics/internal-apps.md index 0413a45..53d4706 100644 --- a/docs/topics/internal-apps.md +++ b/docs/topics/internal-apps.md @@ -1,15 +1,15 @@ # Internal Apps -Internal apps are project-specific modules that live in your project's `apps/` directory. +Deep dive into internal apps - project-specific modules that live in your `apps/` directory. ## Characteristics -- Located in `./apps//` -- Part of your project's codebase -- Share the project's migration timeline -- Can depend on other internal apps -- Use shared `alembic_version` table -- Not meant for packaging (unless explicitly opted in) +- Located in `./apps//` +- Part of your project's codebase +- Share the project's migration timeline +- Can depend on other internal apps +- Use shared `alembic_version` table +- Not meant for packaging (unless explicitly opted in) ## Structure @@ -37,6 +37,8 @@ class Post(Base): content = Column(String) ``` +**CRITICAL**: Must import `Base` from `core.models`, not create own. + ## Registration Internal apps register with the FastAPI application via a `register()` function: @@ -55,9 +57,13 @@ def list_posts(): def register(app: FastAPI) -> APIRouter: """Register this app with the FastAPI application.""" settings = get_settings() - return router + return router # Return router for auto-mount ``` +**Signature**: `register(app: FastAPI) -> APIRouter | None` +- Return `APIRouter` → auto-mount with prefix +- Return `None` → manual mount (app handles mounting itself) + ## Migrations Internal apps share the core's migration directory (`core/db/migrations/`). They do not have their own `migrations/` directory. @@ -70,6 +76,15 @@ fastappkit migrate app blog makemigrations -m "Add post model" The migration is created in `core/db/migrations/versions/`, not in `apps/blog/migrations/`. +**Migration Order:** +- From `fastappkit.toml` apps order, or +- From `[tool.fastappkit.migration.order]` if specified + +**Autogenerate Behavior:** +- Sees all internal app models +- Sees all core models +- Can detect cross-app relationships + ## Dependencies Internal apps can depend on other internal apps: @@ -77,6 +92,8 @@ Internal apps can depend on other internal apps: ```python # apps/blog/models.py from apps.auth.models import User # OK - internal app can import internal app +from core.models import Base +from sqlalchemy import Column, Integer, ForeignKey class Post(Base): __tablename__ = "posts" @@ -86,8 +103,33 @@ class Post(Base): !!! warning "External App Dependencies" Internal apps cannot depend on external apps. This is enforced by isolation validation. +## Best Practices + +1. **One app per feature/domain** + - Keep apps focused on a single responsibility + - Group related functionality together + +2. **Clear naming** + - Use descriptive names: `blog`, `user_auth`, `payment_processing` + - Avoid generic names: `app1`, `module1` + +3. **Organize by domain** + - Group related models and routes together + - Keep app boundaries clear + +4. **Use relationships wisely** + - Cross-app relationships are allowed + - But consider if apps should be merged if tightly coupled + +## Limitations + +- **Cannot be published to PyPI** (not designed for packaging) +- **Shared migrations** (all internal apps use same migration directory) +- **Cannot depend on external apps** (isolation rule) +- **Project-specific** (tied to project's codebase) + ## Learn More -- [Creating Apps](../guides/creating-apps.md) - How to create internal apps -- [Migration System](migration-system.md) - How migrations work for internal apps -- [App Isolation](app-isolation.md) - Isolation rules and constraints +- [Creating Apps](../guides/creating-apps.md) - How to create internal apps +- [Migration System](migration-system.md) - How migrations work for internal apps +- [App Isolation](app-isolation.md) - Isolation rules and constraints diff --git a/docs/topics/migration-system.md b/docs/topics/migration-system.md index 49158c3..9a426ab 100644 --- a/docs/topics/migration-system.md +++ b/docs/topics/migration-system.md @@ -1,8 +1,10 @@ # Migration System -fastappkit uses a unified migration runner that coordinates database schema management across core, internal apps, and external apps. +How fastappkit's migration system works internally. + +## Migration Architecture -## Architecture +fastappkit uses a unified migration runner that coordinates database schema management across core, internal apps, and external apps. ``` ┌─────────────────────────────────────────────┐ @@ -28,56 +30,75 @@ CORE MIGRATIONS APP MIGRATIONS ## Version Tables -- **Core & Internal Apps:** Use shared `alembic_version` table -- **External Apps:** Use per-app tables (`alembic_version_`) +- **Core & Internal Apps**: Use shared `alembic_version` table +- **External Apps**: Use per-app tables (`alembic_version_`) + +This isolation ensures external apps don't conflict with each other or with core/internal migrations. ## Migration Execution Flow +### For Core + Internal Apps + +1. **Load Migration Directory** + - Location: `core/db/migrations/` + - Shared by core and all internal apps + +2. **Build Revision Graph** + - Scan `versions/` directory for migration files + - Parse revision IDs and dependencies + - Determine `head` revision + +3. **Check Database State** + - Connect to database + - Check `alembic_version` table + - Read current revision + +4. **Determine Upgrade Path** + - Compare current revision with `head` + - Build list of migrations to apply (in order) + +5. **Execute Migrations** + - For each migration: execute `upgrade()`, update version table, commit + ### For External App Upgrade 1. **App Resolution & Validation** - - Resolve app from `fastappkit.toml` - - Load manifest - - Validate app exists and is external type - - Get `migrations_path` from manifest + - Resolve app from `fastappkit.toml` + - Load manifest + - Validate app exists and is external type + - Get `migrations_path` from manifest 2. **Build Alembic Config** - - Set `script_location` = external app's migrations directory - - Set `version_table` = `alembic_version_` - - Set `sqlalchemy.url` = core project's `DATABASE_URL` (from settings) - - Set `config_file_name = None` (don't read external app's `alembic.ini`) + - Set `script_location` = external app's migrations directory + - Set `version_table` = `alembic_version_` + - Set `sqlalchemy.url` = core project's `DATABASE_URL` (from settings) + - Set `config_file_name = None` (don't read external app's `alembic.ini`) 3. **Load Migration Scripts** - - Create `ScriptDirectory` from migration folder - - Scan `migrations/versions/` for `.py` files - - Parse revision IDs and build revision graph - - Determine `head` revision + - Create `ScriptDirectory` from migration folder + - Scan `migrations/versions/` for `.py` files + - Parse revision IDs and build revision graph + - Determine `head` revision 4. **Check Database State** - - Connect to core project's database - - Check if `alembic_version_` table exists - - Read current revision (or `None` if first time) + - Connect to core project's database + - Check if `alembic_version_` table exists + - Read current revision (or `None` if first time) 5. **Determine Upgrade Path** - - Compare current revision with `head` - - Build list of migrations to apply (in order) + - Compare current revision with `head` + - Build list of migrations to apply (in order) 6. **Execute Migrations** - - For each migration: execute `upgrade()`, update version table, commit - - Create version table if it doesn't exist (first migration) - -### Edge Cases - -- Database has revision `X` but migration file doesn't exist → Error -- Migration file references non-existent `down_revision` → Error -- Empty migration directory → Error (external apps must have migrations) + - For each migration: execute `upgrade()`, update version table, commit + - Create version table if it doesn't exist (first migration) ## Migration Ordering Migrations run in this order: 1. **Core** (always first) -2. **Internal apps** (order from `fastappkit.toml`) +2. **Internal apps** (order from `fastappkit.toml` or `[tool.fastappkit.migration.order]`) 3. **External apps** (order from `fastappkit.toml`) You can override internal app order in config: @@ -87,30 +108,62 @@ You can override internal app order in config: order = ["core", "auth", "blog"] ``` +**Note**: `"core"` is a special value (always first). Only affects internal apps. + ## Autogenerate Behavior ### Internal Apps Autogenerate sees: -- All internal app models -- All core models -- Cross-app relations +- All internal app models +- All core models +- Cross-app relationships -Good for teams working on shared schema. +**Good for**: Teams working on shared schema. ### External Apps Autogenerate sees: -- Only that app's own models -- No internal app models -- No other external apps' models -- No core models +- Only that app's own models +- No internal app models +- No other external apps' models +- No core models + +**If an external app tries to import foreign models** → validation error. + +## Edge Cases + +### Database Has Revision But File Missing + +**Error**: `Can't locate revision identified by '...'` + +**Cause**: Database has revision stored but migration file doesn't exist. + +**Solution**: +- Check migration files exist +- Restore missing migration file +- Or reset version table (if safe) + +### Migration File References Non-Existent `down_revision` + +**Error**: Migration dependency chain broken + +**Solution**: +- Review migration files +- Fix `down_revision` references +- Ensure migration chain is complete + +### Empty Migration Directory + +**Problem**: External app has no migrations -If an external app tries to import foreign models → validation error. +**Solution**: +- Create initial migration: `alembic revision --autogenerate -m "initial"` +- Ensure `migrations/versions/` directory exists ## Learn More -- [Migrations Guide](../guides/migrations.md) - How to work with migrations -- [CLI Reference](../reference/cli-reference.md) - Migration commands +- [Migrations Guide](../guides/migrations.md) - Migration workflows +- [CLI Reference](../reference/cli-reference.md) - Migration commands diff --git a/docs/topics/router-mounting.md b/docs/topics/router-mounting.md index 668c833..23af24f 100644 --- a/docs/topics/router-mounting.md +++ b/docs/topics/router-mounting.md @@ -1,151 +1,181 @@ # Router Mounting -fastappkit automatically mounts app routers with configurable prefixes. +How routers are automatically mounted in fastappkit applications. -## Architecture +## Automatic Mounting -``` -┌───────────────────── FastAPI app ───────────────────────┐ -│ │ -│ /core/... │ -│ │ -│ /blog/... <-- internal apps │ -│ /account/... │ -│ │ -│ /fastapi_blog/... <-- external apps │ -│ /fastapi_store/... │ -│ │ -└─────────────────────────────────────────────────────────┘ +If `register()` returns `APIRouter`, it's automatically mounted by fastappkit: + +```python +def register(app: FastAPI) -> APIRouter: + return router # Auto-mounted with prefix ``` -## Default Prefix +The prefix comes from: +1. Manifest `route_prefix` (if specified) +2. Default `/` (if not specified) -The default prefix is `/`: +## Route Prefixes -- `apps.blog` → `/blog` -- `fastapi_payments` → `/fastapi_payments` +### Default Prefix -## Override Methods +If not specified in manifest, default is `/`: + +```python +# App name: "blog" +# Default prefix: "/blog" +``` -### Via Manifest +### Custom Prefix + +Specify in manifest: ```toml -# In fastappkit.toml (external apps) or inferred (internal apps) +# fastappkit.toml (external app) route_prefix = "/api/blog" ``` -### Via Register Function +Or in internal app, you can mount manually: ```python def register(app: FastAPI) -> None: - # Mount with custom prefix, tags, dependencies - app.include_router( - router, - prefix="/api/v1/blog", - tags=["blog", "content"], - dependencies=[Depends(require_auth)] - ) + app.include_router(router, prefix="/api/blog") + return None # Manual mount ``` -### Empty Prefix +### Empty Prefix (Root Mount) + +Use empty string to mount at root level: ```toml route_prefix = "" # Mounts at root level ``` -## Customization Options +**Use with caution**: Can cause route collisions. -- **Tags:** Add OpenAPI tags: `tags=["blog", "content"]` -- **Dependencies:** Add route dependencies: `dependencies=[Depends(...)]` -- **Response Class:** Customize response: `response_class=CustomResponse` -- **Prefix Override:** Override manifest prefix in `register()` -- **Multiple Routers:** Mount multiple routers from same app -- **Sub-applications:** Mount sub-applications: `app.mount("/static", StaticFiles(...))` +## Registration Function -## Route Collision Detection +### Auto-Mount + +Return `APIRouter` from `register()`: + +```python +def register(app: FastAPI) -> APIRouter: + """Register this app with the FastAPI application.""" + return router # Auto-mounted with prefix from manifest +``` + +### Manual Mount + +Return `None` and mount yourself: + +```python +def register(app: FastAPI) -> None: + """Register this app with the FastAPI application.""" + app.include_router(router, prefix="/custom/prefix") + return None # Manual mount, fastappkit skips +``` + +## Mount Order + +Apps are mounted in order from `fastappkit.toml`: + +```toml +[tool.fastappkit] +apps = [ + "apps.blog", # Mounted first + "apps.auth", # Mounted second + "payments", # Mounted third +] +``` + +Route collision detection runs **after** all mounts. -fastappkit automatically detects overlapping routes (same path + method): +## Route Collision Detection -- Emits warnings (not fatal) - startup continues -- Shows which apps have conflicting routes -- Provides suggestions for resolution -- Developer responsibility to fix collisions +fastappkit detects route collisions and emits warnings (not fatal): -### Example Collision +``` +⚠️ ROUTE COLLISIONS DETECTED -If two apps use the same prefix: + Route '/api' used by multiple apps: + - blog (prefix: /api) + - auth (prefix: /api) + 💡 Fix: Change route_prefix for one of the apps ``` -ROUTE COLLISIONS DETECTED -🔴 Collision between apps: blog, news - App 'blog' uses prefix: /api - App 'news' uses prefix: /api - - Conflicting routes (2 found): - • GET /api/posts - • POST /api/posts + +**Detection:** +- Checks for overlapping route paths +- Warns but doesn't fail +- Developer responsibility to fix + +## Examples + +### Auto-Mount with Default Prefix + +```python +# apps/blog/__init__.py +router = APIRouter() + +@router.get("/posts") +def list_posts(): + return [] + +def register(app: FastAPI) -> APIRouter: + return router # Auto-mounted at "/blog" ``` -## Entrypoint Patterns +### Auto-Mount with Custom Prefix -### Function-based +```toml +# External app manifest +route_prefix = "/api/blog" +``` ```python -def register(app: FastAPI) -> APIRouter | None: - """Register app with FastAPI application. - - Can return APIRouter (fastappkit mounts it) or None (mount yourself). - """ - settings = get_settings() - # Option 1: Return router - return router - # Option 2: Mount yourself - # app.include_router(router, prefix="/blog") - # return None +def register(app: FastAPI) -> APIRouter: + return router # Auto-mounted at "/api/blog" ``` -### Class-based +### Manual Mount ```python -class App: - def register(self, app: FastAPI) -> APIRouter | None: - """Register app with FastAPI application.""" - settings = get_settings() - app.include_router(router, prefix="/blog") - return None # Or return router +def register(app: FastAPI) -> None: + app.include_router(router, prefix="/custom/prefix", tags=["blog"]) + return None # Manual mount +``` + +## Common Issues + +### Route Collisions + +**Warning**: `Route collision detected` + +**Solution**: Check `route_prefix` in manifests: +```toml +# Change one app's prefix +route_prefix = "/api/blog" # Instead of "/blog" ``` -### Entrypoint String Formats +### Duplicate App Names -- `"blog:register"` - Function in module -- `"blog:App"` - Class (must have `register` method) -- `"blog"` - Defaults to `"blog:register"` -- `"blog.main:register"` - Function in submodule +**Warning**: `Duplicate app names detected` -The loader instantiates class-based apps with no constructor arguments. +**Problem**: May cause route conflicts -## What `register()` Can Do +**Solution**: Rename one of the apps. -- Return `APIRouter` - fastappkit mounts it with prefix -- Mount routers yourself - fastappkit skips mounting -- Register startup/shutdown events: `@app.on_event("startup")` -- Add middleware: `app.add_middleware(...)` -- Add exception handlers: `app.add_exception_handler(...)` -- Add background tasks: `app.add_task(...)` -- Access settings via `get_settings()` -- Mount sub-applications: `app.mount(...)` -- Add dependencies, tags, etc. when mounting routers +### Router Not Mounting -## What `register()` Must Not Do +**Problem**: Routes not accessible -- Modify global FastAPI state outside its namespace -- Perform blocking operations at import time -- Connect to DB directly (use startup events instead) -- Access settings via global variables (use `get_settings()`) -- Raise exceptions that prevent other apps from loading +**Solution**: +- Check `register()` returns `APIRouter` (for auto-mount) +- Or verify manual mount in `register()` function +- Check app is in `fastappkit.toml` ## Learn More -- [Creating Apps](../guides/creating-apps.md) - How to create apps with routers -- [Internal Apps](internal-apps.md) - Internal app router patterns -- [External Apps](external-apps.md) - External app router patterns +- [Creating Apps](../guides/creating-apps.md) - App creation guide +- [CLI Reference](../reference/cli-reference.md) - Command reference diff --git a/docs/troubleshooting/common-issues.md b/docs/troubleshooting/common-issues.md index 0fa8006..1d116f8 100644 --- a/docs/troubleshooting/common-issues.md +++ b/docs/troubleshooting/common-issues.md @@ -1,10 +1,10 @@ # Common Issues -Solutions to common problems when using fastappkit. +Solutions to frequent problems when using fastappkit. ## App Fails to Load -**Error:** `AppLoadError: Failed to load app 'blog'` +**Error**: `AppLoadError: Failed to load app 'blog'` ### Check List @@ -25,14 +25,14 @@ Check error message for specific stage (resolve/load/register). ### Common Causes -- Missing `__init__.py` in app directory -- Incorrect entrypoint path in manifest -- Import errors in app code -- Missing dependencies +- Missing `__init__.py` in app directory +- Incorrect entrypoint path in manifest +- Import errors in app code +- Missing dependencies ## Migration Revision Not Found -**Error:** `Can't locate revision identified by '0d037769d7fb'` +**Error**: `Can't locate revision identified by '0d037769d7fb'` ### Possible Causes @@ -55,7 +55,7 @@ Ensure using core project's `DATABASE_URL` (not external app's). ## Route Collisions -**Warning:** `Route collision detected: /api used by multiple apps` +**Warning**: `Route collision detected: /api used by multiple apps` ### Solution @@ -77,7 +77,7 @@ Ensure each app has unique prefix. ## External App Cannot Create Migrations -**Error:** `Cannot create migrations for external app` +**Error**: `Cannot create migrations for external app` ### Explanation @@ -94,7 +94,7 @@ fastappkit migrate app upgrade ## Settings Not Loaded -**Error:** Settings-related errors at runtime +**Error**: Settings-related errors at runtime ### Solution @@ -114,23 +114,23 @@ For CLI commands, ensure you're in the project root directory. ## Import Errors -**Error:** `ModuleNotFoundError` or import-related errors +**Error**: `ModuleNotFoundError` or import-related errors ### Common Causes -- Missing `__init__.py` files -- Incorrect Python path -- Missing dependencies +- Missing `__init__.py` files +- Incorrect Python path +- Missing dependencies ### Solution -- Ensure all app directories have `__init__.py` -- Check that dependencies are installed -- Verify Python path includes project root +- Ensure all app directories have `__init__.py` +- Check that dependencies are installed +- Verify Python path includes project root ## Database Connection Errors -**Error:** Database connection failures +**Error**: Database connection failures ### Solution @@ -144,7 +144,7 @@ Verify database is running and accessible. ## Project Creation Errors -**Error:** `Directory already exists` +**Error**: `Directory already exists` ### Solution @@ -157,7 +157,7 @@ fastappkit core new myproject ## Settings Not Found -**Error:** Settings-related errors when running CLI commands +**Error**: Settings-related errors when running CLI commands ### Solution @@ -172,7 +172,7 @@ Verify `core/config.py` exists and contains a `Settings` class. ## App Not Found in Registry -**Error:** `App 'blog' not found in registry` +**Error**: `App 'blog' not found in registry` ### Solution @@ -183,7 +183,7 @@ Verify `core/config.py` exists and contains a `Settings` class. ## Migration Directory Not Found -**Error:** `Core migrations directory not found` +**Error**: `Core migrations directory not found` ### Solution @@ -195,25 +195,44 @@ ls -la core/db/migrations/ If missing, recreate the project or manually create the directory structure. -## Import Errors in Apps +## External App Not Pip-Installed -**Error:** `ModuleNotFoundError` or import errors when loading apps +**Error**: `AppLoadError: Could not resolve app entry` -### Common Causes +### Solution + +External apps must be pip-installed (even for local development): + +```bash +pip install -e /path/to/external/app +``` + +Then add to `fastappkit.toml` as package name (not path). + +## Duplicate App Names + +**Warning**: `Duplicate app names detected` + +### Solution + +Rename one of the apps or check if multiple entries resolve to same name: + +```bash +fastappkit app list # Check resolved names +``` + +## Route Not Accessible -- Missing `__init__.py` files -- Incorrect Python path -- Missing dependencies -- Circular imports +**Problem**: Routes return 404 ### Solution -- Ensure all app directories have `__init__.py` -- Check that dependencies are installed -- Verify Python path includes project root -- Review import statements for circular dependencies +1. Check app is in `fastappkit.toml` +2. Verify `register()` returns `APIRouter` (for auto-mount) +3. Check `route_prefix` in manifest +4. Verify app loaded: `fastappkit app list` ## Learn More -- [Debugging](debugging.md) - Debugging techniques -- [CLI Reference](../reference/cli-reference.md) - Command options and usage +- [Debugging](debugging.md) - Debugging techniques +- [CLI Reference](../reference/cli-reference.md) - All commands diff --git a/docs/troubleshooting/debugging.md b/docs/troubleshooting/debugging.md index 483b7b5..a646b02 100644 --- a/docs/troubleshooting/debugging.md +++ b/docs/troubleshooting/debugging.md @@ -1,132 +1,285 @@ # Debugging -Techniques for debugging fastappkit applications. +Debugging techniques for fastappkit applications. -## Verbose and Debug Output +## Using `--debug` Flag -### Enable Verbose Output +Enable debug output to see detailed error information: ```bash -fastappkit app list --verbose -fastappkit core dev --verbose +fastappkit core dev --debug +fastappkit app validate --debug +fastappkit migrate all --debug ``` -Shows detailed information about operations. +**What you get:** +- Stack traces +- Detailed error messages +- Full exception information +- Internal state information -### Enable Debug Output +## Logging Configuration -```bash -fastappkit app list --debug -fastappkit core dev --debug +Configure logging in `core/app.py`: + +```python +import logging + +logging.basicConfig( + level=logging.DEBUG if settings.debug else logging.INFO, + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", + handlers=[ + logging.FileHandler("app.log"), + logging.StreamHandler() + ] +) ``` -Shows debug information including stack traces. +### Log Levels + +- **DEBUG**: Detailed information for debugging +- **INFO**: General information +- **WARNING**: Warning messages +- **ERROR**: Error messages +- **CRITICAL**: Critical errors + +## Common Error Messages + +### `AppLoadError` + +**Format**: `AppLoadError: Failed to load app '' at stage ''` + +**Stages:** +- `resolve`: App entry couldn't be resolved +- `manifest`: Manifest loading failed +- `entrypoint`: Entrypoint loading failed +- `register`: Registration execution failed +- `router`: Router mounting failed + +**Debugging:** +1. Run `fastappkit app validate --debug` +2. Check specific stage mentioned in error +3. Verify app structure matches requirements + +### `ConfigError` + +**Format**: `ConfigError: ` + +**Common causes:** +- `fastappkit.toml` not found +- Invalid TOML syntax +- Missing `[tool.fastappkit]` section + +**Debugging:** +1. Verify you're in project root +2. Check `fastappkit.toml` exists +3. Validate TOML syntax + +### `ImportError` -## App Validation +**Format**: `ImportError: ` -Validate apps to check for issues: +**Common causes:** +- Missing `__init__.py` +- Package not installed +- Incorrect import path + +**Debugging:** +1. Check `__init__.py` files exist +2. Verify package is installed: `pip list` +3. Check import path is correct + +## Stack Trace Analysis + +### Reading Stack Traces + +1. **Top of trace**: Most recent call (where error occurred) +2. **Bottom of trace**: Original call site +3. **Look for**: Your code (not library code) for clues + +### Common Patterns + +**Settings not initialized:** +``` +AttributeError: 'NoneType' object has no attribute 'database_url' +``` +→ Settings not initialized in `core/app.py` + +**App not found:** +``` +AppLoadError: Could not resolve app entry +``` +→ App not pip-installed or path incorrect + +**Migration error:** +``` +alembic.util.exc.CommandError: Can't locate revision +``` +→ Migration file missing or database out of sync + +## Validation Debugging + +### Run Validation ```bash -fastappkit app validate +fastappkit app validate --debug ``` -For JSON output (useful for CI/CD): +### Check Each Stage + +1. **Manifest validation**: Required fields, format +2. **Isolation validation**: Import analysis +3. **Migration validation**: Directory structure, version table + +### JSON Output + +For CI/CD integration: ```bash fastappkit app validate --json ``` -## Migration Preview +Returns structured JSON with errors and warnings. + +## Migration Debugging -Preview SQL before applying migrations: +### Preview SQL + +Before applying migrations: ```bash -# Core + internal apps fastappkit migrate preview - -# External app -fastappkit migrate app preview ``` -## Database Inspection +Shows SQL that would be executed. -Check database state: +### Check Database State ```sql --- Core & internal apps version +-- Core + internal apps SELECT * FROM alembic_version; --- External app version +-- External apps SELECT * FROM alembic_version_; +``` + +### Check Migration Files --- Check tables -SELECT table_name FROM information_schema.tables -WHERE table_schema = 'public'; +```bash +# Core + internal +ls -la core/db/migrations/versions/ + +# External app +ls -la //migrations/versions/ ``` -## App Registry Inspection +### Common Migration Issues + +**Revision not found:** +- Check migration files exist +- Verify database state matches files +- Check `down_revision` references + +**Version table mismatch:** +- External apps: Check `migrations/env.py` uses `alembic_version_` +- Core/internal: Check uses `alembic_version` -Start dev server with debug to see app loading details: +## App Loading Debugging + +### Check App Resolution ```bash -fastappkit core dev --debug +fastappkit app list --verbose ``` -Check logs for: +Shows: +- Import path +- Filesystem path +- Migrations path +- Route prefix -- App resolution steps -- App registration steps -- Router mounting details -- Validation results +### Verify App Structure + +**Internal app:** +```bash +ls -la apps// +# Should have: __init__.py, models.py, router.py +``` -## Python Debugger +**External app:** +```bash +ls -la // +# Should have: __init__.py, models.py, router.py, fastappkit.toml +``` -Use Python debugger for code inspection: +### Check Imports ```python -import pdb; pdb.set_trace() +# Test import manually +python -c "import apps.blog" +python -c "import payments" ``` -Or use breakpoints in your IDE. +## Settings Debugging -## Logging - -Configure logging for detailed output: +### Verify Settings Loaded ```python -import logging +from fastappkit.conf import get_settings -logging.basicConfig( - level=logging.DEBUG, - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' -) +try: + settings = get_settings() + print(f"Database URL: {settings.database_url}") +except Exception as e: + print(f"Settings not loaded: {e}") +``` + +### Check Environment Variables + +```bash +# Check .env file +cat .env + +# Check environment variables +env | grep DATABASE_URL +``` + +### Verify Settings Initialization + +Check `core/app.py`: + +```python +# Must have this +settings = Settings() +kit = FastAppKit(settings=settings) +app = kit.create_app() ``` -## Common Debugging Scenarios +## Route Debugging -### App Not Loading +### Check Route Collisions -1. Check app exists: `fastappkit app list` -2. Validate app: `fastappkit app validate ` -3. Check logs: `fastappkit core dev --debug` -4. Verify manifest (external apps) -5. Check entrypoint import +```bash +fastappkit core dev --debug +# Look for route collision warnings +``` -### Migration Issues +### Verify Router Mounting -1. Preview SQL: `fastappkit migrate preview` -2. Check database state (SQL queries above) -3. Verify migration files exist -4. Check migration order in config +```python +# In core/app.py, after kit.create_app() +for route in app.routes: + print(f"{route.path} - {route.methods}") +``` -### Router Not Mounted +### Check Route Prefixes -1. Check app is loaded: `fastappkit app list` -2. Verify route prefix -3. Check for route collisions -4. Inspect app registry with debug output +```bash +fastappkit app list --verbose +# Shows route prefix for each app +``` ## Learn More -- [Common Issues](common-issues.md) - Common problems and solutions -- [CLI Reference](../reference/cli-reference.md) - Command options +- [Common Issues](common-issues.md) - Solutions to frequent problems +- [CLI Reference](../reference/cli-reference.md) - All commands diff --git a/docs/usage/full-ecosystem.md b/docs/usage/full-ecosystem.md new file mode 100644 index 0000000..2525cc8 --- /dev/null +++ b/docs/usage/full-ecosystem.md @@ -0,0 +1,356 @@ +# Scenario 3: Full Ecosystem + +This guide covers using both internal and external apps together - the full fastappkit ecosystem. + +## When to Use This Approach + +Use the full ecosystem when: + +- You're building reusable components +- You want to share apps across projects +- You need isolated migrations for plugins +- You're building a plugin ecosystem +- You want both project-specific (internal) and reusable (external) apps + +## Two Development Modes + +You can develop external apps in two ways: + +- **Mode A**: Develop external app separately, then integrate +- **Mode B**: Develop everything together (monorepo style) + +Choose based on your workflow preferences. + +--- + +## Mode A: Separate Development + +Develop external apps independently, then integrate with the core project. + +### Part 1: Create External App (Independent) + +#### Step 1: Create External App + +```bash +fastappkit app new payments --as-package +cd payments +``` + +This creates a complete external app structure: +- `payments/payments/` (package directory) +- `payments/payments/fastappkit.toml` (manifest) +- `payments/payments/migrations/` (isolated migrations) +- `payments/pyproject.toml` +- `payments/alembic.ini` (for independent development) + +#### Step 2: Install the Package + +**CRITICAL**: External apps MUST be pip-installed (no filesystem paths): + +```bash +pip install -e . +``` + +This makes the package importable, which is required for fastappkit to resolve it. + +#### Step 3: Update Dependency Versions + +**IMPORTANT**: Update dependency versions in `pyproject.toml` to match core project: + +```toml +[tool.poetry.dependencies] +python = ">=3.11,<4.0" +fastapi = ">=0.120.0,<0.130" # Match core project +sqlalchemy = ">=2.0,<3.0" +alembic = ">=1.17.2,<1.18" +``` + +#### Step 4: Configure Environment + +Edit `.env` file in external app directory (for independent development): + +```bash +DATABASE_URL=sqlite:///./payments.db +DEBUG=false +``` + +#### Step 5: Develop Independently + +**Add models** to `payments/payments/models.py`: + +```python +from sqlalchemy.orm import DeclarativeBase +from sqlalchemy import Column, Integer, String + +# CRITICAL: External apps must use own Base class +class Base(DeclarativeBase): + pass + +class Payment(Base): + __tablename__ = "payments" + + id = Column(Integer, primary_key=True) + amount = Column(Integer) + status = Column(String) +``` + +**IMPORTANT**: External apps must use their own `Base` class (DeclarativeBase), NOT `core.models.Base`. + +**Create migrations** using Alembic directly (NOT `fastappkit migrate`): + +```bash +alembic revision --autogenerate -m "initial" +alembic upgrade head +``` + +**Test independently:** + +```bash +uvicorn main:app --reload +``` + +This uses the external app's `.env` and `DATABASE_URL`. + +#### Step 6: Publish or Use Locally + +When ready: +- Publish to PyPI, or +- Use locally with `pip install -e /path/to/payments` + +### Part 2: Integrate with Core Project + +#### Step 1: Install in Core Project + +In your core project directory: + +```bash +pip install -e /path/to/payments +# or from PyPI: +pip install payments-app +``` + +**CRITICAL**: Must be pip-installed, cannot use filesystem path directly in `fastappkit.toml`. + +#### Step 2: Add to Configuration + +Edit `fastappkit.toml` in core project: + +```toml +[tool.fastappkit] +apps = [ + "apps.blog", # Internal app + "payments", # External app (package name, not path) +] +``` + +**IMPORTANT**: Use package name, not filesystem path. + +#### Step 3: Apply Migrations + +**IMPORTANT**: External app will use core project's `DATABASE_URL` when integrated: + +```bash +fastappkit migrate app payments upgrade +``` + +This applies the external app's migrations to the core project's database. + +#### Step 4: Start Core Server + +```bash +fastappkit core dev +``` + +The external app is now integrated and routes are mounted automatically. + +--- + +## Mode B: Monorepo Development + +Develop core project and external apps together in the same repository. + +#### Step 1: Create Core Project + +```bash +fastappkit core new myproject +cd myproject +``` + +#### Step 2: Update Dependency Versions + +**IMPORTANT**: Update dependency versions in core project's `pyproject.toml`: + +```toml +[tool.poetry.dependencies] +python = ">=3.11,<4.0" +fastapi = ">=0.120.0,<0.130" +sqlalchemy = ">=2.0,<3.0" +alembic = ">=1.17.2,<1.18" +``` + +#### Step 3: Create External App in Same Repo + +```bash +fastappkit app new payments --as-package +``` + +This creates `payments/` directory in the same repo. + +#### Step 4: Install External App + +**CRITICAL**: Install external app (must be pip-installed even in monorepo): + +```bash +pip install -e ./payments +``` + +#### Step 5: Match Dependency Versions + +Ensure external app's dependency versions match core project (edit `payments/pyproject.toml`). + +#### Step 6: Add to Configuration + +Edit `fastappkit.toml`: + +```toml +[tool.fastappkit] +apps = [ + "apps.blog", + "payments", # Package name +] +``` + +#### Step 7: Develop Both Together + +You can now: +- Edit core project code +- Edit external app code +- Test both together + +#### Step 8: Test External App Independently + +```bash +cd payments +uvicorn main:app --reload +``` + +Uses external app's `.env` and `DATABASE_URL`. + +#### Step 9: Test Integrated + +```bash +cd .. +fastappkit core dev +``` + +Uses core project's `DATABASE_URL`. + +--- + +## Critical Gotchas + +### External Apps MUST be Pip-Installed + +- External apps **MUST** be pip-installed (even for local dev: `pip install -e .`) +- Filesystem paths are NOT supported in `fastappkit.toml` +- This is enforced by the resolver (uses `importlib.import_module()`) + +### Database URL Differences + +- **Independent development**: External app uses its own `.env` and `DATABASE_URL` +- **Integrated**: External app uses core project's `DATABASE_URL` (from core's `.env`) + +### Migration Creation + +- External apps **cannot** create migrations from core project +- Must use `alembic` directly: `alembic revision --autogenerate -m "message"` +- From core project, you can only apply existing migrations: `fastappkit migrate app upgrade` + +### Manifest Location + +- External apps must have `fastappkit.toml` manifest in package directory (`//fastappkit.toml`) +- NOT in project root, NOT in `pyproject.toml` +- Must be included when package is published to PyPI + +### Version Table + +- External apps must have correct version table in `migrations/env.py` +- Should be `alembic_version_`, NOT `alembic_version` +- Check the generated `migrations/env.py` file + +### Base Class + +- External apps must use own `Base` class (DeclarativeBase) +- Cannot import `Base` from `core.models` +- Isolation validation enforces this + +## Quick Commands Reference + +```bash +# Create external app +fastappkit app new payments --as-package +cd payments +pip install -e . + +# Develop independently +alembic revision --autogenerate -m "initial" +uvicorn main:app --reload + +# Integrate with core project +cd /path/to/core/project +pip install -e /path/to/payments +# Add "payments" to fastappkit.toml +fastappkit migrate app payments upgrade +fastappkit core dev +``` + +## Development Workflow + +### Independent Development (External App) + +1. Make changes to external app code +2. Create migration: `alembic revision --autogenerate -m "message"` +3. Test: `uvicorn main:app --reload` +4. When ready, publish or use locally + +### Integrated Development (Core Project) + +1. Install external app: `pip install -e /path/to/app` +2. Add to `fastappkit.toml`: `"app_name"` +3. Apply migrations: `fastappkit migrate app app_name upgrade` +4. Test: `fastappkit core dev` + +## Testing Approaches + +### Test External App Independently + +```bash +cd +uvicorn main:app --reload +# Uses external app's .env and DATABASE_URL +``` + +### Test Integrated + +```bash +cd +fastappkit core dev +# Uses core project's DATABASE_URL +``` + +### Manual Testing + +```bash +# Test external app endpoints +curl http://127.0.0.1:8000/payments/ + +# Test integrated endpoints +curl http://127.0.0.1:8000/blog/posts +curl http://127.0.0.1:8000/payments/transactions +``` + +## Next Steps + +- [Creating Apps](../guides/creating-apps.md) - Detailed app creation guide +- [Configuration](../configuration/index.md) - All configuration options +- [External App Manifest](../configuration/external-app-manifest.md) - Manifest format reference diff --git a/docs/usage/index.md b/docs/usage/index.md new file mode 100644 index 0000000..ab6cb91 --- /dev/null +++ b/docs/usage/index.md @@ -0,0 +1,104 @@ +# Usage Scenarios + +fastappkit supports different development approaches depending on your needs. This guide helps you choose the right path. + +## Three Main Scenarios + +### 1. Scaffolding Only + +Just generate project structure without using fastappkit's app system. + +**Use when:** +- You want a clean FastAPI project structure +- You prefer manual organization +- You don't need the app system features + +**What you get:** +- Project structure (`core/`, `apps/`, etc.) +- Settings system +- Migration setup +- Manual control over everything + +[→ Go to Scaffolding Only Guide](scaffolding-only.md) + +### 2. Scaffolding + Internal Apps + +Build a project with internal apps (like Django apps). + +**Use when:** +- You're building a single project +- You want modular organization within the project +- You need shared migrations across features +- You want to organize code into logical components + +**What you get:** +- Everything from Scaffolding Only +- Internal apps system +- Shared migration management +- Automatic router mounting + +[→ Go to Internal Apps Guide](internal-apps.md) + +### 3. Scaffolding + Internal + External Apps + +Full ecosystem with both internal and external (pluggable) apps. + +**Use when:** +- You're building reusable components +- You want to share apps across projects +- You need isolated migrations for plugins +- You're building a plugin ecosystem + +**What you get:** +- Everything from Internal Apps +- External apps (pip-installable packages) +- Isolated migrations per external app +- Plugin architecture + +[→ Go to Full Ecosystem Guide](full-ecosystem.md) + +## Quick Comparison + +| Feature | Scaffolding Only | Internal Apps | Full Ecosystem | +|---------|------------------|---------------|----------------| +| **Project Structure** | ✅ | ✅ | ✅ | +| **Internal Apps** | ❌ | ✅ | ✅ | +| **External Apps** | ❌ | ❌ | ✅ | +| **Shared Migrations** | ✅ (core only) | ✅ (core + internal) | ✅ (core + internal) | +| **Isolated Migrations** | ❌ | ❌ | ✅ (external apps) | +| **CLI Commands** | Limited | Full | Full | +| **App Validation** | ❌ | ✅ | ✅ | +| **Plugin Architecture** | ❌ | ❌ | ✅ | + +## Decision Tree + +``` +Do you need reusable components? +│ +├─ No → Do you want modular organization? +│ │ +│ ├─ No → Use Scaffolding Only +│ │ +│ └─ Yes → Use Internal Apps +│ +└─ Yes → Use Full Ecosystem +``` + +## Common Questions + +**Q: Can I start with Scaffolding Only and add apps later?** +A: Yes! You can always add internal apps later. External apps require pip installation, so plan accordingly. + +**Q: Can I mix internal and external apps?** +A: Yes, that's the Full Ecosystem scenario. Internal apps for project-specific features, external apps for reusable components. + +**Q: Do I need to use all features?** +A: No. Use only what you need. Start simple and add complexity as required. + +## Next Steps + +Choose your scenario and follow the detailed guide: + +- [Scaffolding Only](scaffolding-only.md) - Just project structure +- [Internal Apps](internal-apps.md) - Build with internal apps +- [Full Ecosystem](full-ecosystem.md) - Internal + external apps diff --git a/docs/usage/internal-apps.md b/docs/usage/internal-apps.md new file mode 100644 index 0000000..53ac015 --- /dev/null +++ b/docs/usage/internal-apps.md @@ -0,0 +1,228 @@ +# Scenario 2: Scaffolding + Internal Apps + +This guide covers building a project with internal apps (like Django apps). + +## When to Use This Approach + +Use internal apps when: + +- You're building a single project +- You want modular organization within the project +- You need shared migrations across features +- You want to organize code into logical components +- You don't need reusable components across projects + +## Step-by-Step Guide + +### 1. Create Project + +```bash +fastappkit core new myproject +cd myproject +``` + +### 2. Update Dependency Versions + +**IMPORTANT**: Update dependency versions in `pyproject.toml` from `*` to specific ranges: + +```toml +[tool.poetry.dependencies] +python = ">=3.11,<4.0" +fastapi = ">=0.120.0,<0.130" +sqlalchemy = ">=2.0,<3.0" +alembic = ">=1.17.2,<1.18" +``` + +### 3. Configure Environment + +Edit `.env` file: + +```bash +DATABASE_URL=sqlite:///./myproject.db +DEBUG=false +``` + +### 4. Create Internal App + +```bash +fastappkit app new blog +``` + +This creates: +- `apps/blog/__init__.py` (with `register()` function) +- `apps/blog/models.py` (imports `Base` from `core.models`) +- `apps/blog/router.py` (FastAPI router) +- Automatically adds `"apps.blog"` to `fastappkit.toml` + +### 5. Add Models + +Edit `apps/blog/models.py`: + +```python +from core.models import Base +from sqlalchemy import Column, Integer, String + +class Post(Base): + __tablename__ = "posts" + + id = Column(Integer, primary_key=True) + title = Column(String) + content = Column(String) +``` + +**IMPORTANT**: Internal apps must import `Base` from `core.models`, not create their own. + +### 6. Create Migration + +```bash +fastappkit migrate app blog makemigrations -m "initial" +``` + +This creates a migration in `core/db/migrations/versions/` (shared with core). + +### 7. Apply Migrations + +```bash +fastappkit migrate all +``` + +This applies all migrations (core + internal apps) in the correct order. + +### 8. Start Development Server + +```bash +fastappkit core dev +``` + +Your API is now running at `http://127.0.0.1:8000` with routes mounted at `/blog/`. + +## Development Workflow + +### Adding More Internal Apps + +```bash +fastappkit app new auth +fastappkit app new payments +``` + +Each app is automatically added to `fastappkit.toml`. + +### Creating Migrations + +```bash +# For a specific app +fastappkit migrate app blog makemigrations -m "Add post model" + +# For core (if you have core models) +fastappkit migrate core -m "Add session table" +``` + +### Applying Migrations + +```bash +# Apply all migrations (recommended) +fastappkit migrate all + +# Or apply core + internal apps only +fastappkit migrate upgrade +``` + +### Testing Internal Apps + +1. **Manual testing:** + ```bash + fastappkit core dev + # Visit http://127.0.0.1:8000/docs + ``` + +2. **Test specific endpoints:** + ```bash + curl http://127.0.0.1:8000/blog/posts + ``` + +## Migration Workflow for Internal Apps + +Internal apps share the migration directory with core (`core/db/migrations/`): + +1. **Create migration:** + ```bash + fastappkit migrate app makemigrations -m "message" + ``` + +2. **Review migration:** + ```bash + # Preview SQL + fastappkit migrate preview + ``` + +3. **Apply migration:** + ```bash + fastappkit migrate all + ``` + +4. **If needed, downgrade:** + ```bash + fastappkit migrate downgrade + ``` + +## Important Notes + +- **All commands must be run from project root** (where `fastappkit.toml` is located) +- Internal apps share migration directory with core (`core/db/migrations/`) +- Internal apps can import from `core.models` and other internal apps +- Internal apps are automatically added to `fastappkit.toml` by CLI +- Ensure `apps//__init__.py` exists (created by CLI, but verify if manual) + +## Quick Commands Reference + +```bash +# Create project +fastappkit core new myproject && cd myproject + +# Create internal app +fastappkit app new blog + +# Create migration +fastappkit migrate app blog makemigrations -m "Add post model" + +# Apply all migrations +fastappkit migrate all + +# Start development server +fastappkit core dev +``` + +## Common Patterns + +### App Dependencies + +Internal apps can depend on other internal apps: + +```python +# apps/blog/models.py +from apps.auth.models import User +from core.models import Base +from sqlalchemy import Column, Integer, ForeignKey + +class Post(Base): + __tablename__ = "posts" + author_id = Column(Integer, ForeignKey("users.id")) +``` + +### Cross-App Relationships + +Since all internal apps share the same Base and migration directory, you can create relationships between apps: + +```python +# apps/blog/models.py +from apps.auth.models import User + +class Post(Base): + author = relationship("User", back_populates="posts") +``` + +## Next Steps + +- [Full Ecosystem](full-ecosystem.md) - Add external apps for reusable components +- [Creating Apps](../guides/creating-apps.md) - Detailed app creation guide +- [Migrations](../guides/migrations.md) - Complete migration workflow diff --git a/docs/usage/scaffolding-only.md b/docs/usage/scaffolding-only.md new file mode 100644 index 0000000..3a1c3e4 --- /dev/null +++ b/docs/usage/scaffolding-only.md @@ -0,0 +1,193 @@ +# Scenario 1: Scaffolding Only + +This guide is for users who just want the project structure without using fastappkit's app system. + +## When to Use This Approach + +Use scaffolding only when: + +- You want a clean FastAPI project structure +- You prefer manual organization over the app system +- You don't need modular apps or plugin architecture +- You want full control over project organization + +## Step-by-Step Guide + +### 1. Create Project + +```bash +fastappkit core new myproject +cd myproject +``` + +This creates a complete project structure with: +- `core/` directory (config, app, models) +- `apps/` directory (empty, for manual use) +- `fastappkit.toml` (project configuration) +- `.env` file (environment variables) +- `main.py` (entry point) + +### 2. Install Dependencies + +```bash +poetry install +# or +pip install -e . +``` + +### 3. Update Dependency Versions + +**IMPORTANT**: Dependency versions in `pyproject.toml` default to `*` (any version). Update them for production: + +```toml +[tool.poetry.dependencies] +python = ">=3.11,<4.0" +fastapi = ">=0.120.0,<0.130" # Specific range instead of * +sqlalchemy = ">=2.0,<3.0" +alembic = ">=1.17.2,<1.18" +``` + +### 4. Configure Environment + +Edit `.env` file: + +```bash +DATABASE_URL=sqlite:///./myproject.db +DEBUG=false +``` + +Add any custom settings you need. + +### 5. Customize Settings (Optional) + +Edit `core/config.py` to add custom settings: + +```python +from pydantic import Field +from pydantic_settings import BaseSettings, SettingsConfigDict + +class Settings(BaseSettings): + database_url: str = Field(default="sqlite:///./myproject.db") + debug: bool = Field(default=False) + + # Add your custom settings + secret_key: str = Field(default="change-me", alias="SECRET_KEY") + api_key: str = Field(default="", alias="API_KEY") + + model_config = SettingsConfigDict( + env_file=".env", + env_file_encoding="utf-8", + populate_by_name=True + ) +``` + +### 6. Customize FastAPI App (Optional) + +Edit `core/app.py` to add middleware, exception handlers, etc.: + +```python +from core.config import Settings +from fastappkit.core.kit import FastAppKit +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware + +settings = Settings() +kit = FastAppKit(settings=settings) +app = kit.create_app() + +# Customize FastAPI app +app.title = "My Custom API" +app.version = "1.0.0" +app.description = "Custom API description" + +# Add middleware +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) +``` + +### 7. Run Manually + +Since you're not using the app system, run the server manually: + +```bash +uvicorn main:app --reload +``` + +Or use Python directly: + +```bash +python main.py +``` + +## What You Can Customize + +- **Settings**: Add any custom settings in `core/config.py` +- **FastAPI App**: Modify `core/app.py` to add middleware, exception handlers, etc. +- **Models**: Add models to `core/models.py` (for shared infrastructure) +- **Routes**: Add routes directly to `main.py` or create your own router modules +- **Migrations**: Use `fastappkit migrate core -m "message"` for core migrations + +## What You Can Skip + +- Creating apps (you can organize code manually) +- Using `fastappkit app` commands +- App validation +- Automatic router mounting (mount manually if needed) + +## Manual Testing Approach + +Since you're running manually, you can: + +1. **Test with uvicorn directly:** + ```bash + uvicorn main:app --reload --host 0.0.0.0 --port 8000 + ``` + +2. **Use FastAPI's interactive docs:** + - Visit `http://127.0.0.1:8000/docs` for Swagger UI + - Visit `http://127.0.0.1:8000/redoc` for ReDoc + +3. **Test with curl or httpx:** + ```bash + curl http://127.0.0.1:8000/ + ``` + +## Quick Commands Reference + +```bash +# Create project +fastappkit core new myproject + +# Install dependencies +cd myproject && poetry install + +# Update dependency versions in pyproject.toml (IMPORTANT!) + +# Configure .env file + +# Run manually +uvicorn main:app --reload +``` + +## Critical Manual Steps + +- ✅ Update dependency versions from `*` to specific ranges +- ✅ Configure `.env` file with `DATABASE_URL` and other settings +- ✅ Ensure `core/app.py` initializes Settings and FastAppKit correctly + +## Next Steps + +If you later decide you want modular organization: + +- [Internal Apps](internal-apps.md) - Add internal apps to your project +- [Full Ecosystem](full-ecosystem.md) - Add external apps for reusable components + +Or continue with manual organization and use fastappkit only for: +- Project structure +- Settings management +- Core migrations diff --git a/mkdocs.yml b/mkdocs.yml index 8bc28bc..7a20c55 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -15,6 +15,14 @@ copyright: Copyright © 2025 VanyLabs theme: name: readthedocs +# Exclude temp directory from processing +exclude_docs: | + temp/** + +# Extra CSS +extra_css: + - stylesheets/extra.css + # Plugins plugins: - search: @@ -50,11 +58,21 @@ nav: - Installation: getting-started/installation.md - Quick Start: getting-started/quickstart.md - Core Concepts: getting-started/core-concepts.md + - Usage Scenarios: + - Overview: usage/index.md + - Scaffolding Only: usage/scaffolding-only.md + - Internal Apps: usage/internal-apps.md + - Full Ecosystem: usage/full-ecosystem.md + - Configuration: + - Overview: configuration/index.md + - Project Configuration: configuration/project-config.md + - Settings: configuration/settings.md + - External App Manifest: configuration/external-app-manifest.md - Guides: - Creating Projects: guides/creating-projects.md - Creating Apps: guides/creating-apps.md - Migrations: guides/migrations.md - - Configuration: guides/configuration.md + - Development Workflow: guides/development-workflow.md - Deployment: guides/deployment.md - Topics: - Internal Apps: topics/internal-apps.md @@ -65,7 +83,6 @@ nav: - Reference: - CLI Reference: reference/cli-reference.md - Configuration Reference: reference/configuration-reference.md - - Manifest Reference: reference/manifest-reference.md - API Reference: reference/api-reference.md - Advanced: - Architecture: advanced/architecture.md @@ -75,16 +92,3 @@ nav: - Common Issues: troubleshooting/common-issues.md - Debugging: troubleshooting/debugging.md - Community: community/index.md - - Development: - - CI/CD: CI_CD.md - - Changelog: - - Overview: community/index.md#changelog - - Version 0.2.1: changelog/0.2.1.md - - Version 0.2.0: changelog/0.2.0.md - - Version 0.1.9: changelog/0.1.9.md - - Version 0.1.0: changelog/0.1.0.md - -# Extra -extra: - version: - provider: readthedocs