کش کردن پاسخها در GraphQL
در GraphQL، کش کردن (Caching) کمی پیچیدهتر از REST است، زیرا در REST هر endpoint ثابت است و میتوان پاسخ آن را به راحتی روی CDN یا حافظه مرورگر نگهداری کرد. اما در GraphQL، تمام درخواستها از یک endpoint عبور میکنند و Queryها میتوانند ساختارهای متفاوتی داشته باشند، بنابراین نیاز به استراتژیهای متفاوت داریم.
روشهای کش کردن در GraphQL
- 1. کش در سمت کلاینت
کتابخانههایی مانند Apollo Client یا Relay به صورت داخلی قابلیت کش کردن پاسخها را دارند. زمانی که یک Query مشابه دوباره ارسال شود، پاسخ از کش داخلی گرفته میشود و نیاز به درخواست مجدد به سرور نیست. - 2. کش در سمت سرور
میتوان با استفاده از ابزارهایی مانندFastAPI-Cache
یا Redis، پاسخهای Queryها را بر اساس محتوا یا شناسه کاربر ذخیره کرد. این روش برای دادههایی که تغییرات کمی دارند مناسب است و باعث کاهش بار دیتابیس میشود. - 3. کش در سطح Field
در بعضی کتابخانهها مثل Strawberry، میتوان کش را روی Resolverهای خاص فعال کرد. به این ترتیب فقط دادههای پرهزینه یا پرکاربرد کش میشوند و بقیه Queryها به شکل عادی اجرا میشوند.
۱. نصب کتابخانه
pip install fastapi-cache2[redis] aioredis
۲. راهاندازی کش با Lifespan
# main.py
from fastapi import FastAPI
from contextlib import asynccontextmanager
from fastapi_cache import FastAPICache
from fastapi_cache.backends.redis import RedisBackend
import aioredis
@asynccontextmanager
async def lifespan(app: FastAPI):
redis = aioredis.from_url("redis://localhost:6379", encoding="utf8", decode_responses=True)
FastAPICache.init(RedisBackend(redis), prefix="fastapi-cache")
yield
await redis.close()
app = FastAPI(lifespan=lifespan)
۳. استفاده از کش در Resolverهای GraphQL
# graphql/tasks.py
import strawberry
from fastapi_cache.decorator import cache
from graphql.types import TaskType
from models.tasks import Task as TaskModel
@strawberry.type
class TaskQuery:
@strawberry.field
@cache(expire=60) # کش به مدت 60 ثانیه
def my_tasks(self, info) -> list[TaskType]:
db = info.context["db"]
current_user = info.context["current_user"]
tasks = db.query(TaskModel).filter(TaskModel.user_id == current_user.id).all()
return [
TaskType(
id=task.id,
title=task.title,
completed=task.completed,
user_id=task.user_id,
created_at=str(task.created_at),
updated_at=str(task.updated_at)
) for task in tasks
]
نکات مهم
- با Lifespan اتصال Redis به صورت خودکار مدیریت میشود.
- دکوراتور
@cache(expire=...)
پاسخ Resolver را برای مدت مشخص کش میکند. - برای Queryهای حساس یا دادههای شخصی، میتوان از
key_builder
برای تفکیک کش بر اساس کاربر استفاده کرد. - استفاده از Lifespan باعث پاکیزگی و مدیریت بهتر منابع هنگام شروع و پایان اپلیکیشن میشود.