{"id":14979073,"url":"https://github.com/steinitzu/fastapi-etag","last_synced_at":"2025-10-28T14:30:44.815Z","repository":{"id":40290553,"uuid":"230986974","full_name":"steinitzu/fastapi-etag","owner":"steinitzu","description":"Convenience library for working with etags in fastapi","archived":false,"fork":false,"pushed_at":"2022-05-16T20:12:08.000Z","size":88,"stargazers_count":26,"open_issues_count":5,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-02-01T15:21:53.188Z","etag":null,"topics":["asyncio","caching","etag","fastapi","http-cache"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/steinitzu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-12-30T22:16:06.000Z","updated_at":"2024-10-29T09:25:50.000Z","dependencies_parsed_at":"2022-08-09T16:39:38.024Z","dependency_job_id":null,"html_url":"https://github.com/steinitzu/fastapi-etag","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steinitzu%2Ffastapi-etag","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steinitzu%2Ffastapi-etag/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steinitzu%2Ffastapi-etag/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steinitzu%2Ffastapi-etag/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/steinitzu","download_url":"https://codeload.github.com/steinitzu/fastapi-etag/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238662673,"owners_count":19509645,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asyncio","caching","etag","fastapi","http-cache"],"created_at":"2024-09-24T13:59:11.155Z","updated_at":"2025-10-28T14:30:44.478Z","avatar_url":"https://github.com/steinitzu.png","language":"Python","readme":"# fastapi-etag\n\n## Quickstart\n\nBasic etag support for FastAPI, allowing you to benefit from conditional caching in web browsers and reverse-proxy caching layers.\n\nThis does not generate etags that are a hash of the response content, but instead lets you pass in a custom etag generating function per endpoint that is called before executing the route function.  \nThis lets you bypass expensive API calls when client includes a matching etag in the `If-None-Match` header, in this case your endpoint is never called, instead returning a 304 response telling the client nothing has changed.\n\nThe etag logis is implemented with a fastapi dependency that you can add to your routes or entire routers.\n\nHere's how you use it:\n\n```python3\n# app.py\n\nfrom fastapi import FastAPI\nfrom starlette.requests import Request\nfrom fastapi_etag import Etag, add_exception_handler\n\napp = FastAPI()\nadd_exception_handler(app)\n\n\nasync def get_hello_etag(request: Request):\n    return \"etagfor\" + request.path_params[\"name\"]\n\n\n@app.get(\"/hello/{name}\", dependencies=[Depends(Etag(get_hello_etag))])\nasync def hello(name: str):\n    return {\"hello\": name}\n\n```\n\nRun this example with `uvicorn: uvicorn --port 8090 app:app`\n\nLet's break it down:\n\n```python3\nadd_exception_handler(app)\n```\n\nThe dependency raises a special `CacheHit` exception to exit early when there's a an etag match, this adds a standard exception handler to the app to generate a correct 304 response from the exception.\n\n```python3\nasync def get_hello_etag(request: Request):\n    name = request.path_params.get(\"name\")\n    return f\"etagfor{name}\"\n```\n\nThis is the function that generates the etag for your endpoint.  \nIt can do anything you want, it could for example return a hash of a last modified timestamp in your database.  \nIt can be either a normal function or an async function.  \nOnly requirement is that it accepts one argument (request) and that it returns either a string (the etag) or `None` (in which case no etag header is added)\n\n\n```python3\n@app.get(\"/hello/{name}\", dependencies=[Depends(Etag(get_hello_etag))])\ndef hello(name: str):\n\t...\n```\n\nThe `Etag` dependency is called like any fastapi dependency.\nIt always adds the etag returned by your etag gen function to the response.  \nIf client passes a matching etag in the `If-None-Match` header, it will raise a `CacheHit` exception which triggers a 304 response before calling your endpoint.\n\n\nNow try it with curl:\n\n```\ncurl -i \"http://localhost:8090/hello/bob\"\nHTTP/1.1 200 OK\ndate: Mon, 30 Dec 2019 21:55:43 GMT\nserver: uvicorn\ncontent-length: 15\ncontent-type: application/json\netag: W/\"etagforbob\"\n\n{\"hello\":\"bob\"}\n```\n\nEtag header is added\n\nNow including the etag in `If-None-Match` header (mimicking a web browser):\n\n```\ncurl -i -X GET \"http://localhost:8090/hello/bob\" -H \"If-None-Match: W/\\\"etagforbob\\\"\"\nHTTP/1.1 304 Not Modified\ndate: Mon, 30 Dec 2019 21:57:37 GMT\nserver: uvicorn\netag: W/\"etagforbob\"\n```\n\nIt now returns no content, only the 304 telling us nothing has changed.\n\n### Add response headers\n\nIf you want to add some extra response headers to the 304 and regular response,\nyou can add the `extra_headers` argument with a dict of headers:\n\n```\n@app.get(\n    \"/hello/{name}\",\n    dependencies=[\n        Depends(\n            Etag(\n                get_hello_etag,\n                extra_headers={\"Cache-Control\": \"public, max-age: 30\"},\n            )\n        )\n    ],\n)\ndef hello(name: str):\n\t...\n```\n\nThis will add the `cache-control` header on all responses from the endpoint.\n\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md)\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsteinitzu%2Ffastapi-etag","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsteinitzu%2Ffastapi-etag","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsteinitzu%2Ffastapi-etag/lists"}