Fastapi: [QUESTION] Error handlers in separate module?

Created on 24 Jan 2020  路  4Comments  路  Source: tiangolo/fastapi

First check

  • [x] I used the GitHub search to find a similar issue and didn't find it.
  • [x] I searched the FastAPI documentation, with the integrated search.
  • [x] I already searched in Google "How to X in FastAPI" and didn't find any information.

Description

How can I have my error handlers in a separate module cleanly? My current fix for this is to do what this stackoverflow answer says in the first section. I don't necessarily like having to create the app first and then do the import of the error handler module though. I would like to be able to have all my imports before any code is actually run (essentially I don't want to have circular dependencies).

Additional context

I found these different doc sections in FastAPI docs, but couldn't find anything about having error handlers in a separate module.

I do notice that in the link talking about bigger applications it says it is the equivalent of blueprints. Flask blueprints allow me to put error handlers in a separate module, so am I missing something or does this feature not exist yet?

question

Most helpful comment

Yeah, another approach is to put the error-handler-adding decorators inside a function. Adapting the fastapi docs code, it could look something like this:

# error_handler.py
from fastapi import FastAPI

def add_unicorn_exception_handler(app: FastAPI) -> None:
    @app.exception_handler(UnicornException)
    async def unicorn_exception_handler(request: Request, exc: UnicornException):
        return JSONResponse(
            status_code=418,
            content={"message": f"Oops! {exc.name} did something. There goes a rainbow..."},
        )
from error_handler import add_unicorn_exception_handler

app = FastAPI()
...
add_unicorn_exception_handler(app)

You can follow this pattern to create functions that add your start-up events, shut-down events, and other app initialization. For example, I use it to simplify the operation IDs in my generated OpenAPI schemas (so my generated clients are nicer to work with):

from fastapi import FastAPI
from fastapi.routing import APIRoute


def simplify_operation_ids(app: FastAPI) -> None:
    """ Simplify operation IDs so that generated clients have simpler api function names """
    for route in app.routes:
        if isinstance(route, APIRoute):
            route.operation_id = route.name

All 4 comments

@joe-eklund
Hi! Since FastAPI does nothing with Starlette's error handling system, except for registering some handlers for its own exceptions, you can use the Starlette.add_exception_handler method instead of the Starlette.exception_handler decorator to register new handlers.

Here is a small example, also you can look here:

from fastapi import FastAPI
from starlette.requests import Request
from starlette.responses import JSONResponse

app = FastAPI()


async def my_handler_for_runtime_error(
    request: Request, exc: RuntimeError
) -> JSONResponse:
    return JSONResponse(content={"error_type": "runtime_error"}, status_code=500)


@app.get("/")
async def buggy_route() -> None:
    raise RuntimeError("error")


app.add_exception_handler(RuntimeError, my_handler_for_runtime_error)

Thank you @nsidnev! That worked perfectly.

Yeah, another approach is to put the error-handler-adding decorators inside a function. Adapting the fastapi docs code, it could look something like this:

# error_handler.py
from fastapi import FastAPI

def add_unicorn_exception_handler(app: FastAPI) -> None:
    @app.exception_handler(UnicornException)
    async def unicorn_exception_handler(request: Request, exc: UnicornException):
        return JSONResponse(
            status_code=418,
            content={"message": f"Oops! {exc.name} did something. There goes a rainbow..."},
        )
from error_handler import add_unicorn_exception_handler

app = FastAPI()
...
add_unicorn_exception_handler(app)

You can follow this pattern to create functions that add your start-up events, shut-down events, and other app initialization. For example, I use it to simplify the operation IDs in my generated OpenAPI schemas (so my generated clients are nicer to work with):

from fastapi import FastAPI
from fastapi.routing import APIRoute


def simplify_operation_ids(app: FastAPI) -> None:
    """ Simplify operation IDs so that generated clients have simpler api function names """
    for route in app.routes:
        if isinstance(route, APIRoute):
            route.operation_id = route.name

Thanks for the help here everyone! :cake: :bowing_man:

Thanks @joe-eklund for reporting back and closing the issue :+1:

Was this page helpful?
0 / 5 - 0 ratings

Related issues

scheung38 picture scheung38  路  3Comments

laith43d picture laith43d  路  3Comments

iwoloschin picture iwoloschin  路  3Comments

updatatoday picture updatatoday  路  3Comments

kkinder picture kkinder  路  3Comments