Files
prompts/skills/nicegui/SKILL.md
T
John Lancaster c8906cef7b reorg
2026-06-16 23:39:45 -05:00

18 KiB
Raw Blame History

name, description
name description
nicegui 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):

.
├─ 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:

.
├─ 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
  • 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.

  • 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

.
└─ 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 LangGraphs 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).

  • 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

.
├─ 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.