Blog

I don't like dependency injection

I don't like dependency injection. Here's a comparison of a starlette request handler vs. a FastAPI request handler:

Starlette

def get_pagination_params(request: Request) -> dict[str, int]:
    skip = int(request.query_params.get("skip", 0))
    limit = int(request.query_params.get("limit", 10))
    return {"skip": skip, "limit": limit}

async def get_users(request: Request) -> JSONResponse:
    params = get_pagination_params(request)
    return JSONResponse(params)

FastAPI

# FastAPI
async def pagination(skip: int = 0, limit: int = 10):
    return {"skip": skip, "limit": limit}

@app.get("/users/")
async def get_users(params: Annotated[dict, Depends(pagination)]):
    return params

Implementation vs. Language

In the first, I don't need to know how starlette works. I know that there is a Request object and I can inspect what data it makes available to me.

In the second, I need to understand FastAPI and that it does magic dependency injection to inject query parameters as function args.

The few times I've looked into using FastAPI or Litestar this was one (of many) things that turns me off. I want a library that helps me with some of the low-level plumbing for HTTP servers and gets out of the way so I can use my Python knowledge directly. I'm not interested in learning the internals of FastAPI and how to correctly decorate my request handlers to make it happy. As a consequence for Python services I often just use starlette directly.