fastapi-cache icon indicating copy to clipboard operation
fastapi-cache copied to clipboard

Way to dynamically disable/deactivate caching functionality?

Open joeflack4 opened this issue 4 years ago • 4 comments

Description

I'm having an issue where I want to debug certain endpoints locally. But since those responses are cached, my breakpoints are never getting reached.

What's the best way to temporarily disable if I want to set to development/debug mode?

What I've tried

I added this condition to only add FastApiCache if in production:

@app.on_event("startup")
async def startup():
    """Start up FastAPI server"""
    app.state.graph = TccmGraph()
    app.state.graph.connect()
    redis = aioredis.from_url(
        url=get_settings().redis_url,
        encoding="utf8",
        decode_responses=True)
    if get_settings().environment_name == 'production':
        FastAPICache.init(
            backend=RedisBackend(redis),
            prefix="fastapi-cache")

But I'm getting the following error, expectedly:

  File "/Users/joeflack4/virtualenvs/ccdh-terminology-service/lib/python3.9/site-packages/fastapi_cache/__init__.py", line 35, in get_backend
    assert cls._backend, "You must call init first!"  # nosec: B101
AssertionError: You must call init first!

Possible solutions

What's the best way to handle this?

  1. Create my own cache decorator wrapper w/ custom logic around FastAPI's?
  2. Initialize FastAPICache.init() but do it differently?

joeflack4 avatar Sep 22 '21 02:09 joeflack4

That's actual a scene, how other frameworks solve that?

long2ice avatar Sep 22 '21 02:09 long2ice

My workaround

I think that it would be good if the framework (FastApi-Cache2) had a native solution for this, but here's what I did to solve this for now:

app.py: Left it the normal way

@app.on_event("startup")
async def startup():
    """Start up FastAPI server"""
    app.state.graph = TccmGraph()
    app.state.graph.connect()
    redis = aioredis.from_url(
        url=get_settings().redis_url,
        encoding="utf8",
        decode_responses=True)
    FastAPICache.init(
        backend=RedisBackend(redis),
        prefix="fastapi-cache")

New file (called "ccdh.api.cache" in my app)

"""Wrapper around the FastApiCache-2 library"""
from fastapi_cache.decorator import cache

from ccdh.config import get_settings


def nocache(*args, **kwargs):
    def decorator(func):
        return func
    return decorator

# I have an .env file, and my get_settings() reads the .env file
if get_settings().environment_name == 'production':
    cache = cache
else:
    cache = nocache

Import from new file:

# import from wherever you created your new file; in my app, `ccdh.api.cache` is the path
from ccdh.api.cache import cache

@cache() usage remains unchanged

# example of one of our routes:

@router.get('/{model}', response_model=Model, operation_id='get_model', response_model_exclude_none=True,
            responses={
                "200": {
                    "links": {
                        "Entities": {
                            "operationId": "get_model_entities",
                            "parameters": {
                                "model": "$request.path.model"
                            }
                        }
                    }
                }
            })
@cache()  # <---- nothing needs to change here
async def get_model(model: str):
    """Get a single model"""
    return Model(name=model)

joeflack4 avatar Sep 22 '21 15:09 joeflack4

That's good

long2ice avatar Sep 23 '21 02:09 long2ice

This would make a nice example in the documentation at some point.

mjpieters avatar May 15 '23 12:05 mjpieters