You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The log messages show two successful (**`200 OK`**) responses to the same request (**`GET /immutable_data`**). The first request executed the `get_immutable_data` function and stored the result in Redis under key `api.get_immutable_data()`. The second request _**did not**_ execute the `get_immutable_data` function, instead the cached result was retrieved and sent as the response.
86
86
87
-
If data for an API endpoint needs to expire, you can specify the number of seconds before it is deleted by Redis using the `expire_after_seconds` parameter:
87
+
In most situations, response data must expire in a much shorter period of time than one year. Using the `expire` parameter, You can specify the number of seconds before data is deleted:
return {"success": True, "message": "this data should only be cached temporarily"}
95
95
```
96
96
97
+
> **NOTE!**`expire` can be either an `int` value or `timedelta` object. When the TTL is very short (like the example above) this results in a decorator that is expressive and requires minimal effort to parse visually. For durations an hour or longer (e.g., `@cache(expire=86400)`), IMHO, using a `timedelta` object is much easier to grok (`@cache(expire=timedelta(days=1))`).
98
+
99
+
Additionally, the decorators listed below define several common durations and can be used in place of the `@cache` decorator:
100
+
101
+
-`@cache_one_minute`
102
+
-`@cache_one_hour`
103
+
-`@cache_one_day`
104
+
-`@cache_one_week`
105
+
-`@cache_one_month`
106
+
-`@cache_one_year`
107
+
108
+
For example, instead of `@cache(expire=timedelta(days=1))`, you could use:
109
+
110
+
```python
111
+
from fastapi_redis_cache import cache_one_day
112
+
113
+
@app.get("/cache_one_day")
114
+
@cache_one_day()
115
+
defpartial_cache_one_day(response: Response):
116
+
return {"success": True, "message": "this data should be cached for 24 hours"}
117
+
```
118
+
119
+
If a duration that you would like to use throughout your project is missing from the list, you can easily create your own:
Then, simply import `cache_two_hours` and use it to decorate your API endpoint path functions:
132
+
133
+
```python
134
+
@app.get("/cache_two_hours")
135
+
@cache_two_hours()
136
+
defpartial_cache_two_hours(response: Response):
137
+
return {"success": True, "message": "this data should be cached for two hours"}
138
+
```
139
+
97
140
#### Response Headers
98
141
99
142
Below is an example HTTP response for the `/dynamic_data` endpoint. The `cache-control`, `etag`, `expires`, and `x-fastapi-cache` headers are added because of the `@cache` decorator:
- The `x-fastapi-cache` header field indicates that this response was found in the Redis cache (a.k.a. a `Hit`). The only other possible value for this field is `Miss`.
120
-
- The `expires` field and `max-age` value in the `cache-control` field indicate that this response will be considered fresh for 29 seconds. This is expected since `expire_after_seconds=30` was specified in the `@cache` decorator.
163
+
- The `expires` field and `max-age` value in the `cache-control` field indicate that this response will be considered fresh for 29 seconds. This is expected since `expire=30` was specified in the `@cache` decorator.
121
164
- The `etag` field is an identifier that is created by converting the response data to a string and applying a hash function. If a request containing the `if-none-match` header is received, the `etag` value will be used to determine if the requested resource has been modified.
122
165
123
166
If this request was made from a web browser, and a request for the same resource was sent before the cached response expires, the browser would automatically serve the cached version and the request would never even be sent to the FastAPI server.
@@ -126,27 +169,25 @@ Similarly, if a request is sent with the `cache-control` header containing `no-c
126
169
127
170
#### Cache Keys
128
171
129
-
Consider the `/get_user` API route defined below. This is the first path function we have seen where the response depends on the value of an argument (`user_id: int`). This is a typical CRUD operation where `user_id` is used to retrieve a `User` record from a SQLAlchemy database.
172
+
Consider the `/get_user` API route defined below. This is the first path function we have seen where the response depends on the value of an argument (`user_id: int`). This is a typical CRUD operation where `user_id` is used to retrieve a `User` record from a database. The API route also includes a dependency that injects a `Session` object (`db`) into the function, [per the instructions from the FastAPI docs](https://fastapi.tiangolo.com/tutorial/sql-databases/#create-a-dependency):
The API route also includes a dependency that injects a Session object (`db`) into the function, [per the instructions from the FastAPI docs](https://fastapi.tiangolo.com/tutorial/sql-databases/#create-a-dependency). This is used to query the database for the `User` corresponding to the `user_id` value.
139
-
140
181
In the [Initialize Redis](#initialize-redis) section of this document, the `FastApiRedisCache.init` method was called with `ignore_arg_types=[Request, Response, Session]`. Why is it necessary to include `Session` in this list?
141
182
142
183
Before we can answer that question, we must understand how a cache key is created. In order to create a unique identifier for the data sent in response to an API request, the following values are combined:
143
184
144
185
1) The optional `prefix` value provided as an argument to the `FastApiRedisCache.init` method (`"myapi-cache"`).
145
186
2) The module containing the path function (`"api"`).
146
187
3) The name of the path function (`"get_user"`).
147
-
4) The name and value of all arguments to the path function **EXCEPT for arguments with a type that exists in**`ignore_arg_types` (`"user_id=?"`).
188
+
4) The name and value of all arguments to the path function **EXCEPT for arguments with a type that exists in**`ignore_arg_types` (`"user_id=1"`).
148
189
149
-
Therefore, all response data for the `/get_user` endpoint will have a cache key equal to `"myapi-cache:api.get_user(user_id=?)"` (e.g., for `user_id=1`, the cache key will be `"myapi-cache:api.get_user(user_id=1)"`).
190
+
Therefore, the cache key in this example will be `"myapi-cache:api.get_user(user_id=1)"`).
150
191
151
192
Even though `db` is an argument to the path function, it is not included in the cache key because it is a `Session` type. If `Session` had not been included in the `ignore_arg_types` list, caching would be completely broken.
152
193
@@ -203,7 +244,6 @@ def get_scoreboard_for_date(
203
244
204
245
The `game_date` argument is a `MLBGameDate` type. This is a custom type that parses the value from the querystring to a date, and determines if the parsed date is valid by checking if it is within a certain range. The implementation for `MLBGameDate` is given below:
205
246
206
-
207
247
```python
208
248
classMLBGameDate:
209
249
def__init__(
@@ -225,7 +265,7 @@ class MLBGameDate:
225
265
returnself.date.strftime("%Y-%m-%d")
226
266
```
227
267
228
-
Please note the custom `__str__` method that overrides the default behavior. This way, instead of `<MLBGameDate object at 0x11c7e35e0>`, the value will be formatted as, for example, `2019-05-09`. You can use this strategy whenever you have an argument that has en effect on the response data but converting that argument to a string results in a value containing the object's memory location.
268
+
Please note the `__str__` method that overrides the default behavior. This way, instead of `<MLBGameDate object at 0x11c7e35e0>`, the value will be formatted as, for example, `2019-05-09`. You can use this strategy whenever you have an argument that has en effect on the response data but converting that argument to a string results in a value containing the object's memory location.
0 commit comments