Skip to content

Web API Reference

API reference for myfy-web package.


Routes

route module-attribute

route = Router()

Web Module

WebModule

WebModule(router=None)

Web module - provides HTTP server capabilities.

Features: - FastAPI-like routing with @route.get/post/etc decorators - Automatic DI injection in handlers - Request-scoped dependencies - ASGI standard (works with uvicorn, hypercorn, etc.)

Create web module.

Parameters:

Name Type Description Default
router Router | None

Custom router (defaults to global route decorator instance)

None
Source code in packages/myfy-web/myfy/web/module.py
def __init__(self, router: Router | None = None):
    """
    Create web module.

    Args:
        router: Custom router (defaults to global route decorator instance)
    """
    self.router = router or default_router
    self._asgi_app: ASGIApp | None = None

configure

configure(container)

Configure web module.

Registers WebSettings, Router, and ASGI app in the DI container.

Note: In nested settings pattern (ADR-0007), WebSettings is registered by Application. Otherwise, load standalone WebSettings.

Source code in packages/myfy-web/myfy/web/module.py
def configure(self, container) -> None:
    """
    Configure web module.

    Registers WebSettings, Router, and ASGI app in the DI container.

    Note: In nested settings pattern (ADR-0007), WebSettings is registered
    by Application. Otherwise, load standalone WebSettings.
    """
    from myfy.core.di.types import ProviderKey  # noqa: PLC0415

    # Check if WebSettings already registered (from nested app settings)
    key = ProviderKey(WebSettings)
    if key not in container._providers:
        # Load standalone WebSettings
        web_settings = load_settings(WebSettings)
        container.register(
            type_=WebSettings,
            factory=lambda: web_settings,
            scope="singleton",
        )

    # Register router as singleton
    container.register(
        type_=Router,
        factory=lambda: self.router,
        scope="singleton",
    )

    # Register ASGI app factory
    # Note: container is captured from closure, router is the dependency
    def create_asgi_app(router: Router) -> ASGIApp:
        return ASGIApp(container, router)

    container.register(
        type_=ASGIApp,
        factory=create_asgi_app,
        scope="singleton",
    )

start async

start()

Start web module (nothing to do - ASGI server handles this).

Source code in packages/myfy-web/myfy/web/module.py
async def start(self) -> None:
    """Start web module (nothing to do - ASGI server handles this)."""

stop async

stop()

Stop web module gracefully.

Source code in packages/myfy-web/myfy/web/module.py
async def stop(self) -> None:
    """Stop web module gracefully."""

get_asgi_app

get_asgi_app(container, lifespan=None)

Get the ASGI application.

Note: This method is primarily for the myfy run command. The myfy start command uses the factory pattern instead (see myfy.web.factory.create_asgi_app_with_lifespan).

Parameters:

Name Type Description Default
container

DI container

required
lifespan

Optional lifespan context manager for module startup/shutdown

None

Returns:

Type Description
ASGIApp

ASGIApp instance

Source code in packages/myfy-web/myfy/web/module.py
def get_asgi_app(self, container, lifespan=None) -> ASGIApp:
    """
    Get the ASGI application.

    Note: This method is primarily for the `myfy run` command.
    The `myfy start` command uses the factory pattern instead
    (see myfy.web.factory.create_asgi_app_with_lifespan).

    Args:
        container: DI container
        lifespan: Optional lifespan context manager for module startup/shutdown

    Returns:
        ASGIApp instance
    """
    if self._asgi_app is None:
        if lifespan is not None:
            # Create new ASGI app with lifespan
            router = container.get(Router)
            self._asgi_app = ASGIApp(container, router, lifespan=lifespan)
        else:
            # Get from DI container (no lifespan)
            self._asgi_app = container.get(ASGIApp)
    return self._asgi_app

Handlers

handlers

Handler execution with dependency injection.

Compiles injection plans for routes at startup.

HandlerExecutor

