fast-cache-middleware 0.0.3__tar.gz → 0.0.5__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Vadim Vakilov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,344 @@
1
+ Metadata-Version: 2.3
2
+ Name: fast-cache-middleware
3
+ Version: 0.0.5
4
+ Summary: An intelligent middleware for caching FastAPI responses
5
+ License: MIT
6
+ Author: www.chud0@gmail.com
7
+ Requires-Python: >=3.11,<4.0
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Requires-Dist: fastapi (>=0.111.1,<1.0.0)
14
+ Requires-Dist: pydantic (>=1.7.4,!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0)
15
+ Description-Content-Type: text/markdown
16
+
17
+ # FastCacheMiddleware
18
+
19
+ 🚀 **High-performance ASGI middleware for caching with route resolution approach**
20
+
21
+ ## ✨ Key Features
22
+
23
+ FastCacheMiddleware uses a **route resolution approach** - it analyzes application routes at startup and extracts cache configurations from FastAPI dependencies.
24
+
25
+ ### 🔧 How it works
26
+
27
+ 1. **At application startup:**
28
+ - Middleware analyzes all routes and their dependencies
29
+ - Extracts `CacheConfig` and `CacheDropConfig` from dependencies
30
+ - Creates internal route index with caching configurations
31
+
32
+ 2. **During request processing:**
33
+ - Checks HTTP method (cache only GET, invalidate for POST/PUT/DELETE)
34
+ - Finds matching route by path and method
35
+ - Extracts cache configuration from pre-analyzed dependencies
36
+ - Performs caching or invalidation according to configuration
37
+
38
+ ### 💡 Benefits
39
+
40
+ - **⚡ High performance** - pre-route analysis
41
+ - **🎯 Easy integration** - standard FastAPI dependencies
42
+ - **🔧 Flexible configuration** - custom key functions, route-level TTL
43
+ - **🛡️ Automatic invalidation** - cache invalidation for modifying requests
44
+ - **📊 Minimal overhead** - efficient handling of large numbers of routes
45
+
46
+ ## 📦 Installation
47
+
48
+ ```bash
49
+ pip install fast-cache-middleware
50
+ ```
51
+
52
+ ## 🎯 Quick Start
53
+
54
+ ```python
55
+ import uvicorn
56
+ from fastapi import FastAPI
57
+
58
+ from fast_cache_middleware import CacheConfig, CacheDropConfig, FastCacheMiddleware
59
+
60
+ app = FastAPI()
61
+
62
+ # Add middleware - it will automatically analyze routes
63
+ app.add_middleware(FastCacheMiddleware)
64
+
65
+
66
+ # Routes with caching
67
+ @app.get("/users/{user_id}", dependencies=[CacheConfig(max_age=300)])
68
+ async def get_user(user_id: int) -> dict[str, int | str]:
69
+ """This endpoint is cached for 5 minutes."""
70
+ # Simulate database load
71
+ return {"user_id": user_id, "name": f"User {user_id}"}
72
+
73
+
74
+ # Routes with cache invalidation
75
+ @app.post(
76
+ "/users/{user_id}",
77
+ dependencies=[CacheDropConfig(paths=["/users/*", "/api/users/*"])],
78
+ )
79
+ async def update_user(user_id: int) -> dict[str, int | str]:
80
+ """It will invalidate cache for all /users/* paths."""
81
+ return {"user_id": user_id, "status": "updated"}
82
+
83
+
84
+ if __name__ == "__main__":
85
+ uvicorn.run(app, host="127.0.0.1", port=8000)
86
+ ```
87
+
88
+ ## 🔧 Configuration
89
+
90
+ ### CacheConfig
91
+
92
+ Configure caching for GET requests:
93
+
94
+ ```python
95
+ from fast_cache_middleware import CacheConfig
96
+
97
+ # Simple caching
98
+ CacheConfig(max_age=300) # 5 minutes
99
+
100
+ # With custom key function, for personalized cache
101
+ def key_func(request: Request):
102
+ user_id = request.headers.get("Authorization", "anonymous")
103
+ path = request.url.path
104
+ query = str(request.query_params)
105
+ return f"{path}:{user_id}:{query}"
106
+
107
+ CacheConfig(max_age=600, key_func=key_func) # 10 minutes
108
+ ```
109
+
110
+ ### CacheDropConfig
111
+
112
+ Configure cache invalidation for modifying requests:
113
+
114
+ ```python
115
+ # Paths can be matched by startswith
116
+ CacheDropConfig(
117
+ paths=[
118
+ "/users/", # Will match /users/123, /users/profile, etc.
119
+ "/api/", # Will match all API paths
120
+ ]
121
+ )
122
+
123
+ # Paths can be matched by regexp
124
+ CacheDropConfig(
125
+ paths=[
126
+ r"^/users/\d+$", # Will match /users/123, /users/456, etc.
127
+ r"^/api/.*", # Will match all API paths
128
+ ]
129
+ )
130
+
131
+ # You can mix regexp and simple string matching - use what's more convenient
132
+ CacheDropConfig(
133
+ paths=[
134
+ "/users/", # Simple prefix match
135
+ r"^/api/\w+/\d+$" # Regexp for specific API endpoints
136
+ ]
137
+ )
138
+ ```
139
+
140
+ ## 🏗️ Architecture
141
+
142
+ ### System Components
143
+
144
+ ```
145
+ FastCacheMiddleware
146
+ ├── RouteInfo # Route information with cache configuration
147
+ ├── Controller # Caching logic and validation
148
+ ├── Storage # Storages (InMemory, Redis, etc.)
149
+ ├── Serializers # Cached data serialization
150
+ └── Dependencies # FastAPI dependencies for configuration
151
+ ```
152
+
153
+ ### Request Processing Flow
154
+
155
+ ```mermaid
156
+ graph TD
157
+ A[HTTP Request] --> B{Route analysis done?}
158
+ B -->|No| C[Analyze application routes]
159
+ C --> D[Save route configurations]
160
+ B -->|Yes| E{Method supports caching?}
161
+ D --> E
162
+ E -->|No| F[Pass to application]
163
+ E -->|Yes| G[Find matching route]
164
+ G --> H{Route found?}
165
+ H -->|No| F
166
+ H -->|Yes| I{GET request + CacheConfig?}
167
+ I -->|Yes| J[Check cache]
168
+ J --> K{Cache found?}
169
+ K -->|Yes| L[Return from cache]
170
+ K -->|No| M[Execute request + save to cache]
171
+ I -->|No| N{POST/PUT/DELETE + CacheDropConfig?}
172
+ N -->|Yes| O[Invalidate cache]
173
+ N -->|No| F
174
+ O --> F
175
+ M --> P[Return response]
176
+ ```
177
+
178
+ ## 🎛️ Storages
179
+
180
+ ### InMemoryStorage (default)
181
+
182
+ ```python
183
+ from fast_cache_middleware import FastCacheMiddleware, InMemoryStorage
184
+
185
+ storage = InMemoryStorage(max_size=1000)
186
+ app.add_middleware(FastCacheMiddleware, storage=storage)
187
+ ```
188
+
189
+ InMemoryStorage uses batch cleanup for better performance
190
+
191
+ ### Custom Storage
192
+
193
+ ```python
194
+ from fast_cache_middleware import BaseStorage
195
+
196
+ class RedisStorage(BaseStorage):
197
+ def __init__(self, redis_url: str):
198
+ import redis
199
+ self.redis = redis.from_url(redis_url)
200
+
201
+ async def store(self, key: str, response, request, metadata):
202
+ # Implementation for saving to Redis
203
+ pass
204
+
205
+ async def retrieve(self, key: str):
206
+ # Implementation for retrieving from Redis
207
+ pass
208
+
209
+ app.add_middleware(FastCacheMiddleware, storage=RedisStorage("redis://localhost"))
210
+ ```
211
+
212
+ ## 🧪 Testing
213
+
214
+ ```python
215
+ import pytest
216
+ from httpx import AsyncClient
217
+ from examples.basic import app
218
+
219
+ @pytest.mark.asyncio
220
+ async def test_caching():
221
+ async with AsyncClient(app=app, base_url="http://test") as client:
222
+ # First request - cache miss
223
+ response1 = await client.get("/users/1")
224
+ assert response1.status_code == 200
225
+
226
+ # Second request - cache hit (should be faster)
227
+ response2 = await client.get("/users/1")
228
+ assert response2.status_code == 200
229
+ assert response1.json() == response2.json()
230
+
231
+ @pytest.mark.asyncio
232
+ async def test_cache_invalidation():
233
+ async with AsyncClient(app=app, base_url="http://test") as client:
234
+ # Cache data
235
+ await client.get("/users/1")
236
+
237
+ # Invalidate cache
238
+ await client.post("/users/1", json={})
239
+
240
+ # Next GET should execute new request
241
+ response = await client.get("/users/1")
242
+ assert response.status_code == 200
243
+ ```
244
+
245
+ ## 📊 Performance
246
+
247
+ ### Benchmarks
248
+
249
+ - **Route analysis**: ~5ms for 100 routes at startup
250
+ - **Route lookup**: ~0.1ms per request (O(n) by number of cached routes)
251
+ - **Cache hit**: ~1ms per request
252
+ - **Cache miss**: original request time + ~2ms for saving
253
+
254
+ ### Optimization
255
+
256
+ ```python
257
+ # For applications with many routes
258
+ app.add_middleware(
259
+ FastCacheMiddleware,
260
+ storage=InMemoryStorage(max_size=10000), # Increase cache size
261
+ controller=Controller(default_ttl=3600) # Increase default TTL
262
+ )
263
+ ```
264
+
265
+ ## 🔒 Security
266
+
267
+ ### Cache Isolation
268
+
269
+ ```python
270
+ def user_specific_cache() -> CacheConfig:
271
+ def secure_key_func(request):
272
+ # Include user token in key
273
+ token = request.headers.get("authorization", "").split(" ")[-1]
274
+ return f"{request.url.path}:token:{token}"
275
+
276
+ return CacheConfig(max_age=300, key_func=secure_key_func)
277
+
278
+ @app.get("/private/data", dependencies=[Depends(user_specific_cache)])
279
+ async def get_private_data():
280
+ return {"sensitive": "data"}
281
+ ```
282
+
283
+ ### Header Validation
284
+
285
+ Middleware automatically respects standard HTTP caching headers:
286
+
287
+ - `Cache-Control: no-cache` - skip cache
288
+ - `Cache-Control: no-store` - forbid caching
289
+ - `Cache-Control: private` - don't cache private responses
290
+
291
+
292
+ ## 🛠️ Advanced Usage
293
+
294
+ ### Custom Controller
295
+
296
+ ```python
297
+ from fast_cache_middleware import Controller
298
+
299
+ class CustomController(Controller):
300
+ async def is_cachable_request(self, request):
301
+ # Custom logic - don't cache admin requests
302
+ if request.headers.get("x-admin-request"):
303
+ return False
304
+ return await super().should_cache_request(request)
305
+
306
+ async def generate_cache_key(self, request):
307
+ # Add API version to key
308
+ version = request.headers.get("api-version", "v1")
309
+ base_key = await super().generate_cache_key(request)
310
+ return f"{version}:{base_key}"
311
+
312
+ app.add_middleware(
313
+ FastCacheMiddleware,
314
+ controller=CustomController()
315
+ )
316
+ ```
317
+
318
+ ## 📝 Examples
319
+
320
+ More examples in the `examples/` folder:
321
+
322
+ - **quick_start.py** - minimal example showing basic caching and invalidation
323
+ - **basic.py** - basic usage with FastAPI
324
+
325
+ ## 🤝 Contributing
326
+
327
+ ```bash
328
+ git clone https://github.com/chud0/FastCacheMiddleware
329
+ cd FastCacheMiddleware
330
+ poetry install --with dev
331
+ ./scripts/test.sh
332
+ ```
333
+
334
+ ## 📄 License
335
+
336
+ MIT License - see [LICENSE](LICENSE)
337
+
338
+ ---
339
+
340
+ ⭐ **Like the project? Give it a star!**
341
+
342
+ 🐛 **Found a bug?** [Create an issue](https://github.com/chud0/FastCacheMiddleware/issues)
343
+
344
+ 💡 **Have an idea?** [Suggest a feature](https://github.com/chud0/FastCacheMiddleware/discussions/categories/ideas)
@@ -0,0 +1,328 @@
1
+ # FastCacheMiddleware
2
+
3
+ 🚀 **High-performance ASGI middleware for caching with route resolution approach**
4
+
5
+ ## ✨ Key Features
6
+
7
+ FastCacheMiddleware uses a **route resolution approach** - it analyzes application routes at startup and extracts cache configurations from FastAPI dependencies.
8
+
9
+ ### 🔧 How it works
10
+
11
+ 1. **At application startup:**
12
+ - Middleware analyzes all routes and their dependencies
13
+ - Extracts `CacheConfig` and `CacheDropConfig` from dependencies
14
+ - Creates internal route index with caching configurations
15
+
16
+ 2. **During request processing:**
17
+ - Checks HTTP method (cache only GET, invalidate for POST/PUT/DELETE)
18
+ - Finds matching route by path and method
19
+ - Extracts cache configuration from pre-analyzed dependencies
20
+ - Performs caching or invalidation according to configuration
21
+
22
+ ### 💡 Benefits
23
+
24
+ - **⚡ High performance** - pre-route analysis
25
+ - **🎯 Easy integration** - standard FastAPI dependencies
26
+ - **🔧 Flexible configuration** - custom key functions, route-level TTL
27
+ - **🛡️ Automatic invalidation** - cache invalidation for modifying requests
28
+ - **📊 Minimal overhead** - efficient handling of large numbers of routes
29
+
30
+ ## 📦 Installation
31
+
32
+ ```bash
33
+ pip install fast-cache-middleware
34
+ ```
35
+
36
+ ## 🎯 Quick Start
37
+
38
+ ```python
39
+ import uvicorn
40
+ from fastapi import FastAPI
41
+
42
+ from fast_cache_middleware import CacheConfig, CacheDropConfig, FastCacheMiddleware
43
+
44
+ app = FastAPI()
45
+
46
+ # Add middleware - it will automatically analyze routes
47
+ app.add_middleware(FastCacheMiddleware)
48
+
49
+
50
+ # Routes with caching
51
+ @app.get("/users/{user_id}", dependencies=[CacheConfig(max_age=300)])
52
+ async def get_user(user_id: int) -> dict[str, int | str]:
53
+ """This endpoint is cached for 5 minutes."""
54
+ # Simulate database load
55
+ return {"user_id": user_id, "name": f"User {user_id}"}
56
+
57
+
58
+ # Routes with cache invalidation
59
+ @app.post(
60
+ "/users/{user_id}",
61
+ dependencies=[CacheDropConfig(paths=["/users/*", "/api/users/*"])],
62
+ )
63
+ async def update_user(user_id: int) -> dict[str, int | str]:
64
+ """It will invalidate cache for all /users/* paths."""
65
+ return {"user_id": user_id, "status": "updated"}
66
+
67
+
68
+ if __name__ == "__main__":
69
+ uvicorn.run(app, host="127.0.0.1", port=8000)
70
+ ```
71
+
72
+ ## 🔧 Configuration
73
+
74
+ ### CacheConfig
75
+
76
+ Configure caching for GET requests:
77
+
78
+ ```python
79
+ from fast_cache_middleware import CacheConfig
80
+
81
+ # Simple caching
82
+ CacheConfig(max_age=300) # 5 minutes
83
+
84
+ # With custom key function, for personalized cache
85
+ def key_func(request: Request):
86
+ user_id = request.headers.get("Authorization", "anonymous")
87
+ path = request.url.path
88
+ query = str(request.query_params)
89
+ return f"{path}:{user_id}:{query}"
90
+
91
+ CacheConfig(max_age=600, key_func=key_func) # 10 minutes
92
+ ```
93
+
94
+ ### CacheDropConfig
95
+
96
+ Configure cache invalidation for modifying requests:
97
+
98
+ ```python
99
+ # Paths can be matched by startswith
100
+ CacheDropConfig(
101
+ paths=[
102
+ "/users/", # Will match /users/123, /users/profile, etc.
103
+ "/api/", # Will match all API paths
104
+ ]
105
+ )
106
+
107
+ # Paths can be matched by regexp
108
+ CacheDropConfig(
109
+ paths=[
110
+ r"^/users/\d+$", # Will match /users/123, /users/456, etc.
111
+ r"^/api/.*", # Will match all API paths
112
+ ]
113
+ )
114
+
115
+ # You can mix regexp and simple string matching - use what's more convenient
116
+ CacheDropConfig(
117
+ paths=[
118
+ "/users/", # Simple prefix match
119
+ r"^/api/\w+/\d+$" # Regexp for specific API endpoints
120
+ ]
121
+ )
122
+ ```
123
+
124
+ ## 🏗️ Architecture
125
+
126
+ ### System Components
127
+
128
+ ```
129
+ FastCacheMiddleware
130
+ ├── RouteInfo # Route information with cache configuration
131
+ ├── Controller # Caching logic and validation
132
+ ├── Storage # Storages (InMemory, Redis, etc.)
133
+ ├── Serializers # Cached data serialization
134
+ └── Dependencies # FastAPI dependencies for configuration
135
+ ```
136
+
137
+ ### Request Processing Flow
138
+
139
+ ```mermaid
140
+ graph TD
141
+ A[HTTP Request] --> B{Route analysis done?}
142
+ B -->|No| C[Analyze application routes]
143
+ C --> D[Save route configurations]
144
+ B -->|Yes| E{Method supports caching?}
145
+ D --> E
146
+ E -->|No| F[Pass to application]
147
+ E -->|Yes| G[Find matching route]
148
+ G --> H{Route found?}
149
+ H -->|No| F
150
+ H -->|Yes| I{GET request + CacheConfig?}
151
+ I -->|Yes| J[Check cache]
152
+ J --> K{Cache found?}
153
+ K -->|Yes| L[Return from cache]
154
+ K -->|No| M[Execute request + save to cache]
155
+ I -->|No| N{POST/PUT/DELETE + CacheDropConfig?}
156
+ N -->|Yes| O[Invalidate cache]
157
+ N -->|No| F
158
+ O --> F
159
+ M --> P[Return response]
160
+ ```
161
+
162
+ ## 🎛️ Storages
163
+
164
+ ### InMemoryStorage (default)
165
+
166
+ ```python
167
+ from fast_cache_middleware import FastCacheMiddleware, InMemoryStorage
168
+
169
+ storage = InMemoryStorage(max_size=1000)
170
+ app.add_middleware(FastCacheMiddleware, storage=storage)
171
+ ```
172
+
173
+ InMemoryStorage uses batch cleanup for better performance
174
+
175
+ ### Custom Storage
176
+
177
+ ```python
178
+ from fast_cache_middleware import BaseStorage
179
+
180
+ class RedisStorage(BaseStorage):
181
+ def __init__(self, redis_url: str):
182
+ import redis
183
+ self.redis = redis.from_url(redis_url)
184
+
185
+ async def store(self, key: str, response, request, metadata):
186
+ # Implementation for saving to Redis
187
+ pass
188
+
189
+ async def retrieve(self, key: str):
190
+ # Implementation for retrieving from Redis
191
+ pass
192
+
193
+ app.add_middleware(FastCacheMiddleware, storage=RedisStorage("redis://localhost"))
194
+ ```
195
+
196
+ ## 🧪 Testing
197
+
198
+ ```python
199
+ import pytest
200
+ from httpx import AsyncClient
201
+ from examples.basic import app
202
+
203
+ @pytest.mark.asyncio
204
+ async def test_caching():
205
+ async with AsyncClient(app=app, base_url="http://test") as client:
206
+ # First request - cache miss
207
+ response1 = await client.get("/users/1")
208
+ assert response1.status_code == 200
209
+
210
+ # Second request - cache hit (should be faster)
211
+ response2 = await client.get("/users/1")
212
+ assert response2.status_code == 200
213
+ assert response1.json() == response2.json()
214
+
215
+ @pytest.mark.asyncio
216
+ async def test_cache_invalidation():
217
+ async with AsyncClient(app=app, base_url="http://test") as client:
218
+ # Cache data
219
+ await client.get("/users/1")
220
+
221
+ # Invalidate cache
222
+ await client.post("/users/1", json={})
223
+
224
+ # Next GET should execute new request
225
+ response = await client.get("/users/1")
226
+ assert response.status_code == 200
227
+ ```
228
+
229
+ ## 📊 Performance
230
+
231
+ ### Benchmarks
232
+
233
+ - **Route analysis**: ~5ms for 100 routes at startup
234
+ - **Route lookup**: ~0.1ms per request (O(n) by number of cached routes)
235
+ - **Cache hit**: ~1ms per request
236
+ - **Cache miss**: original request time + ~2ms for saving
237
+
238
+ ### Optimization
239
+
240
+ ```python
241
+ # For applications with many routes
242
+ app.add_middleware(
243
+ FastCacheMiddleware,
244
+ storage=InMemoryStorage(max_size=10000), # Increase cache size
245
+ controller=Controller(default_ttl=3600) # Increase default TTL
246
+ )
247
+ ```
248
+
249
+ ## 🔒 Security
250
+
251
+ ### Cache Isolation
252
+
253
+ ```python
254
+ def user_specific_cache() -> CacheConfig:
255
+ def secure_key_func(request):
256
+ # Include user token in key
257
+ token = request.headers.get("authorization", "").split(" ")[-1]
258
+ return f"{request.url.path}:token:{token}"
259
+
260
+ return CacheConfig(max_age=300, key_func=secure_key_func)
261
+
262
+ @app.get("/private/data", dependencies=[Depends(user_specific_cache)])
263
+ async def get_private_data():
264
+ return {"sensitive": "data"}
265
+ ```
266
+
267
+ ### Header Validation
268
+
269
+ Middleware automatically respects standard HTTP caching headers:
270
+
271
+ - `Cache-Control: no-cache` - skip cache
272
+ - `Cache-Control: no-store` - forbid caching
273
+ - `Cache-Control: private` - don't cache private responses
274
+
275
+
276
+ ## 🛠️ Advanced Usage
277
+
278
+ ### Custom Controller
279
+
280
+ ```python
281
+ from fast_cache_middleware import Controller
282
+
283
+ class CustomController(Controller):
284
+ async def is_cachable_request(self, request):
285
+ # Custom logic - don't cache admin requests
286
+ if request.headers.get("x-admin-request"):
287
+ return False
288
+ return await super().should_cache_request(request)
289
+
290
+ async def generate_cache_key(self, request):
291
+ # Add API version to key
292
+ version = request.headers.get("api-version", "v1")
293
+ base_key = await super().generate_cache_key(request)
294
+ return f"{version}:{base_key}"
295
+
296
+ app.add_middleware(
297
+ FastCacheMiddleware,
298
+ controller=CustomController()
299
+ )
300
+ ```
301
+
302
+ ## 📝 Examples
303
+
304
+ More examples in the `examples/` folder:
305
+
306
+ - **quick_start.py** - minimal example showing basic caching and invalidation
307
+ - **basic.py** - basic usage with FastAPI
308
+
309
+ ## 🤝 Contributing
310
+
311
+ ```bash
312
+ git clone https://github.com/chud0/FastCacheMiddleware
313
+ cd FastCacheMiddleware
314
+ poetry install --with dev
315
+ ./scripts/test.sh
316
+ ```
317
+
318
+ ## 📄 License
319
+
320
+ MIT License - see [LICENSE](LICENSE)
321
+
322
+ ---
323
+
324
+ ⭐ **Like the project? Give it a star!**
325
+
326
+ 🐛 **Found a bug?** [Create an issue](https://github.com/chud0/FastCacheMiddleware/issues)
327
+
328
+ 💡 **Have an idea?** [Suggest a feature](https://github.com/chud0/FastCacheMiddleware/discussions/categories/ideas)