Skip to content

Rollback database session in dependency using custom route class #2066

@finswimmer

Description

@finswimmer

Hello,

what I try to achieve is to rollback a database session on an unhanded exception and customize the response in that case.

The first one can be done, handling the database connection in its own method with a try, except, finally block and use it as a dependency. The second one can be done using a custom route class.

But combining both will not work as I wish, because the dependency would be executed after leaving the custom route class.

I've found similar issues/questions about this, but no satisfying answer.

Here's my minimal example which of course doesn't work as a like to have. (Of course the get_db() method returns a database session in real life)

What must I change or how does the pattern must look like to reach out what I want?

#!usr/bin/env python
# -*- coding: utf-8 -*-
import logging
from typing import Callable

import uvicorn
from fastapi import Depends, FastAPI, Request, Response
from fastapi.exceptions import HTTPException
from fastapi.routing import APIRoute

logging.basicConfig(level=logging.DEBUG)


class ExceptionRouter(APIRoute):
    def get_route_handler(self) -> Callable:
        original_route_handler = super().get_route_handler()

        async def custom_route_handler(request: Request) -> Response:
            try:
                return await original_route_handler(request)
            except Exception as exc:
                logging.exception("Unexpected error")
                body = await request.body()
                detail = {"errors": str(exc), "body": body.decode()}
                raise HTTPException(status_code=500, detail=detail)

        return custom_route_handler


def get_db() -> str:
    db = "mydb"
    try:
        yield db
    except Exception:
        logging.debug("rolling back db")
        raise
    finally:
        logging.debug("closing db")


app = FastAPI()
app.router.route_class = ExceptionRouter


@app.get("/exception",)
async def raise_exception(db: str = Depends(get_db)):
    raise Exception("Unexpected error!")


if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)

fin swimmer

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions