Skip to main content

Minimal Starlette-powered app framework with pages, APIs, and a DX-first CLI.

Project description

Z8ter

alt text8ter is a lightweight, Laravel-inspired full-stack Python web framework built on [Starlette], designed for rapid development with tight integration between backend logic and frontend templates plus small client-side “islands” where they make sense.

flowchart LR
  %% --------- Style (GitHub-friendly) ---------
  classDef box fill:#1113,stroke:#888,rx:6,ry:6,color:#eee
  classDef key fill:#2563eb,stroke:#1e40af,color:#fff,rx:6,ry:6
  classDef accent fill:#059669,stroke:#047857,color:#fff,rx:6,ry:6
  classDef warn fill:#dc2626,stroke:#991b1b,color:#fff,rx:6,ry:6
  linkStyle default stroke:#94a3b8,color:#94a3b8

  %% --------- Browser ---------
  subgraph B[🌐 Browser]
    Bhtml["HTML (Jinja)"]:::box
    Bisland["JS Island (if page_id)"]:::box
    Bcookie["Cookie z8_sid"]:::key
  end

  %% --------- Z8ter App ---------
  subgraph Z[⚡ Z8ter App]
    direction TB
    MW1["Session Middleware"]:::box
    MW2["Auth Middleware\n→ sets request.state.user"]:::accent
    Router["File-based Router\nviews/, api/"]:::box

    subgraph SSR[Views]
      Tmpl["Jinja2 Engine"]:::box
    end

    subgraph API[APIs]
      Deco["Decorator-driven Endpoints"]:::box
    end

    subgraph Auth[Auth Backends]
      URepo["UserRepo (pluggable)"]:::box
      SRepo["SessionRepo (pluggable)"]:::box
    end

    Err["Global Exception Handlers"]:::warn
  end

  %% --------- Assets (optional) ---------
  A["Vite / Static Assets"]:::box

  %% --------- Request path ---------
  B -->|"HTTP Request"| MW1 --> MW2 --> Router
  Router -->|SSR| Tmpl -->|"HTML"| Bhtml
  Router -->|API| Deco -->|"JSON"| Bhtml
  B -- "Hydrate" --> Bisland

  %% --------- Cookies & Identity ---------
  MW2 <-->|read/write SID| Bcookie
  MW2 -->|lookup| SRepo
  MW2 -->|load user| URepo

  %% --------- Errors ---------
  Router --> Err -->|"JSON error"| Bhtml
  Tmpl --> Err
  Deco --> Err

  %% --------- Assets ---------
  Bhtml -->|"links/scripts"| A

Features

1) File-Based Views (SSR)

  • Files under views/ become routes automatically.
  • Each view pairs Python logic with a Jinja template in templates/.
  • A stable page_id (derived from views/ path) is injected into templates and used by the frontend loader to hydrate per-page JS.

2) Jinja2 Templating

  • Template inheritance with {% extends %} / {% block %}.
  • Templates live in templates/ (default extension: .jinja).

3) CSR “Islands”

  • A tiny client router lazy-loads /static/js/pages/<page_id>.js and runs its default export.
  • Great for interactive bits (theme toggles, pings, clipboard, etc.) without going full SPA.

4) Decorator-Driven APIs

  • Classes under api/ subclass API and register endpoints with a decorator.
  • Each class mounts under /api/<id> (derived from module path).

Example shape (conceptual):

api/hello.py      →  /api/hello
views/about.py    →  /about
templates/about.jinja + static/js/pages/about.js (island)

Getting Started

Prerequisites

  • Python 3.11+ and pip
  • Node 18+ and npm

Install & Run (dev)

# 1) Python deps (in a venv)
python -m venv .venv
source .venv/bin/activate        # Windows: .\.venv\Scripts\Activate.ps1
pip install -r requirements.txt  # or: pip install -e .

# 2) Frontend deps
npm install

# 3) Dev server(s)
npm run dev

npm run dev runs the dev workflow (backend + assets). Check the terminal for the local URL.


