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.2.tar.gz (24.7 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.2-py3-none-any.whl (28.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: z8ter-0.2.2.tar.gz
  • Upload date:
  • Size: 24.7 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.2.tar.gz
Algorithm Hash digest
SHA256 d7ca7e796771d66c1836792013b1864b6c460f2b3fd487313a03021f442ac4f5
MD5 64fc4f03e8aaad6a461bdd1c22335d08
BLAKE2b-256 90897603e635bcbfdf94113f34fbdf875f60f0ec1368cb4be959836f174da569

See more details on using hashes here.

File details

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

File metadata

  • Download URL: z8ter-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 28.5 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.2-py3-none-any.whl
Algorithm Hash digest
SHA256 3b8f79b9354773817a29c7f6b5ed66a096a04b9634991b9e5c5f26fed56dd1c2
MD5 4d7499c3927dfdff5f39dccc5f805e4f
BLAKE2b-256 827f196950d184946ea99e724b3baa57e007d7e5c1602283340aeedf7a9e1e1e

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