HandlerExecutor(container)

Executes route handlers with dependency injection.

Resolves dependencies from the DI container and injects them along with path parameters and request body.

Source code in packages/myfy-web/myfy/web/handlers.py
def __init__(self, container: Any):
    self.container = container
    self._execution_plans: dict[str, Callable] = {}
    self._logger = logging.getLogger(__name__)

compile_route

compile_route(route)

Compile an execution plan for a route.

Analyzes the handler signature and builds a fast execution path.

Source code in packages/myfy-web/myfy/web/handlers.py
def compile_route(self, route: Route) -> None:
    """
    Compile an execution plan for a route.

    Analyzes the handler signature and builds a fast execution path.
    """
    hints = get_type_hints(route.handler)

    # Build execution plan
    async def execute(request: Request, path_params: dict[str, Any]) -> Response:
        kwargs = {}

        # 1. Inject path parameters
        for param_name in route.path_params:
            param_type = hints.get(param_name, str)
            raw_value = path_params.get(param_name)
            # Convert to appropriate type with validation
            kwargs[param_name] = self._convert_param(raw_value, param_type, param_name)

        # 2. Inject request body if needed
        if route.body_param:
            body_type = hints.get(route.body_param)
            if body_type is not None:
                body_data = await self._parse_body(request, body_type)
                kwargs[route.body_param] = body_data

        # 3. Inject dependencies from container
        for param_name in route.dependencies:
            param_type = hints.get(param_name)
            if param_type:
                try:
                    # Special case: inject Request or RequestContext
                    if param_type == Request:
                        kwargs[param_name] = request
                    elif param_type == RequestContext:
                        kwargs[param_name] = get_request_context()
                    else:
                        # Resolve from DI container
                        kwargs[param_name] = self.container.get(param_type)
                except Exception as e:
                    return JSONResponse(
                        {"error": f"Failed to inject {param_name}: {e!s}"},
                        status_code=500,
                    )

        # 4. Execute handler
        try:
            if iscoroutinefunction(route.handler):
                result = await route.handler(**kwargs)
            else:
                result = route.handler(**kwargs)

            # Convert result to response
            return self._make_response(result)

        except HTTPException as e:
            # Known HTTP exceptions - safe to expose
            return JSONResponse(
                {"detail": e.detail},
                status_code=e.status_code,
            )
        except Exception as e:
            # Unknown errors - sanitize based on environment
            return self._make_error_response(e)

    self._execution_plans[self._route_key(route)] = execute

execute_route async

execute_route(route, request, path_params)

Execute a route handler.

Source code in packages/myfy-web/myfy/web/handlers.py
async def execute_route(
    self, route: Route, request: Request, path_params: dict[str, Any]
) -> Response:
    """Execute a route handler."""
    plan = self._execution_plans.get(self._route_key(route))
    if plan is None:
        raise RuntimeError(f"Route not compiled: {route}")
    return await plan(request, path_params)

ASGI

asgi

ASGI adapter using Starlette.

Integrates myfy routing and DI with ASGI protocol.

ASGIApp

ASGIApp(container, router, lifespan=None)

ASGI application adapter.

Bridges myfy routes and DI with Starlette's ASGI implementation.

Create ASGI app.

Parameters:

Name Type Description Default
container Any

DI container (must be compiled)

required
router Router

Router with registered routes

required
lifespan Any

Lifespan context manager (optional)

None
Source code in packages/myfy-web/myfy/web/asgi.py
def __init__(self, container: Any, router: Router, lifespan: Any = None):
    """
    Create ASGI app.

    Args:
        container: DI container (must be compiled)
        router: Router with registered routes
        lifespan: Lifespan context manager (optional)
    """
    self.container = container
    self.router = router
    self.executor = HandlerExecutor(container)

    # Compile all routes
    for route in router.get_routes():
        self.executor.compile_route(route)

    # Build Starlette app
    self.app = self._build_starlette_app(lifespan)