Project Structure

.
├─ api/                     # API classes (@API.endpoint)
│  └─ hello.py
├─ views/                   # File-based pages (SSR)
│  └─ index.py
├─ templates/               # Jinja templates
│  ├─ base.jinja
│  └─ index.jinja
├─ static/
│  └─ js/
│     └─ pages/             # Per-page islands: about.js, app/home.js, ...
│        └─ common.js
├─ z8ter/                   # Framework core (Page, API, router)
└─ main.py                  # App entrypoint

Usage Examples

View + Template (SSR)

{# templates/index.jinja #}
{% extends "base.jinja" %}
{% block content %}
  <h1>{{ title }}</h1>
  <div id="api-response"></div>
{% endblock %}

Client Island (runs when page_id matches)

// static/js/pages/common.ts (or a specific page module)
export default async function init() {
  // hydrate interactive bits, fetch data, etc.
}

Minimal API Class

# api/hello.py
from z8ter.api import API

class Hello(API):
    @API.endpoint("GET", "/hello")
    async def hello(self, request):
        return {"ok": True, "message": "Hello from Z8ter"}

Main Application (bootstrapping alt text8ter)

Your app entrypoint defines the pipeline of features by chaining builder steps. This example shows a minimal project with templating, Vite, and authentication wired in.

# main.py
from z8ter.builders.app_builder import AppBuilder
from app.identity.data.session_repo import InMemorySessionRepo
from app.identity.data.user_repo import InMemoryUserRepo

app_builder = AppBuilder()
app_builder.use_config(".env")             # load environment config
app_builder.use_templating()               # enable Jinja2 templates
app_builder.use_vite()                     # dev/prod asset handling
app_builder.use_auth_repos(                # provide your own repos
    session_repo=InMemorySessionRepo(),
    user_repo=InMemoryUserRepo()
)
app_builder.use_authentication()           # auth middleware + request.state.user
app_builder.use_errors()                   # global JSON error handlers

if __name__ == "__main__":
    app = app_builder.build()

Authentication (Sessions + Users)

alt text8ter ships with a minimal but flexible authentication layer. You provide two repos — SessionRepo and UserRepo — and alt text8ter wires them into middleware that sets request.state.user.

Setup in AppBuilder

from z8ter.auth.inmemory_repos import InMemorySessionRepo, InMemoryUserRepo

builder.use_sessions()  # enables secure cookie handling

builder.use_auth_repos(
    session_repo=InMemorySessionRepo(),
    user_repo=InMemoryUserRepo()
)

builder.use_authentication()  # middleware populates request.state.user

Planned

  • Stripe integration: pricing page, checkout routes, webhooks
  • DB adapters: SQLite default, Postgres option

Philosophy

  • Conventions over configuration
  • SSR with CSR islands
  • Small surface area; sharp, pragmatic tools

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

z8ter-0.2.0.tar.gz (24.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

z8ter-0.2.0-py3-none-any.whl (28.0 kB view details)

Uploaded Python 3

File details

Details for the file z8ter-0.2.0.tar.gz.

File metadata

  • Download URL: z8ter-0.2.0.tar.gz
  • Upload date:
  • Size: 24.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.5

File hashes

Hashes for z8ter-0.2.0.tar.gz
Algorithm Hash digest
SHA256 3fdbc5af2cca7b06887c25c030c1244731fe29521a49df1572c452ffdb8041cb
MD5 3ea177ed2921053d3b89dd0fdef6306b
BLAKE2b-256 3b7406c73de713b92d2e004a170582e3419794745dbc0ca9949650f7352de3b2

See more details on using hashes here.

File details

Details for the file z8ter-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: z8ter-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 28.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.5

File hashes

Hashes for z8ter-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 216b133467e728a7eb4e2a462777fc6990353720155449a51fd7720e240087cb
MD5 7be5cb6cf294fedb93133205a3131f14
BLAKE2b-256 94d327d82a6bf4b287fbebf823cb406151aedc9214cfda8a543d21c431149829

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page