fastapi-cachekit 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastapi_cachekit-0.1.0/PKG-INFO +289 -0
- fastapi_cachekit-0.1.0/README.md +246 -0
- fastapi_cachekit-0.1.0/fast_cache/__init__.py +13 -0
- fastapi_cachekit-0.1.0/fast_cache/backends/__init__.py +0 -0
- fastapi_cachekit-0.1.0/fast_cache/backends/backend.py +126 -0
- fastapi_cachekit-0.1.0/fast_cache/backends/memcached.py +126 -0
- fastapi_cachekit-0.1.0/fast_cache/backends/memory.py +291 -0
- fastapi_cachekit-0.1.0/fast_cache/backends/postgres.py +230 -0
- fastapi_cachekit-0.1.0/fast_cache/backends/redis.py +257 -0
- fastapi_cachekit-0.1.0/fast_cache/integration.py +199 -0
- fastapi_cachekit-0.1.0/fastapi_cachekit.egg-info/PKG-INFO +289 -0
- fastapi_cachekit-0.1.0/fastapi_cachekit.egg-info/SOURCES.txt +15 -0
- fastapi_cachekit-0.1.0/fastapi_cachekit.egg-info/dependency_links.txt +1 -0
- fastapi_cachekit-0.1.0/fastapi_cachekit.egg-info/requires.txt +18 -0
- fastapi_cachekit-0.1.0/fastapi_cachekit.egg-info/top_level.txt +1 -0
- fastapi_cachekit-0.1.0/pyproject.toml +83 -0
- fastapi_cachekit-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fastapi-cachekit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: High-performance caching solution for FastAPI applications
|
|
5
|
+
Author-email: Bijay Nayak <bijay6779@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/devbijay/fast-cache
|
|
8
|
+
Project-URL: Documentation, https://github.com/devbijay/fast-cache#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/devbijay/fast-cache.git
|
|
10
|
+
Project-URL: Issues, https://github.com/devbijay/fast-cache/issues
|
|
11
|
+
Keywords: fastapi,cache,redis,async,python,starlette,asyncio
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Framework :: FastAPI
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
23
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
25
|
+
Classifier: Topic :: Utilities
|
|
26
|
+
Classifier: Typing :: Typed
|
|
27
|
+
Requires-Python: >=3.9
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
Requires-Dist: fastapi>=0.75.0
|
|
30
|
+
Requires-Dist: uvicorn[standard]>=0.20.0
|
|
31
|
+
Provides-Extra: redis
|
|
32
|
+
Requires-Dist: redis>=4.2.0; extra == "redis"
|
|
33
|
+
Provides-Extra: postgres
|
|
34
|
+
Requires-Dist: psycopg[pool]>=3.2.9; extra == "postgres"
|
|
35
|
+
Provides-Extra: memcached
|
|
36
|
+
Requires-Dist: aiomcache>=0.8.1; extra == "memcached"
|
|
37
|
+
Requires-Dist: pymemcache>=4.0.0; extra == "memcached"
|
|
38
|
+
Provides-Extra: all
|
|
39
|
+
Requires-Dist: redis>=4.2.0; extra == "all"
|
|
40
|
+
Requires-Dist: psycopg[pool]>=3.2.9; extra == "all"
|
|
41
|
+
Requires-Dist: aiomcache>=0.8.1; extra == "all"
|
|
42
|
+
Requires-Dist: pymemcache>=4.0.0; extra == "all"
|
|
43
|
+
|
|
44
|
+
# fastapi-cachekit
|
|
45
|
+
|
|
46
|
+
A high-performance, flexible caching solution for FastAPI applications. fastapi-cachekit supports both synchronous and asynchronous operations with a clean API and multiple backend options.
|
|
47
|
+
|
|
48
|
+
[](https://badge.fury.io/py/fastapi-cachekit)
|
|
49
|
+
[](https://www.python.org/downloads/release/python-380/)
|
|
50
|
+
[](https://pepy.tech/project/fastapi-cachekit)
|
|
51
|
+
|
|
52
|
+
[](https://opensource.org/licenses/MIT)
|
|
53
|
+
|
|
54
|
+
## Features
|
|
55
|
+
|
|
56
|
+
- ✅ Full async/sync support for all operations
|
|
57
|
+
- ✅ Redis backend with connection pooling
|
|
58
|
+
- ✅ Function result caching with decorator syntax
|
|
59
|
+
- ✅ FastAPI dependency injection support
|
|
60
|
+
- ✅ Namespace support for isolating cache entries
|
|
61
|
+
- ✅ Customizable key generation
|
|
62
|
+
- ✅ Type hinting throughout the codebase
|
|
63
|
+
- ✅ Expiration time support (seconds or timedelta)
|
|
64
|
+
|
|
65
|
+
## 📦 Backends & Sync/Async Support
|
|
66
|
+
|
|
67
|
+
| Backend | Sync API | Async API | Install Extra |
|
|
68
|
+
|--------------------|:--------:|:---------:|----------------------|
|
|
69
|
+
| `InMemoryBackend` | ✅ | ✅ | _built-in_ |
|
|
70
|
+
| `RedisBackend` | ✅ | ✅ | `redis` |
|
|
71
|
+
| `PostgresBackend` | ✅ | ✅ | `postgres` |
|
|
72
|
+
| `MemcachedBackend` | ✅ | ✅ | `memcached` |
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 🛠️ Installation
|
|
77
|
+
|
|
78
|
+
**Base (in-memory only):**
|
|
79
|
+
```bash
|
|
80
|
+
pip install fastapi-cachekit
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**With Redis:**
|
|
84
|
+
```bash
|
|
85
|
+
pip install fastapi-cachekit[redis]
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**With Postgres:**
|
|
89
|
+
```bash
|
|
90
|
+
pip install fastapi-cachekit[postgres]
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**With Memcached:**
|
|
94
|
+
```bash
|
|
95
|
+
pip install fastapi-cachekit[memcached]
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**All backends:**
|
|
99
|
+
```bash
|
|
100
|
+
pip install fastapi-cachekit[all]
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
## Quick Start
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from fastapi import FastAPI, Depends
|
|
108
|
+
from fast_cache import cache, RedisBackend
|
|
109
|
+
from typing import Annotated
|
|
110
|
+
|
|
111
|
+
app = FastAPI()
|
|
112
|
+
|
|
113
|
+
# Initialize cache with Redis backend
|
|
114
|
+
cache.init_app(
|
|
115
|
+
app=app,
|
|
116
|
+
backend=RedisBackend(redis_url="redis://localhost:6379/0", namespace="myapp"),
|
|
117
|
+
default_expire=300 # 5 minutes default expiration
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# Use function caching decorator
|
|
122
|
+
@app.get("/items/{item_id}")
|
|
123
|
+
@cache.cached(expire=60) # Cache for 60 seconds
|
|
124
|
+
async def read_item(item_id: int):
|
|
125
|
+
# Expensive operation simulation
|
|
126
|
+
return {"item_id": item_id, "name": f"Item {item_id}"}
|
|
127
|
+
|
|
128
|
+
# Use cache backend directly with dependency injection
|
|
129
|
+
@app.get("/manual-cache")
|
|
130
|
+
async def manual_cache_example(cache_backend: Annotated[RedisBackend, Depends(cache.get_cache)]):
|
|
131
|
+
# Check if key exists
|
|
132
|
+
has_key = await cache_backend.ahas("my-key")
|
|
133
|
+
|
|
134
|
+
if not has_key:
|
|
135
|
+
# Set a value in the cache
|
|
136
|
+
await cache_backend.aset("my-key", {"data": "cached value"}, expire=30)
|
|
137
|
+
return {"cache_set": True}
|
|
138
|
+
|
|
139
|
+
# Get the value from cache
|
|
140
|
+
value = await cache_backend.aget("my-key")
|
|
141
|
+
return {"cached_value": value}
|
|
142
|
+
```
|
|
143
|
+
Now You Can use the cache from other Sub Routes by importing from
|
|
144
|
+
```from fast_cache import cache```
|
|
145
|
+
|
|
146
|
+
## Detailed Usage
|
|
147
|
+
|
|
148
|
+
### Initializing the Cache
|
|
149
|
+
|
|
150
|
+
Before using the cache, you need to initialize it with a backend:
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from fastapi import FastAPI
|
|
154
|
+
from fast_cache import cache, RedisBackend
|
|
155
|
+
from datetime import timedelta
|
|
156
|
+
|
|
157
|
+
app = FastAPI()
|
|
158
|
+
|
|
159
|
+
cache.init_app(
|
|
160
|
+
app=app,
|
|
161
|
+
backend=RedisBackend(
|
|
162
|
+
redis_url="redis://localhost:6379/0",
|
|
163
|
+
namespace="myapp",
|
|
164
|
+
max_connections=20
|
|
165
|
+
),
|
|
166
|
+
default_expire=timedelta(minutes=5)
|
|
167
|
+
)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Method1: Caching a Function Result Using A Cache Decorator
|
|
171
|
+
|
|
172
|
+
The `@cache.cached()` decorator is the simplest way to cache function results:
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
from fast_cache import cache
|
|
176
|
+
|
|
177
|
+
# Cache with default expiration time
|
|
178
|
+
@cache.cached()
|
|
179
|
+
def get_user_data(user_id: int):
|
|
180
|
+
# Expensive database query
|
|
181
|
+
return {"user_id": user_id, "name": "John Doe"}
|
|
182
|
+
|
|
183
|
+
# Cache with custom namespace and expiration
|
|
184
|
+
@cache.cached(namespace="users", expire=300)
|
|
185
|
+
async def get_user_profile(user_id: int):
|
|
186
|
+
# Async expensive operation
|
|
187
|
+
return {"user_id": user_id, "profile": "..."}
|
|
188
|
+
|
|
189
|
+
# Cache with custom key builder
|
|
190
|
+
@cache.cached(key_builder=lambda user_id, **kwargs: f"user:{user_id}")
|
|
191
|
+
def get_user_permissions(user_id: int):
|
|
192
|
+
# Complex permission calculation
|
|
193
|
+
return ["read", "write"]
|
|
194
|
+
|
|
195
|
+
# Skip Cache for Specific Calls
|
|
196
|
+
|
|
197
|
+
Sometimes you need to bypass the cache for certain requests:
|
|
198
|
+
|
|
199
|
+
@cache.cached()
|
|
200
|
+
async def get_weather(city: str, skip_cache: bool = False):
|
|
201
|
+
# Function will be called directly if skip_cache is True
|
|
202
|
+
return await fetch_weather_data(city)
|
|
203
|
+
|
|
204
|
+
# Usage:
|
|
205
|
+
weather = await get_weather("New York", skip_cache=True) # Bypasses cache
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
### Method 2: Using Via Dependency Injection
|
|
210
|
+
|
|
211
|
+
You can access the cache backend directly for more control:
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
from fastapi import Depends
|
|
215
|
+
from fast_cache import cache, CacheBackend
|
|
216
|
+
from typing import Annotated
|
|
217
|
+
|
|
218
|
+
@app.get("/api/data")
|
|
219
|
+
async def get_data(cache_backend: Annotated[CacheBackend, Depends(cache.get_cache)]):
|
|
220
|
+
# Try to get from cache
|
|
221
|
+
cached_data = await cache_backend.aget("api:data")
|
|
222
|
+
if cached_data:
|
|
223
|
+
return cached_data
|
|
224
|
+
|
|
225
|
+
# Generate new data
|
|
226
|
+
data = await fetch_expensive_api_data()
|
|
227
|
+
|
|
228
|
+
# Store in cache for 1 hour
|
|
229
|
+
await cache_backend.aset("api:data", data, expire=3600)
|
|
230
|
+
|
|
231
|
+
return data
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
### Advanced: Implementing Custom Backends
|
|
237
|
+
|
|
238
|
+
You can create your own cache backend by implementing the `CacheBackend` abstract class:
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
from fast_cache.backends.backend import CacheBackend
|
|
242
|
+
from typing import Any, Optional, Union
|
|
243
|
+
from datetime import timedelta
|
|
244
|
+
|
|
245
|
+
class MyCustomBackend(CacheBackend):
|
|
246
|
+
# Implement all required methods
|
|
247
|
+
async def aget(self, key: str) -> Any:
|
|
248
|
+
# Your implementation here
|
|
249
|
+
...
|
|
250
|
+
|
|
251
|
+
def get(self, key: str) -> Any:
|
|
252
|
+
# Your implementation here
|
|
253
|
+
...
|
|
254
|
+
|
|
255
|
+
# ... implement all other required methods
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## API Reference
|
|
259
|
+
|
|
260
|
+
### Cache Instance
|
|
261
|
+
|
|
262
|
+
- `cache.init_app(app, backend, default_expire=None)` - Initialize cache with FastAPI app
|
|
263
|
+
- `cache.get_cache()` - Get cache backend instance (for dependency injection)
|
|
264
|
+
- `cache.cached(expire=None, key_builder=None, namespace=None)` - Caching decorator
|
|
265
|
+
|
|
266
|
+
### CacheBackend Interface
|
|
267
|
+
|
|
268
|
+
All backends implement these methods in both sync and async versions:
|
|
269
|
+
|
|
270
|
+
- `get(key)` / `aget(key)` - Retrieve a value
|
|
271
|
+
- `set(key, value, expire)` / `aset(key, value, expire)` - Store a value
|
|
272
|
+
- `delete(key)` / `adelete(key)` - Delete a value
|
|
273
|
+
- `clear()` / `aclear()` - Clear all values
|
|
274
|
+
- `has(key)` / `ahas(key)` - Check if key exists
|
|
275
|
+
|
|
276
|
+
### RedisBackend Configuration
|
|
277
|
+
|
|
278
|
+
- `redis_url` - Redis connection string (required)
|
|
279
|
+
- `namespace` - Key prefix (default: "fastapi-cache")
|
|
280
|
+
- `pool_size` - Minimum pool connections (default: 10)
|
|
281
|
+
- `max_connections` - Maximum pool connections (default: 20)
|
|
282
|
+
|
|
283
|
+
## Contributing
|
|
284
|
+
|
|
285
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
286
|
+
|
|
287
|
+
## License
|
|
288
|
+
|
|
289
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# fastapi-cachekit
|
|
2
|
+
|
|
3
|
+
A high-performance, flexible caching solution for FastAPI applications. fastapi-cachekit supports both synchronous and asynchronous operations with a clean API and multiple backend options.
|
|
4
|
+
|
|
5
|
+
[](https://badge.fury.io/py/fastapi-cachekit)
|
|
6
|
+
[](https://www.python.org/downloads/release/python-380/)
|
|
7
|
+
[](https://pepy.tech/project/fastapi-cachekit)
|
|
8
|
+
|
|
9
|
+
[](https://opensource.org/licenses/MIT)
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- ✅ Full async/sync support for all operations
|
|
14
|
+
- ✅ Redis backend with connection pooling
|
|
15
|
+
- ✅ Function result caching with decorator syntax
|
|
16
|
+
- ✅ FastAPI dependency injection support
|
|
17
|
+
- ✅ Namespace support for isolating cache entries
|
|
18
|
+
- ✅ Customizable key generation
|
|
19
|
+
- ✅ Type hinting throughout the codebase
|
|
20
|
+
- ✅ Expiration time support (seconds or timedelta)
|
|
21
|
+
|
|
22
|
+
## 📦 Backends & Sync/Async Support
|
|
23
|
+
|
|
24
|
+
| Backend | Sync API | Async API | Install Extra |
|
|
25
|
+
|--------------------|:--------:|:---------:|----------------------|
|
|
26
|
+
| `InMemoryBackend` | ✅ | ✅ | _built-in_ |
|
|
27
|
+
| `RedisBackend` | ✅ | ✅ | `redis` |
|
|
28
|
+
| `PostgresBackend` | ✅ | ✅ | `postgres` |
|
|
29
|
+
| `MemcachedBackend` | ✅ | ✅ | `memcached` |
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 🛠️ Installation
|
|
34
|
+
|
|
35
|
+
**Base (in-memory only):**
|
|
36
|
+
```bash
|
|
37
|
+
pip install fastapi-cachekit
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**With Redis:**
|
|
41
|
+
```bash
|
|
42
|
+
pip install fastapi-cachekit[redis]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**With Postgres:**
|
|
46
|
+
```bash
|
|
47
|
+
pip install fastapi-cachekit[postgres]
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**With Memcached:**
|
|
51
|
+
```bash
|
|
52
|
+
pip install fastapi-cachekit[memcached]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**All backends:**
|
|
56
|
+
```bash
|
|
57
|
+
pip install fastapi-cachekit[all]
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
## Quick Start
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from fastapi import FastAPI, Depends
|
|
65
|
+
from fast_cache import cache, RedisBackend
|
|
66
|
+
from typing import Annotated
|
|
67
|
+
|
|
68
|
+
app = FastAPI()
|
|
69
|
+
|
|
70
|
+
# Initialize cache with Redis backend
|
|
71
|
+
cache.init_app(
|
|
72
|
+
app=app,
|
|
73
|
+
backend=RedisBackend(redis_url="redis://localhost:6379/0", namespace="myapp"),
|
|
74
|
+
default_expire=300 # 5 minutes default expiration
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# Use function caching decorator
|
|
79
|
+
@app.get("/items/{item_id}")
|
|
80
|
+
@cache.cached(expire=60) # Cache for 60 seconds
|
|
81
|
+
async def read_item(item_id: int):
|
|
82
|
+
# Expensive operation simulation
|
|
83
|
+
return {"item_id": item_id, "name": f"Item {item_id}"}
|
|
84
|
+
|
|
85
|
+
# Use cache backend directly with dependency injection
|
|
86
|
+
@app.get("/manual-cache")
|
|
87
|
+
async def manual_cache_example(cache_backend: Annotated[RedisBackend, Depends(cache.get_cache)]):
|
|
88
|
+
# Check if key exists
|
|
89
|
+
has_key = await cache_backend.ahas("my-key")
|
|
90
|
+
|
|
91
|
+
if not has_key:
|
|
92
|
+
# Set a value in the cache
|
|
93
|
+
await cache_backend.aset("my-key", {"data": "cached value"}, expire=30)
|
|
94
|
+
return {"cache_set": True}
|
|
95
|
+
|
|
96
|
+
# Get the value from cache
|
|
97
|
+
value = await cache_backend.aget("my-key")
|
|
98
|
+
return {"cached_value": value}
|
|
99
|
+
```
|
|
100
|
+
Now You Can use the cache from other Sub Routes by importing from
|
|
101
|
+
```from fast_cache import cache```
|
|
102
|
+
|
|
103
|
+
## Detailed Usage
|
|
104
|
+
|
|
105
|
+
### Initializing the Cache
|
|
106
|
+
|
|
107
|
+
Before using the cache, you need to initialize it with a backend:
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
from fastapi import FastAPI
|
|
111
|
+
from fast_cache import cache, RedisBackend
|
|
112
|
+
from datetime import timedelta
|
|
113
|
+
|
|
114
|
+
app = FastAPI()
|
|
115
|
+
|
|
116
|
+
cache.init_app(
|
|
117
|
+
app=app,
|
|
118
|
+
backend=RedisBackend(
|
|
119
|
+
redis_url="redis://localhost:6379/0",
|
|
120
|
+
namespace="myapp",
|
|
121
|
+
max_connections=20
|
|
122
|
+
),
|
|
123
|
+
default_expire=timedelta(minutes=5)
|
|
124
|
+
)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Method1: Caching a Function Result Using A Cache Decorator
|
|
128
|
+
|
|
129
|
+
The `@cache.cached()` decorator is the simplest way to cache function results:
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from fast_cache import cache
|
|
133
|
+
|
|
134
|
+
# Cache with default expiration time
|
|
135
|
+
@cache.cached()
|
|
136
|
+
def get_user_data(user_id: int):
|
|
137
|
+
# Expensive database query
|
|
138
|
+
return {"user_id": user_id, "name": "John Doe"}
|
|
139
|
+
|
|
140
|
+
# Cache with custom namespace and expiration
|
|
141
|
+
@cache.cached(namespace="users", expire=300)
|
|
142
|
+
async def get_user_profile(user_id: int):
|
|
143
|
+
# Async expensive operation
|
|
144
|
+
return {"user_id": user_id, "profile": "..."}
|
|
145
|
+
|
|
146
|
+
# Cache with custom key builder
|
|
147
|
+
@cache.cached(key_builder=lambda user_id, **kwargs: f"user:{user_id}")
|
|
148
|
+
def get_user_permissions(user_id: int):
|
|
149
|
+
# Complex permission calculation
|
|
150
|
+
return ["read", "write"]
|
|
151
|
+
|
|
152
|
+
# Skip Cache for Specific Calls
|
|
153
|
+
|
|
154
|
+
Sometimes you need to bypass the cache for certain requests:
|
|
155
|
+
|
|
156
|
+
@cache.cached()
|
|
157
|
+
async def get_weather(city: str, skip_cache: bool = False):
|
|
158
|
+
# Function will be called directly if skip_cache is True
|
|
159
|
+
return await fetch_weather_data(city)
|
|
160
|
+
|
|
161
|
+
# Usage:
|
|
162
|
+
weather = await get_weather("New York", skip_cache=True) # Bypasses cache
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
### Method 2: Using Via Dependency Injection
|
|
167
|
+
|
|
168
|
+
You can access the cache backend directly for more control:
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
from fastapi import Depends
|
|
172
|
+
from fast_cache import cache, CacheBackend
|
|
173
|
+
from typing import Annotated
|
|
174
|
+
|
|
175
|
+
@app.get("/api/data")
|
|
176
|
+
async def get_data(cache_backend: Annotated[CacheBackend, Depends(cache.get_cache)]):
|
|
177
|
+
# Try to get from cache
|
|
178
|
+
cached_data = await cache_backend.aget("api:data")
|
|
179
|
+
if cached_data:
|
|
180
|
+
return cached_data
|
|
181
|
+
|
|
182
|
+
# Generate new data
|
|
183
|
+
data = await fetch_expensive_api_data()
|
|
184
|
+
|
|
185
|
+
# Store in cache for 1 hour
|
|
186
|
+
await cache_backend.aset("api:data", data, expire=3600)
|
|
187
|
+
|
|
188
|
+
return data
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
### Advanced: Implementing Custom Backends
|
|
194
|
+
|
|
195
|
+
You can create your own cache backend by implementing the `CacheBackend` abstract class:
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
from fast_cache.backends.backend import CacheBackend
|
|
199
|
+
from typing import Any, Optional, Union
|
|
200
|
+
from datetime import timedelta
|
|
201
|
+
|
|
202
|
+
class MyCustomBackend(CacheBackend):
|
|
203
|
+
# Implement all required methods
|
|
204
|
+
async def aget(self, key: str) -> Any:
|
|
205
|
+
# Your implementation here
|
|
206
|
+
...
|
|
207
|
+
|
|
208
|
+
def get(self, key: str) -> Any:
|
|
209
|
+
# Your implementation here
|
|
210
|
+
...
|
|
211
|
+
|
|
212
|
+
# ... implement all other required methods
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## API Reference
|
|
216
|
+
|
|
217
|
+
### Cache Instance
|
|
218
|
+
|
|
219
|
+
- `cache.init_app(app, backend, default_expire=None)` - Initialize cache with FastAPI app
|
|
220
|
+
- `cache.get_cache()` - Get cache backend instance (for dependency injection)
|
|
221
|
+
- `cache.cached(expire=None, key_builder=None, namespace=None)` - Caching decorator
|
|
222
|
+
|
|
223
|
+
### CacheBackend Interface
|
|
224
|
+
|
|
225
|
+
All backends implement these methods in both sync and async versions:
|
|
226
|
+
|
|
227
|
+
- `get(key)` / `aget(key)` - Retrieve a value
|
|
228
|
+
- `set(key, value, expire)` / `aset(key, value, expire)` - Store a value
|
|
229
|
+
- `delete(key)` / `adelete(key)` - Delete a value
|
|
230
|
+
- `clear()` / `aclear()` - Clear all values
|
|
231
|
+
- `has(key)` / `ahas(key)` - Check if key exists
|
|
232
|
+
|
|
233
|
+
### RedisBackend Configuration
|
|
234
|
+
|
|
235
|
+
- `redis_url` - Redis connection string (required)
|
|
236
|
+
- `namespace` - Key prefix (default: "fastapi-cache")
|
|
237
|
+
- `pool_size` - Minimum pool connections (default: 10)
|
|
238
|
+
- `max_connections` - Maximum pool connections (default: 20)
|
|
239
|
+
|
|
240
|
+
## Contributing
|
|
241
|
+
|
|
242
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
243
|
+
|
|
244
|
+
## License
|
|
245
|
+
|
|
246
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .integration import FastAPICache
|
|
2
|
+
from .backends.backend import CacheBackend
|
|
3
|
+
|
|
4
|
+
from .backends.redis import RedisBackend
|
|
5
|
+
from .backends.memory import InMemoryBackend
|
|
6
|
+
from .backends.postgres import PostgresBackend
|
|
7
|
+
from .backends.memcached import MemcachedBackend
|
|
8
|
+
|
|
9
|
+
__all__ = ["FastAPICache", "RedisBackend", "CacheBackend", "InMemoryBackend","PostgresBackend", "cache","MemcachedBackend" ]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Create global cache instance
|
|
13
|
+
cache = FastAPICache()
|
|
File without changes
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Any, Optional, Union
|
|
3
|
+
from datetime import timedelta
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CacheBackend(ABC):
|
|
7
|
+
"""
|
|
8
|
+
Abstract base class for cache backends.
|
|
9
|
+
|
|
10
|
+
All cache backend implementations must inherit from this class and implement
|
|
11
|
+
both synchronous and asynchronous methods for cache operations.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
@abstractmethod
|
|
15
|
+
async def aget(self, key: str) -> Optional[Any]:
|
|
16
|
+
"""
|
|
17
|
+
Asynchronously retrieve a value from the cache.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
key (str): The key to retrieve.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Optional[Any]: The cached value, or None if not found.
|
|
24
|
+
"""
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
@abstractmethod
|
|
28
|
+
def get(self, key: str) -> Optional[Any]:
|
|
29
|
+
"""
|
|
30
|
+
Synchronously retrieve a value from the cache.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
key (str): The key to retrieve.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Optional[Any]: The cached value, or None if not found.
|
|
37
|
+
"""
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
@abstractmethod
|
|
41
|
+
async def aset(
|
|
42
|
+
self, key: str, value: Any, expire: Optional[Union[int, timedelta]] = None
|
|
43
|
+
) -> None:
|
|
44
|
+
"""
|
|
45
|
+
Asynchronously set a value in the cache.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
key (str): The key under which to store the value.
|
|
49
|
+
value (Any): The value to store.
|
|
50
|
+
expire (Optional[Union[int, timedelta]]): Expiration time in seconds or as timedelta.
|
|
51
|
+
"""
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
@abstractmethod
|
|
55
|
+
def set(
|
|
56
|
+
self, key: str, value: Any, expire: Optional[Union[int, timedelta]] = None
|
|
57
|
+
) -> None:
|
|
58
|
+
"""
|
|
59
|
+
Synchronously set a value in the cache.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
key (str): The key under which to store the value.
|
|
63
|
+
value (Any): The value to store.
|
|
64
|
+
expire (Optional[Union[int, timedelta]]): Expiration time in seconds or as timedelta.
|
|
65
|
+
"""
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
@abstractmethod
|
|
69
|
+
async def adelete(self, key: str) -> None:
|
|
70
|
+
"""
|
|
71
|
+
Asynchronously delete a value from the cache.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
key (str): The key to delete.
|
|
75
|
+
"""
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
@abstractmethod
|
|
79
|
+
def delete(self, key: str) -> None:
|
|
80
|
+
"""
|
|
81
|
+
Synchronously delete a value from the cache.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
key (str): The key to delete.
|
|
85
|
+
"""
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
@abstractmethod
|
|
89
|
+
async def aclear(self) -> None:
|
|
90
|
+
"""
|
|
91
|
+
Asynchronously clear all values from the cache.
|
|
92
|
+
"""
|
|
93
|
+
pass
|
|
94
|
+
|
|
95
|
+
@abstractmethod
|
|
96
|
+
def clear(self) -> None:
|
|
97
|
+
"""
|
|
98
|
+
Synchronously clear all values from the cache.
|
|
99
|
+
"""
|
|
100
|
+
pass
|
|
101
|
+
|
|
102
|
+
@abstractmethod
|
|
103
|
+
async def ahas(self, key: str) -> bool:
|
|
104
|
+
"""
|
|
105
|
+
Asynchronously check if a key exists in the cache.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
key (str): The key to check.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
bool: True if the key exists, False otherwise.
|
|
112
|
+
"""
|
|
113
|
+
pass
|
|
114
|
+
|
|
115
|
+
@abstractmethod
|
|
116
|
+
def has(self, key: str) -> bool:
|
|
117
|
+
"""
|
|
118
|
+
Synchronously check if a key exists in the cache.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
key (str): The key to check.
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
bool: True if the key exists, False otherwise.
|
|
125
|
+
"""
|
|
126
|
+
pass
|