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 directory structure under consumers/**/api 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 /api/hello
  • GET /api/<name> (dynamic segment)

Routing Model

Routes are inferred from the directory structure under any consumers/**/api directory.

  • Every route directory must contain _server.py and _service.py.
  • The route path is /api/... plus the sub-path after api/.
  • Dynamic segments use [param] directory names and are exposed as params/path_params.

Example layout:

consumers/
  api/
    users/
      _server.py
      _service.py
    hello/
      _server.py
      _service.py
    [name]/
      _server.py
      _service.py

Handlers and Services

In _server.py, export functions named after HTTP methods (lowercase): get, post, etc. The function signature is resolved via dependency injection:

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

Example _server.py:

from consumers.api import HelloService
from yaaf import Request
from yaaf.types import Params


async def get(request: Request, service: HelloService, params: Params):
    return {"message": service.message(), "path": request.path, "params": params}

In _service.py, expose a module-level service instance (or a callable like Service or get_service). Services are registered and can be injected into other services or handlers:

from consumers.api import UsersService


class Service:
    def __init__(self, users: UsersService) -> None:
        self._users = users

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

service = Service

Service-to-Service Injection

Services can depend on other services via type annotations. Example layout:

consumers/
  api/
    users/
      _service.py
      _server.py
    hello/
      _service.py
      _server.py

consumers/api/users/_service.py

class Service:
    def get_user(self, user_id: str) -> dict:
        return {"id": user_id, "name": "Austin"}

service = Service()

consumers/api/hello/_service.py

from consumers.api import UsersService


class Service:
    def __init__(self, users: UsersService) -> None:
        self._users = users

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

service = Service

consumers/api/hello/_server.py

from consumers.api import HelloService
from yaaf import Request


async def get(request: Request, service: HelloService):
    return {"message": service.message(), "path": request.path}

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

Service Type Generation

Every yaaf command regenerates consumers/api/__init__.py for type-checking. You can also run it explicitly:

yaaf gen-services

Dynamic route segments like [name] get Protocol stubs in the generated file since they are not valid import paths.

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.2.8.43615.tar.gz (16.4 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.2.8.43615-py3-none-any.whl (14.3 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for yaafcli-2026.2.8.43615.tar.gz
Algorithm Hash digest
SHA256 c757ae4a5a0d80a4e4ef2aaa9605d6d4a6658a8a4ba53d3fd5e3bbca4dd250f3
MD5 551a02c92fdfd04771a4ed5c3558781f
BLAKE2b-256 8baee19d39e7bfdefa2b188c41bd2a1bde5a43a9c853558f648efe430c3b3f39

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for yaafcli-2026.2.8.43615-py3-none-any.whl
Algorithm Hash digest
SHA256 1c7fd62327b6bdef22bcf2a67c98353c6c6dde23b13134226e0728017e38ad0a
MD5 55c3e47d30e417a7d3a7512b985235c8
BLAKE2b-256 fd2bc301fc2ff3468bf467e5fd3c88da4dc590982a3fd3278a591aab7bc3bc1e

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