--- name: nicegui description: Build and scaffold production-ready NiceGUI + FastAPI app architecture. --- # NiceGUI Build a production-ready, multi-page NiceGUI application that follows modern FastAPI project layout and architecture practices. ## Goal Create a clean, maintainable app structure where: - FastAPI is the underlying ASGI app. - NiceGUI is mounted/initialized in a way that aligns with FastAPI best practices. - Each page is implemented as a separate module in `pages/`. - The project is easy to test, extend, and deploy. ## Requirements - Prefer Pydantic settings with a `.env` example. - Consider adding structured logging configuration. - Use a `src/`-style layout with clear package boundaries. - Use an app factory (`create_app`) and avoid global side effects at import time. - Register NiceGUI pages from modules in `pages/`. - Keep UI logic in page modules and shared UI helpers/components in a separate package. - Include at least these pages: - Home (`/`) - Dashboard (`/dashboard`) - About (`/about`) - Include a health endpoint (`/healthz`) on the FastAPI side. - Provide minimal test examples for FastAPI routes and page registration. - Use FastAPI lifespan for startup/shutdown resource management. ## Recommendations - Consider a shared navigation/header component and left drawer used by all pages. - Consider including dev tooling basics (formatting/lint/test commands). - Prefer clear separation of concerns: - `db/` for engine/session/base/models/repositories (if needed) - `api/` for HTTP endpoints - `ui/` for NiceGUI pages/components - `services/` for business logic - If you need database access, a solid pattern is to use FastAPI + SQLAlchemy best practices: - Use one engine per application process (do not create per request). - Prefer a request-scoped session dependency using `yield`. - Keep transaction boundaries explicit (commit/rollback) in service/repository flows. - Avoid sharing a session across concurrent tasks. - Consider production-minded pool settings (`pool_pre_ping=True` and sensible recycle/timeout values). - Prefer Alembic for schema migrations. ## Suggested Project Structure Base structure (no database required): ```text . ├─ pyproject.toml ├─ .env.example ├─ README.md ├─ src/ │ └─ app/ │ ├─ __init__.py │ ├─ main.py │ ├─ config.py │ ├─ logging.py │ ├─ api/ │ │ ├─ __init__.py │ │ └─ health.py │ ├─ services/ │ │ ├─ __init__.py │ │ └─ example_service.py │ ├─ ui/ │ │ ├─ __init__.py │ │ ├─ components/ │ │ │ ├─ __init__.py │ │ │ └─ nav.py │ │ └─ pages/ │ │ ├─ __init__.py │ │ ├─ home.py │ │ ├─ dashboard.py │ │ └─ about.py │ └─ bootstrap.py └─ tests/ ├─ test_health.py └─ test_pages_registration.py ``` ### Conceptual boundaries for the base structure - `main.py`: application entrypoint only; wires `create_app()` and process startup concerns. - `bootstrap.py`: app composition layer; owns app factory, router registration, page registration, and lifespan wiring. - `config.py`: configuration boundary; centralizes settings parsing and typed config objects. - `logging.py`: observability boundary; centralizes logging format/levels/handlers so modules do not configure logging ad hoc. - `api/`: transport boundary for HTTP endpoints; validate/shape request-response objects and delegate business work to `services/`. - `services/`: use-case/business boundary; orchestrates domain behavior and dependencies, independent of UI rendering. - `ui/pages/`: route-level UI boundary; one module per page, focused on layout/event handling for that page. - `ui/components/`: reusable presentation boundary; shared UI widgets/composition utilities with no business side effects. - `tests/`: behavior boundary; verify public behavior (health endpoint, page registration, service outcomes), not private implementation details. ### Dependency direction to keep clear - Prefer this dependency flow: - `main/bootstrap` -> `config/logging` + `api` + `ui/pages` + `services` - `api` -> `services` - `ui/pages` -> `ui/components` + `services` - `services` -> pure helpers/clients (and later `db/` if enabled) - Avoid reverse imports (for example, `services` importing from `ui/pages` or `api`). - Keep page modules and API handlers as thin adapters; keep business decisions in `services/`. ## Extending This Pattern with a Database (Optional) If database access is needed, extend with: ```text . ├─ alembic.ini ├─ alembic/ │ ├─ env.py │ └─ versions/ ├─ src/ │ └─ app/ │ └─ db/ │ ├─ __init__.py │ ├─ base.py │ ├─ session.py │ ├─ models/ │ │ ├─ __init__.py │ │ └─ example.py │ └─ repositories/ │ ├─ __init__.py │ └─ example_repo.py └─ tests/ └─ test_db_session_dependency.py ``` ### Recommended database layering - Keep `db/` focused on persistence primitives: - `session.py`: engine + session factory + dependency helpers - `models/`: ORM table mappings only - `repositories/`: query/persistence operations only - Keep transaction orchestration in `services/`, not in UI pages. - Keep `ui/pages/` free of SQLAlchemy details; pages call services. - Keep API handlers thin and delegate data work to services/repositories. ### Session and transaction pattern to request - Use one engine and one sessionmaker per process, initialized at app startup. - Use a `get_db_session()` dependency with `yield` for request-scoped sessions. - In write flows, always use context managers for commit/rollback safety. - In read-only flows, avoid unnecessary transactions and keep queries narrowly scoped. - Do not share a session across concurrent tasks; open a new session per task/unit of work. ### Migration and schema workflow - Treat Alembic migrations as the source of truth for schema evolution. - Prefer: - `alembic revision --autogenerate -m "..."` - review migration script - `alembic upgrade head` - Avoid relying on `metadata.create_all()` for production schema management. - Include a lightweight startup check that logs current migration state (without mutating schema). ### Configuration and runtime concerns - Read `DATABASE_URL` from settings (`pydantic-settings`) and keep secrets out of source control. - Configure pool options appropriate for environment (for example: `pool_pre_ping`, timeout/recycle tuning). - Keep SQL echo/debug logging disabled in production by default. - Consider a readiness probe (`/readyz`) that verifies DB connectivity when your deployment needs it. ### Testing guidance for DB-enabled projects - Unit test repositories against a disposable test database. - Integration test `get_db_session()` lifecycle and rollback behavior on failures. - Add migration tests that validate model changes are represented in Alembic revisions. - Add API/service tests for critical read/write paths (including uniqueness and constraint errors). - Keep DB tests isolated and deterministic (fresh schema + transactional cleanup per test module/session). ## Extending This Pattern with LangGraph (Optional) If your app needs multi-step AI workflows, tool-calling loops, or human-in-the-loop approvals, a clean extension is to add a dedicated LangGraph domain layer. ### Recommended architecture shape - Keep LangGraph logic out of `ui/pages/` and out of HTTP route handlers. - Add an `ai/` package (or similar) and keep graph definition, state schema, tools, and orchestration there. - Call the graph from `services/` so your UI and API both use the same orchestration entry points. - Continue using FastAPI lifespan for shared resources (models, clients, stores/checkpointers). ### Suggested project extension ```text . └─ src/ └─ app/ ├─ ai/ │ ├─ __init__.py │ ├─ state.py # TypedDict / Pydantic state schemas + reducers │ ├─ nodes/ │ │ ├─ __init__.py │ │ ├─ llm.py │ │ ├─ tools.py │ │ └─ review.py # optional interrupt/human-review nodes │ ├─ graphs/ │ │ ├─ __init__.py │ │ └─ assistant_graph.py │ ├─ runtime.py # compile() + checkpointer/store wiring │ └─ contracts.py # input/output DTOs between services and graph ├─ services/ │ └─ ai_service.py # invoke/stream/resume wrappers └─ api/ └─ ai.py # optional HTTP endpoints for run/stream/resume ``` ### Idiomatic LangGraph practices to request - Define explicit graph state schema (`TypedDict` or Pydantic) and use reducers for append/merge behavior where needed. - Compile graphs with persistence primitives appropriate to environment: - dev/testing: in-memory checkpointer/store - production: durable checkpointer/store - Always pass a stable `thread_id` in `configurable` for resumable sessions and conversation continuity. - For interactive UX, prefer event streaming APIs and project-specific streaming adapters in service code. - Use `interrupt()` for human approvals/reviews; resume with `Command(resume=...)` using the same `thread_id`. - Keep interrupt payloads JSON-serializable. - Place non-idempotent side effects after interrupts (or isolate them in separate nodes), because interrupted nodes re-run from the start. - If using tools, prefer LangGraph’s `ToolNode` (or an equivalent centralized tool-execution node) for consistent execution/error handling. - Keep graph nodes deterministic and narrow in scope (single responsibility per node). - Add observability with LangSmith tracing for graph runs, transitions, and latency debugging. ### Integration guidance for NiceGUI + FastAPI - UI pages should call `ai_service` methods, not graph internals. - If the UX needs live token/progress updates, expose streaming from service -> UI in a transport-appropriate way. - For approval workflows, surface interrupt payloads in UI, collect user response, then resume via service with `Command(resume=...)`. - Keep graph invocation boundaries typed (request/response contracts) so changes in graph internals do not leak into page modules. ### Testing recommendations for LangGraph additions - Unit test node functions as pure transformations where possible. - Integration test graph routes (happy path, tool path, interrupt/resume path). - Add tests ensuring `thread_id` reuse resumes correctly and new IDs start fresh sessions. - Add regression tests for state-schema evolution and serialization compatibility. ## Extending This Pattern with a Static Docs Site via Zensical (Optional) If the app should also host a static documentation site, mount the generated docs output directory under a configurable route (default: `/docs`). ### Recommended docs workflow - Initialize docs in the project root: - `uv run zensical new .` - Keep source markdown in Zensical's `docs_dir` (default: `docs/`). - Build docs as part of release or startup pipeline: - `uv run zensical build` - Serve the generated static output from Zensical's `site_dir` (default: `site/`). ### Config to include in app settings - `docs_enabled: bool = true` - `docs_mount_path: str = "/docs"` - `docs_site_dir: str = "site"` - Optional for local/dev convenience: - `docs_require_build: bool = false` (if true, fail startup when `site/` is missing) ### FastAPI/NiceGUI integration pattern - Keep docs mounting in the app composition layer (`bootstrap.py`), not in page modules. - Use FastAPI static serving to mount the built directory (for example via `StaticFiles(..., html=True)`). - Ensure `docs_mount_path` is normalized (leading slash, no trailing slash) and does not conflict with app/API routes. - If docs are disabled or build artifacts are missing, log a clear warning and continue (unless `docs_require_build=true`). ### Suggested project additions ```text . ├─ zensical.toml ├─ docs/ │ ├─ index.md │ └─ ... ├─ site/ # generated by `uv run zensical build` └─ src/ └─ app/ ├─ config.py # docs_enabled/docs_mount_path/docs_site_dir └─ bootstrap.py # mount static docs route ``` ### Deployment notes for docs mounting - In CI/CD, run `uv run zensical build` before packaging/deploying the app image. - If `project.site_dir` in `zensical.toml` is changed, keep `docs_site_dir` in app settings aligned. - If hosting behind a reverse proxy with a path prefix, verify docs links and static assets with the final external base path. - Add a test asserting requests to `docs_mount_path` return the built index page when docs are enabled. ## Implementation Notes - Prefer an explicit page registration function pattern, for example: - `register_pages()` in `ui/pages/__init__.py` - Each page module exports `register_page()` - Keep imports one-way to avoid circular dependencies. - Keep page modules cohesive: route declaration + page layout for that route. - Prefer FastAPI lifespan hooks where appropriate for startup/shutdown behavior. - Prefer type hints throughout. ### Styling architecture notes - Prefer class-first UI composition for structure and layout using normal NiceGUI mechanics (`.classes(...)` on containers/components). - Keep structural concerns in Python page/component modules (layout grids, spacing systems, responsive breakpoints, alignment). - Keep cosmetic concerns in static CSS files (colors, gradients, shadows, border polish, typography fine-tuning, transitions). - Avoid large inline `style(...)` strings except for one-off dynamic values that must be computed at runtime. - Centralize CSS entrypoints in a small, predictable set (for example `ui/static/css/base.css`, `ui/static/css/theme.css`, `ui/static/css/components.css`). - Include CSS once at app bootstrap/startup so pages share the same style contract; avoid per-page ad hoc includes. - Prefer semantic class names for reusable patterns (`app-shell`, `page-section`, `card-surface`) and utility classes only for local layout tweaks. - If using design tokens, define them as CSS custom properties in `:root` and reference them from component classes. - Keep page modules focused on structure and behavior; reusable visual patterns should live in shared UI components plus shared CSS. ### Database-specific notes (only if your app needs a DB) - Prefer settings via `pydantic-settings` and read `DATABASE_URL` from environment/dotenv. - Configure the SQLAlchemy engine once at app setup level; avoid creating engine/sessionmaker in endpoint functions. - Provide `get_db_session()` via `yield` so sessions are consistently closed. - Prefer a repository/service boundary to keep SQL details out of page modules. - Prefer Alembic for schema changes and include commands for `revision --autogenerate` and `upgrade head`. - Avoid `metadata.create_all()` in production startup flow; reserve it for optional local/dev bootstrap paths. - Consider a light DB readiness check in health reporting (or a dedicated `/readyz`) when relevant. - Ensure logs do not leak secrets (for example, avoid unsafe SQL echo in production). ## Output Format The goal of the output is to produce an output Return: - A concise, high-level architecture explanation. - An explanation of how the different parts of the concept will fit into this structure - What are the core services? - What are the main ui pages? - What are the main ui components? - Whether a database will be involved and whether the app owns the database - Whether any AI will be used and what the workflow looks like - A concrete implementation plan in the form of a checklist, organized into sections by concept which should roughly mirror the structure of sub-packages. - Key functions/classes - Configuration/settings available - Migration or rollout notes (if relevant) ## Guardrails - Do not put all pages in a single file. - Never use globals, implicit or otherwise. - Keep code minimal but production-minded. - Being clear and concise is a higher priority than being verbose. - Prefer clarity and maintainability over clever abstractions. ## Source Documentation (recommended references) - FastAPI lifespan events: - https://fastapi.tiangolo.com/advanced/events/ - FastAPI settings and environment variables: - https://fastapi.tiangolo.com/advanced/settings/ - FastAPI dependencies with `yield` (session lifecycle pattern): - https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-with-yield/ - FastAPI SQL databases tutorial (when using a DB): - https://fastapi.tiangolo.com/tutorial/sql-databases/ - SQLAlchemy engine configuration and pooling (when using a DB): - https://docs.sqlalchemy.org/en/20/core/engines.html - SQLAlchemy session basics and lifecycle guidance (when using a DB): - https://docs.sqlalchemy.org/en/20/orm/session_basics.html - Alembic tutorial and migration environment (when using a DB): - https://alembic.sqlalchemy.org/en/latest/tutorial.html - Pydantic settings management: - https://pydantic.dev/docs/validation/latest/concepts/pydantic_settings/ - NiceGUI pages/routing and FastAPI integration: - https://www.nicegui.io/documentation/section_pages_routing - NiceGUI security best practices: - https://www.nicegui.io/documentation/section_security - LangGraph overview: - https://docs.langchain.com/oss/python/langgraph/overview - LangGraph quickstart: - https://docs.langchain.com/oss/python/langgraph/quickstart - LangGraph workflows and agents: - https://docs.langchain.com/oss/python/langgraph/workflows-agents - LangGraph persistence (checkpointer vs store): - https://docs.langchain.com/oss/python/langgraph/persistence - LangGraph memory concepts: - https://docs.langchain.com/oss/python/concepts/memory - LangGraph streaming: - https://docs.langchain.com/oss/python/langgraph/streaming - LangGraph interrupts / human-in-the-loop: - https://docs.langchain.com/oss/python/langgraph/interrupts