Skip to content

Quickstart

The default is a page with a form, not an API-only file. FastAPI leaves HTML, forms, and a panel as homework; pyron init does not.

pyron init app

It asks the project name, what it is about, and what people add (dishes, kits, classes…). You get a unique app: home form, /docs, /admin, and 10 starter records for that topic — not a copy of the bookstore example.

pyron init shop
cd shop
pyron dev
  • http://127.0.0.1:8000 — HTML form (creates a row)
  • http://127.0.0.1:8000/docs — the same row as JSON
  • http://127.0.0.1:8000/admin — the same row in the panel (first visit creates root)
  • http://127.0.0.1:8000/p/about — a CMS page; add more under Pages in /admin. Jinja2 renders them.
  • http://127.0.0.1:8000/admin/settings — site name, phone, hours
from pyron import Form, redirect

@app.post("/")
async def add_item(title: str = Form(), description: str = Form("")):
    ...
    return redirect("/", message=f"Added “{title}”.")

API only

from pydantic import BaseModel, Field
from pyron import Pyron

app = Pyron(title="Harbor API")


class BookIn(BaseModel):
    title: str = Field(min_length=1)
    price: float = Field(ge=0)


@app.get("/health")
async def health() -> dict[str, str]:
    return {"status": "ok"}


@app.post("/books", status_code=201)
async def create_book(book: BookIn) -> BookIn:
    return book
pyron dev --app hello:app

Open:

  • http://127.0.0.1:8000/docs — Swagger
  • http://127.0.0.1:8000/redoc
  • http://127.0.0.1:8000/openapi.json

Pydantic models on the handler are the contract: validation, types, and OpenAPI come from the same class.

Scaffold

pyron init shop                     # default ssr: form + API + admin
pyron init shop --template api      # JSON + docs
pyron init shop --template desk     # same as ssr
pyron init shop --template fullstack

Then cd shop, pyron dev.

Server-rendered pages

from starlette.requests import Request
from pyron import Pyron

app = Pyron(templates="templates", static_dir="static")

@app.get("/")
async def home(request: Request):
    return await app.render(request, "index.html", {"title": "Harbor"})

Pages that should not appear in Swagger: include_in_schema=False.

With the admin panel on, Pages in /admin are CMS views (Markdown + preview). You do not write a route: Jinja2 renders /p/{slug} from page.html. Settings is the shop name, phone, and hours. Details: Admin — Pages · Settings.

Example

cd examples/bookstore
pyron dev --app app.main:app

Shop at /, API at /api/books, admin panel at /admin, CMS pages at /p/about.