> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sevalla.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> These are the docs for Sevalla, a platform for deploying applications, databases, static sites, object storage and load balancers. The dashboard is at https://app.sevalla.com.
> Bold text in steps is an exact dashboard label.
> To act on a Sevalla account, prefer the Sevalla MCP server (https://mcp.sevalla.com/mcp, see https://docs.sevalla.com/ai-agents/overview.md), then the Sevalla CLI (https://docs.sevalla.com/developer-tools/cli.md), then the REST API (https://api-docs.sevalla.com, OpenAPI spec at https://api.sevalla.com/v3/openapi.json).
> Every page index is at https://docs.sevalla.com/llms.txt.

# Cache FastAPI with Redis

> Cache FastAPI responses in a Sevalla Redis database with redis-py's asyncio client and a lifespan handler, and control the CDN and edge cache with Cache-Control.

A cache keeps the result of slow work, such as a report or a call to another API, so the next request reads it instead of computing it again. On Sevalla, the cache is a Redis database that the application reaches over an internal connection, so every instance of the web process shares it. FastAPI uses it through redis-py's asyncio client.

## Before you begin

* A FastAPI project deployed on Sevalla, as in [Deploy FastAPI](/guides/python/fastapi/deploy).

## Create a Redis database

<Steps>
  <Step title="Create the database">
    In the sidebar, click **Databases**, then click **Create database**. Under **Database**, choose **Redis**, or **Valkey**: the code in this guide works with both. Enter `storefront-redis` as the **Display name**, choose the same **Data center** as the application and a **Size**, then click **Create database**.
  </Step>

  <Step title="Connect it to the application">
    In the sidebar, click **Applications**, then click the application. Click the **Networking** tab. Under **Connected services**, click **Add internal connection**. Choose `storefront-redis` as the **Service**, turn on **Add environment variables to the application**, and click **Add internal connection**. Sevalla adds `REDIS_HOST`, `REDIS_PORT`, `REDIS_PASSWORD` and `REDIS_URL`. See [Connect an application](/databases/connect#connect-an-application).
  </Step>
</Steps>

<Frame>
  <img className="block dark:hidden" src="https://assets.sevalla.com/screenshots/light/app-networking-internal-connection-add.png" alt="The Add internal connection sheet with the Service field and the Add environment variables to the application switch" width="576" />

  <img className="hidden dark:block" src="https://assets.sevalla.com/screenshots/dark/app-networking-internal-connection-add.png" alt="The Add internal connection sheet with the Service field and the Add environment variables to the application switch" width="576" />
</Frame>

## Connect to Redis

Install redis-py and update `requirements.txt`:

```bash theme={null}
pip install redis
pip freeze > requirements.txt
```

Open one Redis client when the application starts and close it when it stops, with a lifespan handler. In `storefront/main.py`, replace `app = FastAPI()`:

```python storefront/main.py theme={null}
import os
from contextlib import asynccontextmanager

import redis.asyncio as redis
from fastapi import FastAPI


@asynccontextmanager
async def lifespan(app: FastAPI):
    url = os.environ.get("REDIS_URL", "redis://localhost:6379/0")
    # A Valkey database's URL starts with valkey://, which redis-py doesn't accept.
    app.state.redis = redis.from_url(url.replace("valkey://", "redis://", 1), decode_responses=True)
    yield
    await app.state.redis.aclose()


app = FastAPI(lifespan=lifespan)
```

## Cache a response

Read from the cache first, and store a fresh result for 2 minutes when it's empty:

```python storefront/main.py theme={null}
import json
from datetime import datetime, timezone

from fastapi import Request


@app.get("/report")
async def report(request: Request):
    cache = request.app.state.redis
    cached = await cache.get("report")
    if cached:
        return json.loads(cached)
    result = {"generated_at": datetime.now(timezone.utc).isoformat()}
    await cache.set("report", json.dumps(result), ex=120)
    return result
```

Commit and push the changes. If **Auto-deploy** is off, click **Deploy now** on the application's **Overview** tab.

## Verify

Request the route twice, with your application's domain:

```bash theme={null}
curl https://storefront-x7k2m.sevalla.app/report
curl https://storefront-x7k2m.sevalla.app/report
```

Both responses show the same `generated_at`. After 2 minutes, the next request shows a new time. To look at the key, open the Redis database and click the **Redis studio** tab. See [Database studio](/databases/studio).

## Control the CDN and edge cache

Sevalla can also cache responses at Cloudflare's edge, in front of the application. Turn on the CDN and edge caching on the application's **Networking** tab; see [CDN and edge caching](/applications/caching).

* The CDN caches static files by file extension, such as `.css`, `.js` and images.
* Edge caching caches pages and other responses too. It caches only `GET` requests, skips requests with an `Authorization` header, and doesn't store responses that set a cookie.
* When a response has no `Cache-Control` header, Sevalla adds one. With edge caching on, a `200` response without its own header can stay cached for up to 30 days, unless a deployment or a purge clears the cache first. See [Control caching from your application](/applications/caching#control-caching-from-your-application).

So with edge caching on, set `Cache-Control` on every route. `no-store` keeps a response out of every cache, and `max-age` sets how long it may be kept:

```python storefront/main.py theme={null}
from fastapi import Response


@app.get("/cart")
def cart(response: Response):
    response.headers["Cache-Control"] = "no-store"
    return {"items": []}


@app.get("/catalog")
def catalog(response: Response):
    response.headers["Cache-Control"] = "public, max-age=600"
    return {"products": []}
```

To check what a route sends, run `curl -s -D - -o /dev/null https://storefront-x7k2m.sevalla.app/catalog` and read the `Cache-Control` header. (`curl -I` sends a `HEAD` request, which a FastAPI `GET` route answers with `405`.)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.