Skip to main content

Minimal ASGI app scaffold

Project description

yaaf

YAAF stands for "Yet Another ASGI Framework".

A minimal Python ASGI app scaffold that discovers routes from the filesystem. It includes a tiny router and a CLI wrapper around uvicorn.

Design Goals and Opinions

  • Filesystem-first routing. Routes are inferred from the api/ directory structure rather than declared with decorators. This keeps routing discoverable by looking at the tree.
  • Explicit endpoint files. Each route has _server.py and _service.py to separate request handling from domain logic.
  • Dependency injection without wiring. Services are registered automatically and injected by name/type, so handlers and services focus on behavior, not setup.
  • Static-first routing precedence. Static routes always win over dynamic segments, with warnings when a dynamic route would overlap a static route.
  • Minimal core. The framework is intentionally small and opinionated, leaving room for you to add auth, middleware, validation, etc.

Quickstart

python -m venv .venv
source .venv/bin/activate
pip install -e .

# Run the built-in example routes
yaaf --reload

Example routes:

  • GET /hello
  • GET /<name> (dynamic segment)

Routing Model

Routes are inferred from the api/ directory structure.

  • Every route directory must contain _server.py and _service.py.
  • The route path is /... plus the sub-path under api/.
  • Dynamic segments use [param] directory names and are exposed as params/path_params.
  • Catch-all segments use [...filepath] for multi-segment paths.

Example layout:

api/
  users/
    [id]/
      _server.py
      _service.py
    _server.py
    _service.py
  hello/
    _server.py
    _service.py
  name_dynamic/
    _server.py
    _service.py

Services (_service.py)

Use the @service decorator to mark and register services:

from yaaf import service


@service("UsersService")
class UsersService:
    def get_user(self, user_id: str) -> dict:
        return {"id": user_id, "name": "User"}


service = UsersService

Decorator Options:

  • name: Custom service name for DI resolution (defaults to class name)
  • aliases: Additional names to resolve by

Handlers (_server.py)

Export lowercase HTTP method functions. Import services directly from their source modules:

from yaaf import Request
from yaaf.types import Params
from api.users._service import UsersService


async def get(request: Request, params: Params, service: UsersService) -> dict:
    user_id = params.get("id", "1")
    return service.get_user(user_id)

Injectable Parameters:

  • request gives you the yaaf.Request object
  • params or path_params provides dynamic route parameters
  • Services are injected by type annotations

Service Dependencies

Services can depend on other services via constructor injection:

# users/_service.py
from yaaf import service


@service("UsersService")
class UsersService:
    def get_user(self, user_id: str) -> dict:
        return {"id": user_id, "name": "User"}


service = UsersService
# hello/_service.py
from yaaf import service
from api.users._service import UsersService


@service("HelloService")
class HelloService:
    def __init__(self, users: UsersService) -> None:
        self._users = users

    def message(self) -> str:
        user = self._users.get_user("1")
        return f"Hello, {user['name']}"


service = HelloService

Static File Serving

Use yaaf_static to serve files from a directory:

# api/static/[...filepath]/_server.py
from yaaf.types import Params
from yaaf_static import static_files

async def get(path_params: Params, static=static_files("public")):
    return static(path_params)

The static_files() function returns a handler that:

  • Serves files relative to the specified directory
  • Returns 404 if file not found
  • Blocks path traversal attacks (../)

Running Another App

yaaf --app your_package.app:app

Versioning

This project uses calendar-based versions with a timestamp (UTC). To bump the version:

python scripts/bump_version.py

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

yaafcli-2026.3.20.33459.tar.gz (18.6 kB view details)

Uploaded Source

Built Distribution

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

yaafcli-2026.3.20.33459-py3-none-any.whl (15.0 kB view details)

Uploaded Python 3

File details

Details for the file yaafcli-2026.3.20.33459.tar.gz.

File metadata

  • Download URL: yaafcli-2026.3.20.33459.tar.gz
  • Upload date:
  • Size: 18.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for yaafcli-2026.3.20.33459.tar.gz
Algorithm Hash digest
SHA256 abd292c0b88c7cee7e239f8f09731a11fdd5806187bf43b638a223b944c3b6a9
MD5 758a8b2151c5c2adb7266639e1587023
BLAKE2b-256 413dcff6d63b1c0928113b100255dc1fc37183fc0158c13276567081fb35d9f6

See more details on using hashes here.

File details

Details for the file yaafcli-2026.3.20.33459-py3-none-any.whl.

File metadata

File hashes

Hashes for yaafcli-2026.3.20.33459-py3-none-any.whl
Algorithm Hash digest
SHA256 7ef8c161c8d391c1b068a09b20d01d73e1aa5f0093248405085f4c1289b98b0b
MD5 5ce7412caa3c246e3a35cd8ff2c3f2f9
BLAKE2b-256 9a338316e7a24a8b147d4973290a611cd1ef379d7224187a4d51428fd0d42e5e

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