Skip to content

SlowAPI

One handler. Two protocols.

A Python web framework that is a WSGI application and an ASGI application at the same time. Express ergonomics, FastAPI typing, NestJS structure — and zero required runtime dependencies.

The whole idea, in one file

from slowfw import SlowAPI

app = SlowAPI()


@app.get("/users/{id:int}")
def get_user(id: int) -> dict:          # (1)!
    return {"id": id}


@app.get("/fanout")
async def fanout() -> dict:             # (2)!
    return {"results": await gather_everything()}
  1. A synchronous handler. On WSGI it runs with no event loop involved at all.
  2. An asynchronous handler, in the same file, served by the same object.
gunicorn app:app --workers 4     # WSGI — and the async handler still works
uvicorn  app:app                 # ASGI — and the sync handler still works

Same file. No wsgi.py shim. No rewrite. No decision made in week one that you have to live with in year two.


Start here

  • :material-download: Installation


    pip install slowfw. Nothing else is required — every dependency is an opt-in extra.

  • :material-rocket-launch: Quickstart


    A working, tested, validated service in about ten minutes.

  • :material-swap-horizontal: Migrating


    What maps to what, coming from Flask, FastAPI, Express or NestJS.

  • :material-book-open-variant: The guide


    Sixteen chapters, written to be read in order rather than only grepped.


What makes it different

It is not a compatibility shim. A fully synchronous request on the WSGI path never creates an event loop — no task, no scheduler, no thread hop. The dispatch pipeline is written once as async def so both protocols share one implementation, then stepped to completion by hand when nothing in the chain can suspend. How that works.

That is also where the performance difference comes from:

Route FastAPI (ASGI) SlowAPI (ASGI) SlowAPI (WSGI)
plain text 154.1µs 63.3µs 17.8µs
json dict 155.8µs 68.7µs 22.8µs
path + query validated 175.5µs 78.1µs 32.8µs
async json 15.6µs 17.2µs

Median per-request framework overhead, in-process, identical handlers, byte-identical responses. One laptop, no concurrency. Reproduce it with python benchmarks/compare.py.

Read that as one result rather than four. The gap is the worker-thread hop, not the framework. A def handler under ASGI must be offloaded to a thread so it cannot stall the event loop, and that hop costs more than everything else on the row combined. On WSGI, SlowAPI never makes it. Where no hop is involved — an async def handler — the two are level.

So the honest claim is a narrow one: synchronous code is substantially cheaper here, asynchronous code is a wash, and you should be choosing on features and ecosystem, where FastAPI is far ahead.


What is in the box

  • :material-check-decagram: Typed and validated


    Parameters, bodies and DTOs are coerced and validated from annotations, with or without Pydantic. OpenAPI is generated from the same source.

    Validation · Serialization

  • :material-layers-triple: Structure when you need it


    Controllers, modules, providers and scoped dependency injection — available when an application grows, absent until then.

    Modules · DI

  • :material-shield-lock: Secure by default


    Signed sessions, path-traversal defences, proxy-header handling and security headers that are on before you configure anything.

    Security

  • :material-heart-pulse: Ready for an operator


    Liveness and readiness endpoints, background tasks, request deadlines, and startup route validation that fails the build instead of the deploy.

    Background and health · Deployment


Honest limits

This is a young framework, and the useful question is not whether the code is good but whether the project is old enough to trust with someone else's data.

It has no WebSocket support yet, no third-party ecosystem, one maintainer, and no release history. Nobody adversarial has audited it. Those are properties of a project's age, and no amount of engineering shortens them.

Use it for internal tools, side projects and services you own end to end. The FAQ answers this at more length, and why it exists makes the case for the design itself.