{
  "id": "fastapi",
  "title": "Redis with FastAPI",
  "url": "https://redis.io/docs/latest/integrate/fastapi/",
  "summary": "Add idiomatic Redis connection management and dependency-injection caching to FastAPI apps.",
  "tags": [
    "docs",
    "integrate",
    "oss",
    "rs",
    "rc"
  ],
  "last_updated": "2026-07-02T16:24:13+02:00",
  "children": [],
  "page_type": "content",
  "content_hash": "ed74c29b061453d5b597d288a9709f522aa116eb8d9d8545facee685f3ce3996",
  "sections": [
    {
      "id": "requirements",
      "title": "Requirements",
      "role": "content",
      "text": "See [Requirements](https://github.com/redis/fastapi-redis-sdk#requirements) on the\nGitHub repo for the full set of dependencies used by `fastapi-redis-sdk`.\n\nYou also need a running Redis server. You can run one locally with\n[Redis Open Source](https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis),\nuse [Docker](https://hub.docker.com/_/redis), or connect to\n[Redis Cloud](https://redis.io/cloud/)."
    },
    {
      "id": "install-and-import",
      "title": "Install and import",
      "role": "setup",
      "text": "Use the following command to install `fastapi-redis-sdk`:\n\n[code example]\n\nNote that although you install the package as `fastapi-redis-sdk`, you import it as\n`redis_fastapi`:\n\n[code example]"
    },
    {
      "id": "quick-start",
      "title": "Quick start",
      "role": "content",
      "text": "Attach Redis to your app with the [fluent builder](https://en.wikipedia.org/wiki/Fluent_interface). The `lifespan()` call hooks into\nthe [FastAPI lifespan events](https://fastapi.tiangolo.com/advanced/events/) to open a\nconnection pool at startup and close it cleanly on shutdown. Inject `AsyncRedisDep`\ninto your `async` endpoints to get a ready-to-use client:\n\n[code example]\n\nThe builder wraps any existing lifespan, so multiple libraries can register their own\nstartup and shutdown logic without conflicting."
    },
    {
      "id": "configuration",
      "title": "Configuration",
      "role": "configuration",
      "text": "All settings are read from environment variables prefixed with `REDIS_`, or from a\n`.env` file in your project root. The simplest setup is a single connection URL:\n\n[code example]\n\nAlternatively, you can configure individual fields:\n\n[code example]\n\nWhen `REDIS_URL` is set, it takes precedence over the individual connection fields.\nThe most commonly used variables are described in the table below:\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `REDIS_URL` | - | Full Redis URL (takes precedence over the fields below) |\n| `REDIS_HOST` | `localhost` | Redis host |\n| `REDIS_PORT` | `6379` | Redis port |\n| `REDIS_PASSWORD` | - | Redis password (stored securely as a Pydantic `SecretStr`) |\n| `REDIS_SSL` | `false` | Enable TLS (or use a `rediss://` URL) |\n| `REDIS_CLUSTER` | `false` | Enable [OSS Cluster mode](#cluster-mode) |\n| `REDIS_PREFIX` | `redis:fastapi` | Global prefix applied to all keys |\n| `REDIS_DEFAULT_TTL` | `0` | Default cache TTL in seconds (`0` = no expiry) |\n| `REDIS_MAX_CONNECTIONS` | - | Maximum pooled connections |\n\nConfiguration is validated with [Pydantic Settings](https://docs.pydantic.dev/latest/concepts/pydantic_settings/),\nso invalid values (for example, a port outside 1–65535) fail fast at startup. For the\nfull environment-variable reference, TLS options, and programmatic configuration, see\nthe [SDK configuration guide](https://redis.github.io/fastapi-redis-sdk/guide/configuration/)."
    },
    {
      "id": "caching",
      "title": "Caching",
      "role": "content",
      "text": "Enable caching by adding `.caching()` to the builder chain. The SDK then offers two\ncomplementary approaches that share the same connection pool:\n\n| Approach | Best for |\n|----------|----------|\n| `cache()`, `cache_evict()`, `cache_put()` | Most endpoints — read, invalidate, and write-through |\n| `CacheBackend` | Complex invalidation, conditional or dynamic caching |"
    },
    {
      "id": "dependency-injection-factories",
      "title": "Dependency-injection factories",
      "role": "content",
      "text": "`cache()`, `cache_evict()`, and `cache_put()` are\n[dependency](https://fastapi.tiangolo.com/tutorial/dependencies/) factories you attach\nto a route. Because all three share the same `key_builder`, a `GET`, `DELETE`, and\n`PUT` on the same path target the exact same cache key:\n\n[code example]\n\nCached responses include an `X-Redis-Cache` header (`HIT` or `MISS`) along with\n`Cache-Control` and `ETag` headers.\n\nThe example below drives a cached endpoint with FastAPI's\n[`TestClient`](https://fastapi.tiangolo.com/reference/testclient/) so you can see the\n`MISS` → `HIT` → eviction cycle, plus the HTTP caching headers, in action:\n\nFoundational: cache a GET response so the first request is a MISS and the next is served from Redis as a HIT\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]\n\n\n\nEvicting a resource clears its cached entry, so the following read is a `MISS` again:\n\nInvalidate a cached entry so the next read recomputes the response\n\n**Difficulty:** Beginner\n\n**Available in:** Python\n\n##### Python\n\n[code example]"
    },
    {
      "id": "cachebackend-for-full-control",
      "title": "CacheBackend for full control",
      "role": "content",
      "text": "For conditional caching, cascade invalidation, dynamic TTLs, or caching intermediate\nresults, inject `CacheBackendDep` and call its `get`/`set`/`delete`/`has`/`delete_group`\nmethods directly. Values are serialized to and from JSON automatically:\n\n[code example]\n\nSee the [SDK caching guide](https://redis.github.io/fastapi-redis-sdk/guide/caching/)\nfor detailed patterns and best practices."
    },
    {
      "id": "http-caching",
      "title": "HTTP caching",
      "role": "content",
      "text": "Responses cached with the DI factories carry standard HTTP caching headers, so clients\nand proxies can revalidate cheaply. The SDK sets `Cache-Control` from the entry's TTL\nand emits a weak `ETag`; when a client returns that tag in an `If-None-Match` header,\nthe server responds with `304 Not Modified` and no body:\n\nUse ETag and Cache-Control headers so a revalidation request returns 304 Not Modified\n\n**Difficulty:** Intermediate\n\n**Available in:** Python\n\n##### Python\n\n[code example]"
    },
    {
      "id": "cluster-mode",
      "title": "Cluster mode",
      "role": "content",
      "text": "To work with an [OSS Cluster](https://redis.io/docs/latest/operate/oss_and_stack/management/scaling),\nset `REDIS_CLUSTER=true` and point `REDIS_URL` at the cluster nodes:\n\n[code example]\n\nIn cluster mode, `AsyncRedisDep` yields an `AsyncRedisCluster` client."
    },
    {
      "id": "observability",
      "title": "Observability",
      "role": "content",
      "text": "The SDK can emit [OpenTelemetry](https://opentelemetry.io/) spans and metrics for every\ncache operation. Telemetry is opt-in and a zero-cost no-op when disabled. Install the\noptional dependency and enable it on the builder:\n\n[code example]\n\n[code example]\n\nCalling `.otel()` on the builder is what activates the cache telemetry. To also emit\nredis-py's low-level command spans and connection-pool metrics, set\n`REDIS_OTEL_REDIS_ENABLED=true`. See the\n[SDK configuration guide](https://redis.github.io/fastapi-redis-sdk/guide/configuration/#opentelemetry)\nfor the full list of spans and metrics that are emitted."
    },
    {
      "id": "more-information",
      "title": "More information",
      "role": "content",
      "text": "- [fastapi-redis-sdk on GitHub](https://github.com/redis/fastapi-redis-sdk)\n- [fastapi-redis-sdk documentation](https://redis.github.io/fastapi-redis-sdk/)\n- [fastapi-redis-sdk on PyPI](https://pypi.org/project/fastapi-redis-sdk/)\n- [redis-py client documentation](https://redis.io/docs/latest/develop/clients/redis-py)\n- [FastAPI documentation](https://fastapi.tiangolo.com/)"
    }
  ],
  "examples": [
    {
      "id": "install-and-import-ex0",
      "language": "bash",
      "code": "pip install fastapi-redis-sdk",
      "section_id": "install-and-import"
    },
    {
      "id": "install-and-import-ex1",
      "language": "python",
      "code": "from redis_fastapi import FastAPIRedis",
      "section_id": "install-and-import"
    },
    {
      "id": "quick-start-ex0",
      "language": "python",
      "code": "from fastapi import FastAPI\nfrom redis_fastapi import FastAPIRedis, AsyncRedisDep\n\napp = FastAPI()\nFastAPIRedis(app).lifespan()\n\n@app.get(\"/items\")\nasync def get_items(redis: AsyncRedisDep):\n    return {\"items\": await redis.get(\"items\")}",
      "section_id": "quick-start"
    },
    {
      "id": "configuration-ex0",
      "language": "bash",
      "code": "export REDIS_URL=redis://user:pass@host:6379/0",
      "section_id": "configuration"
    },
    {
      "id": "configuration-ex1",
      "language": "bash",
      "code": "export REDIS_HOST=redis.example.com\nexport REDIS_PORT=6380\nexport REDIS_PASSWORD=secret",
      "section_id": "configuration"
    },
    {
      "id": "dependency-injection-factories-ex0",
      "language": "python",
      "code": "from fastapi import Depends, FastAPI\nfrom redis_fastapi import FastAPIRedis, cache, cache_evict, cache_put, default_key_builder\n\napp = FastAPI()\nFastAPIRedis(app).lifespan().caching()\n\n# READ - cache the GET response for 5 minutes\n@app.get(\"/products/{product_id}\", dependencies=[Depends(cache(ttl=300, eviction_group=\"products\"))])\nasync def get_product(product_id: int):\n    return await db.get_product(product_id)\n\n# INVALIDATE - evict the cached entry when the product is deleted\n@app.delete(\n    \"/products/{product_id}\",\n    dependencies=[Depends(cache_evict(eviction_group=\"products\", key_builder=default_key_builder))],\n)\nasync def delete_product(product_id: int):\n    await db.delete(product_id)\n\n# WRITE-THROUGH - refresh the cache so the next GET is a HIT\n@app.put(\n    \"/products/{product_id}\",\n    dependencies=[Depends(cache_put(eviction_group=\"products\", key_builder=default_key_builder, ttl=300))],\n)\nasync def replace_product(product_id: int, body: Product):\n    return await db.update(product_id, body)",
      "section_id": "dependency-injection-factories"
    },
    {
      "id": "dependency-injection-factories-ex1",
      "language": "python",
      "code": "# The first request is a MISS: the handler runs and the response is cached.\nfirst = client.get(\"/cache-demo\")\nprint(first.headers[\"X-Redis-Cache\"])\n# >>> MISS\n\n# A second request within the TTL is a HIT, served from Redis without\n# re-running the handler, so the cached body is returned unchanged.\nsecond = client.get(\"/cache-demo\")\nprint(second.headers[\"X-Redis-Cache\"])\n# >>> HIT\n\nprint(first.json() == second.json())\n# >>> True",
      "section_id": "dependency-injection-factories"
    },
    {
      "id": "dependency-injection-factories-ex2",
      "language": "python",
      "code": "# Deleting the resource evicts its cache entry, so the next read is a MISS\n# again and the handler recomputes a fresh response.\nclient.delete(\"/cache-demo\")\n\nthird = client.get(\"/cache-demo\")\nprint(third.headers[\"X-Redis-Cache\"])\n# >>> MISS\n\nprint(third.json() == first.json())\n# >>> False",
      "section_id": "dependency-injection-factories"
    },
    {
      "id": "cachebackend-for-full-control-ex0",
      "language": "python",
      "code": "from redis_fastapi import CacheBackendDep\n\n@app.get(\"/dashboard/{user_id}\")\nasync def dashboard(user_id: int, cache: CacheBackendDep):\n    cached = await cache.get(f\"stats:{user_id}\", eviction_group=\"dashboard\")\n    if cached is not None:\n        return cached\n    result = await compute_dashboard(user_id)\n    await cache.set(f\"stats:{user_id}\", result, ttl=300, eviction_group=\"dashboard\")\n    return result",
      "section_id": "cachebackend-for-full-control"
    },
    {
      "id": "http-caching-ex0",
      "language": "python",
      "code": "# Cached responses carry standard HTTP caching headers. Evict first so the next\n# request is a fresh MISS whose Cache-Control reflects the full 30-second TTL.\nclient.delete(\"/cache-demo\")\nmiss = client.get(\"/cache-demo\")\nprint(miss.headers[\"Cache-Control\"])\n# >>> max-age=30\n\n# The cached response also carries a weak ETag. Replaying it with If-None-Match\n# lets the server answer 304 Not Modified with no body.\netag = miss.headers[\"ETag\"]\nnot_modified = client.get(\"/cache-demo\", headers={\"If-None-Match\": etag})\nprint(not_modified.status_code)\n# >>> 304",
      "section_id": "http-caching"
    },
    {
      "id": "cluster-mode-ex0",
      "language": "bash",
      "code": "export REDIS_CLUSTER=true\nexport REDIS_URL=redis://node1:6379,node2:6379,node3:6379",
      "section_id": "cluster-mode"
    },
    {
      "id": "observability-ex0",
      "language": "bash",
      "code": "pip install fastapi-redis-sdk[otel]",
      "section_id": "observability"
    },
    {
      "id": "observability-ex1",
      "language": "python",
      "code": "FastAPIRedis(app).lifespan().caching().otel()",
      "section_id": "observability"
    }
  ]
}
