388 lines
18 KiB
Markdown
388 lines
18 KiB
Markdown
---
|
||
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
|