WPGraphQL یک افزونه متن‌باز است که GraphQL را به وردپرس می‌آورد و به‌جای چندین درخواست REST، یک کوئری واحد و دقیق در اختیار کلاینت قرار می‌دهد. این افزونه از سال ۲۰۱۶ توسط Jason Bahl توسعه می‌یابد و امروزه در صدها پروژه Headless Production استفاده می‌شود. GraphQL یک زبان پرس‌وجو است که کلاینت را از وابستگی به ساختار پاسخ سرور آزاد می‌کند. WPGraphQL با تکیه بر Schema خودتوصیف و Resolverهای قابل توسعه، وردپرس را به یک گراف داده‌ای قابل پرس‌وجو تبدیل می‌کند. این مقاله از معماری داخلی و Schema تا امنیت، کش و Resolverهای سفارشی را پوشش می‌دهد.

در یک پروژه Headless که برای یک پلتفرم آموزشی طراحی می‌شد، اولین نسخه با REST ساخته شد و صفحه اصلی به ۱۷ درخواست جداگانه نیاز داشت. بعد از مهاجرت به WPGraphQL، همان صفحه با یک کوئری و در کمتر از یک‌سوم زمان رندر شد. این تفاوت، نه یک بهبود تدریجی، بلکه یک تغییر پارادایم است: از «چند درخواست به چند منبع» به «یک درخواست به یک گراف». همین تغییر، دلیل اصلی محبوبیت WPGraphQL در پروژه‌های مدرن است.

WPGraphQL چیست و چگونه کار می‌کند؟

WPGraphQL یک افزونه رایگان و متن‌باز است که یک Endpoint از نوع GraphQL در مسیر /graphql به وردپرس اضافه می‌کند. این افزونه توسط Jason Bahl در سال ۲۰۱۶ شروع شد و از آن زمان به یکی از بالغ‌ترین پیاده‌سازی‌های GraphQL در اکوسیستم PHP تبدیل شده است. اگر با مفاهیم پایه REST API آشنا باشید، WPGraphQL را می‌توان به‌عنوان یک لایه مکمل در نظر گرفت که مزیت اصلی آن، حذف Over-fetching و Under-fetching است.

GraphQL (Graph Query Language) در سال ۲۰۱۵ توسط فیسبوک متن‌باز شد و به‌سرعت به استانداردی برای طراحی API تبدیل شد. برخلاف REST که در آن سرور ساختار پاسخ را تعیین می‌کند، در GraphQL کلاینت دقیقاً مشخص می‌کند چه فیلدهایی را می‌خواهد. این تفاوت بنیادین، GraphQL را به یک زبان اعلانی (Declarative) تبدیل می‌کند، در حالی که REST ذاتاً Imperative (دستوری) است.

WPGraphQL این زبان را به تمام داده‌های وردپرس متصل می‌کند: نوشته‌ها، برگه‌ها، کاربران، دسته‌بندی‌ها، برچسب‌ها، نظرات، رسانه‌ها، متادیتا و حتی داده‌های افزونه‌های شخص ثالث مثل WooCommerce و Advanced Custom Fields. در واقع، WPGraphQL به‌جای اینکه یک لایه انتزاعی جدید بسازد، از همان توابع و هوک‌های بومی وردپرس استفاده می‌کند. اگر با هوک‌های وردپرس به‌عنوان قلب توسعه آشنا باشید، این موضوع را بهتر درک می‌کنید: WPGraphQL روی همان زیرساخت سوار می‌شود، نه در کنار آن.

«WPGraphQL یک API جدید نیست؛ یک زبان جدید برای دسترسی به همان داده‌های وردپرس است.»

از منظر کاربرد، WPGraphQL سه سناریوی اصلی را پوشش می‌دهد. اول، معماری Headless که در آن وردپرس فقط به‌عنوان CMS (Content Management System) عمل می‌کند و فرانت‌اند با React، Next.js، Vue یا Gatsby ساخته می‌شود. دوم، اپلیکیشن‌های موبایل که نیاز به کاهش تعداد درخواست‌های شبکه دارند. سوم، داشبوردهای مدیریتی پیچیده که نیاز به پرس‌وجوی داده‌های تودرتو دارند. اگر با مقایسه REST و GraphQL در وردپرس آشنا شده باشید، می‌دانید که این سه سناریو دقیقاً همان جاهایی هستند که GraphQL مزیت خود را نشان می‌دهد.

معماری داخلی WPGraphQL

درک معماری داخلی WPGraphQL برای استفاده حرفه‌ای ضروری است. این افزونه از پنج لایه تشکیل شده که هر کدام مسئولیت مشخصی دارند:

لایه اول: Schema Registry. این لایه تمام Typeها، Fieldها و Argumentها را نگه می‌دارد. WPGraphQL یک Schema خودتوصیف (Self-describing) دارد، یعنی خودش می‌تواند ساختارش را توضیح دهد. این ویژگی، ابزارهایی مثل GraphiQL را قادر می‌سازد که Auto-completion و Documentation زنده ارائه دهند.

لایه دوم: Type Registry. Typeها در WPGraphQL به سه دسته تقسیم می‌شوند: Object Typeها (مثل Post، User)، Interface Typeها (مثل Node)، و Scalar Typeها (مثل String، Int). هر Type مجموعه‌ای از Fieldها دارد و هر Field یک Resolver دارد.

لایه سوم: Resolver Layer. Resolverها توابعی هستند که تعیین می‌کنند هر Field چگونه مقداردهی شود. مثلاً Resolver فیلد title در Type Post، داده را از $post->post_title می‌خواند. Resolverها در WPGraphQL می‌توانند Batch شوند (با DataLoader) تا N+1 Problem کاهش یابد.

لایه چهارم: Connection Layer. WPGraphQL از الگوی Relay Connection برای صفحه‌بندی (Pagination) استفاده می‌کند. هر Connection دارای edges، nodes، pageInfo، و totalCount است. این الگو، استانداردی است که توسط Relay.js معرفی شد و امروزه در تمام پیاده‌سازی‌های حرفه‌ای GraphQL رعایت می‌شود.

لایه پنجم: DataLoader Layer. این لایه، قلب بهینه‌سازی عملکرد است. DataLoader با جمع‌آوری Resolverهای هم‌زمان و گروه‌بندی آن‌ها در یک کوئری واحد، از N+1 Query Problem جلوگیری می‌کند. در WPGraphQL، این لایه به‌صورت داخلی با Deferred Resolvers پیاده‌سازی شده است.

لایه مسئولیت کلاس اصلی
Schema Registry نگهداری Typeها و Fieldها WPGraphQLRegistrySchemaRegistry
Type Registry تعریف و مدیریت Typeها WPGraphQLRegistryTypeRegistry
Resolver Layer مقداردهی Fieldها WPGraphQLData*
Connection Layer صفحه‌بندی و روابط WPGraphQLConnection*
DataLoader Layer بهینه‌سازی کوئری WPGraphQLDataLoader*

نکته مهم در معماری WPGraphQL این است که تمام این لایه‌ها از طریق Hookهای بومی وردپرس قابل توسعه هستند. برای مثال، Hook graphql_register_types به شما اجازه می‌دهد Type سفارشی اضافه کنید، و Hook graphql_register_fields برای افزودن Field به Typeهای موجود استفاده می‌شود. این طراحی، WPGraphQL را از یک افزونه معمولی به یک پلتفرم توسعه‌پذیر تبدیل می‌کند.

Schema در WPGraphQL: قلب تپنده گراف

Schema در WPGraphQL یک قرارداد (Contract) است بین سرور و کلاینت. این Schema تعریف می‌کند چه Typeهایی وجود دارند، چه Fieldهایی دارند، و چه Argumentهایی می‌پذیرند. برخلاف REST که در آن هر Endpoint مستندات جداگانه دارد، در GraphQL یک Schema واحد تمام API را توصیف می‌کند.

Schema در WPGraphQL از طریق Introsepction Query قابل کاوش است. این کوئری، ساختار کامل گراف را برمی‌گرداند:

query IntrospectPostType {
  __type(name: "Post") {
    name
    fields {
      name
      type {
        name
        kind
      }
    }
  }
}

Introsepction یکی از قدرتمندترین و در عین حال خطرناک‌ترین ویژگی‌های GraphQL است. از یک سو، به ابزارهایی مثل GraphiQL اجازه می‌دهد که Auto-completion و Documentation زنده ارائه دهند. از سوی دیگر، در محیط Production می‌تواند اطلاعات حساس درباره ساختار API را فاش کند. به همین دلیل، توصیه می‌شود Introsepction در Production غیرفعال شود.

«Schema در GraphQL، قرارداد است. اگر قرارداد نشت کند، مهاجم نقشه کامل سیستم را در دست دارد.»

Typeهای اصلی در Schema وردپرس

WPGraphQL یک Schema پیش‌فرض دارد که تمام Entityهای اصلی وردپرس را پوشش می‌دهد. مهم‌ترین Typeها عبارتند از:

  • RootQuery: نقطه ورود تمام کوئری‌های خواندن
  • RootMutation: نقطه ورود تمام عملیات نوشتن
  • Post: نوشته‌ها و برگه‌ها (با Subtypeهای Page، Post، و Custom Post Typeها)
  • User: کاربران وردپرس
  • TermNode: دسته‌بندی‌ها و برچسب‌ها
  • Comment: نظرات
  • MediaItem: رسانه‌ها
  • Taxonomy: تاکسونومی‌ها
  • PostType: انواع پست
  • MenuItem: آیتم‌های منو

هر Type با Interface Node پیاده‌سازی می‌شود که یک id جهانی (Global ID) دارد. این Global ID، فرمت Base64 encoded از type:id است، مثلاً post:۱۲۳ به cG9zdDoxMjM= تبدیل می‌شود. این استاندارد، از تداخل ID بین Typeهای مختلف جلوگیری می‌کند.

افزودن Type سفارشی به Schema

برای افزودن یک Object Type سفارشی، از Hook graphql_register_types استفاده می‌شود:

add_action( 'graphql_register_types', function() {
    register_graphql_object_type( 'BookInfo', [
        'description' => __('اطلاعات کتاب', 'textdomain' ),
        'fields'      => [
            'isbn'   => [
                'type'        => 'String',
                'description' => __( 'شماره ISBN', 'textdomain' ),
            ],
            'author' => [
                'type'        => 'String',
                'description' => __( 'نام نویسنده', 'textdomain' ),
            ],
        ],
    ] );
} );

این Type سفارشی می‌تواند به Type Post متصل شود تا داده‌های متادیتا را در گراف قابل پرس‌وجو کند. این الگو، در پروژه‌هایی که از Advanced Custom Fields استفاده می‌کنند، بسیار رایج است. اگر با ساخت فیلدهای سفارشی در وردپرس کار کرده باشید، می‌دانید که نمایش این فیلدها در GraphQL نیازمند Resolver سفارشی است.

Mutationها در Schema

Mutationها در WPGraphQL نقطه ورود عملیات نوشتن هستند. هر Mutation یک Input Type و یک Output Type دارد. مهم‌ترین Mutationهای پیش‌فرض عبارتند از:

  • createPost: ایجاد نوشته جدید
  • updatePost: به‌روزرسانی نوشته
  • deletePost: حذف نوشته
  • createUser: ایجاد کاربر
  • updateUser: به‌روزرسانی کاربر
  • createComment: ایجاد نظر
  • updateSettings: به‌روزرسانی تنظیمات

هر Mutation نیازمند احراز هویت است. بدون Token معتبر، WPGraphQL اجازه اجرای Mutation را نمی‌دهد. این موضوع، امنیت را در سطح Schema تضمین می‌کند.

نصب و راه‌اندازی گام‌به‌گام

نصب WPGraphQL از طریق مخزن رسمی وردپرس انجام می‌شود. پس از نصب و فعال‌سازی، یک منوی جدید در پنل مدیریت با عنوان «GraphQL» ظاهر می‌شود که شامل سه بخش است: Settings، GraphiQL، و Help.

اولین گام، بررسی پیش‌نیازها است. WPGraphQL به PHP نسخه ۷.۴ یا بالاتر و وردپرس نسخه ۵.۰ یا بالاتر نیاز دارد. همچنین توصیه می‌شود که Permalinkها روی ساختار «نام نوشته» تنظیم شده باشند، چون WPGraphQL از URL Rewrite برای Endpoint خود استفاده می‌کند.

دومین گام، دسترسی به GraphiQL است. GraphiQL یک IDE مرورگری است که در پنل مدیریت وردپرس در مسیر /wp-admin/admin.php?page=graphiql-ide قابل دسترسی است. این ابزار، امکان نوشتن کوئری، مشاهده Schema، و تست Mutationها را فراهم می‌کند.

query FirstQuery {
  posts(first: 5) {
    nodes {
      id
      title
      slug
      date
    }
  }
}

این کوئری ساده، پنج نوشته آخر را با چهار فیلد برمی‌گرداند. اگر با آموزش استفاده از GraphQL در وردپرس آشنا شده باشید، این ساختار برای شما آشناست.

سومین گام، پیکربندی CORS (Cross-Origin Resource Sharing) است. اگر فرانت‌اند شما روی دامنه‌ای جداگانه اجرا می‌شود (مثلاً app.example.com و بک‌اند روی cms.example.com)، باید هدرهای CORS را تنظیم کنید. WPGraphQL یک فیلتر graphql_response_headers_to_send فراهم می‌کند که با آن می‌توانید هدرهای موردنیاز را اضافه کنید:

add_filter( 'graphql_response_headers_to_send', function( $headers ) {
    $headers['Access-Control-Allow-Origin']  = 'https://app.example.com';
    $headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS';
    $headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization';
    return $headers;
} );

چهارمین گام، راه‌اندازی احراز هویت است. WPGraphQL از سه روش پشتیبانی می‌کند: Application Passwords (بومی وردپرس)، JWT Authentication (با افزونه wp-graphql-jwt-authentication)، و Cookie Authentication (برای درخواست‌های همان دامنه). اگر با پیاده‌سازی JWT در APIهای مدرن آشنا هستید، می‌دانید که JWT برای معماری Headless انتخاب طبیعی‌تری است.

پنجمین گام، غیرفعال کردن Introsepction در Production است. این کار با فیلتر graphql_introspection_enabled انجام می‌شود:

add_filter( 'graphql_introspection_enabled', function( $enabled ) {
    if ( 'production' === wp_get_environment_type() ) {
        return false;
    }
    return $enabled;
} );

کوئری‌نویسی در GraphQL وردپرس

کوئری‌نویسی در GraphQL، هنر درخواست دقیق داده است. برخلاف REST که در آن ساختار پاسخ توسط سرور تعیین می‌شود، در GraphQL شما ساختار پاسخ را می‌سازید. این انعطاف‌پذیری، هم قدرت است و هم مسئولیت.

کوئری‌های پایه

ساده‌ترین کوئری، دریافت لیست نوشته‌هاست:

query GetPosts {
  posts(first: 10, where: { orderby: { field: DATE, order: DESC } }) {
    nodes {
      id
      title
      excerpt
      date
      author {
        node {
          name
        }
      }
      featuredImage {
        node {
          sourceUrl
          altText
        }
      }
    }
  }
}

این کوئری، در یک درخواست واحد، ده نوشته آخر را با نویسنده و تصویر شاخص برمی‌گرداند. در REST، همین داده نیازمند سه درخواست جداگانه بود. اگر با ساختاردهی داده با JSON آشنا باشید، می‌دانید که پاسخ GraphQL در قالب JSON برگردانده می‌شود، اما ساختار آن توسط کلاینت تعیین شده است.

فیلترها و Argumentها

WPGraphQL مجموعه‌ای غنی از Argumentها برای فیلتر داده فراهم می‌کند. برای مثال، دریافت نوشته‌های یک دسته‌بندی خاص:

query PostsByCategory {
  posts(first: 10, where: { categoryName: "technology" }) {
    nodes {
      title
      slug
    }
  }
}

یا دریافت نوشته‌های یک نویسنده خاص با مرتب‌سازی بر اساس عنوان:

query PostsByAuthor {
  posts(
    first: 10
    where: {
      authorName: "admin"
      orderby: { field: TITLE, order: ASC }
    }
  ) {
    nodes {
      title
      date
    }
  }
}

صفحه‌بندی با Connection

WPGraphQL از الگوی Relay Connection برای صفحه‌بندی استفاده می‌کند. این الگو، استانداردی است که در آن هر Connection دارای edges و pageInfo است:

query PaginatedPosts {
  posts(first: 10, after: "YXJyYXljb25uZWN0aW9uOjk=") {
    edges {
      node {
        title
      }
      cursor
    }
    pageInfo {
      hasNextPage
      hasPreviousPage
      startCursor
      endCursor
    }
  }
}

Cursor در این الگو، یک رشته Base64 encoded از موقعیت رکورد است. برخلاف Offset-based Pagination که در REST رایج است، Cursor-based Pagination در برابر تغییرات داده مقاوم‌تر است و مشکل «پرش آیتم‌ها» را حل می‌کند.

«صفحه‌بندی Cursor-based در GraphQL، از Offset-based در REST پایدارتر است، به‌خصوص در سیستم‌هایی که داده‌ها به‌سرعت تغییر می‌کنند.»

Fragments: کاهش تکرار

Fragments در GraphQL امکان بازاستفاده از مجموعه Fieldها را فراهم می‌کند:

fragment PostFields on Post {
  id
  title
  slug
  date
}

query GetPosts {
  posts(first: 10) {
    nodes {
      ...PostFields
    }
  }
}

این ویژگی، در پروژه‌هایی که ساختار داده پیچیده دارند، حجم کد را به‌شدت کاهش می‌دهد و از تکرار جلوگیری می‌کند. Fragments در سمت کلاینت نیز قابل استفاده هستند (با Apollo Client یا Relay) و به Fragment Colocation معروفند.

Mutations: نوشتن داده در WPGraphQL

Mutationها در WPGraphQL نقطه ورود عملیات نوشتن هستند. برخلاف REST که در آن از متدهای POST، PUT، PATCH و DELETE استفاده می‌شود، در GraphQL تمام عملیات نوشتن از طریق mutation انجام می‌شود.

نمونه‌ای از Mutation برای ایجاد نوشته:

mutation CreatePost {
  createPost(input: {
    title: "عنوان نوشته جدید"
    content: "محتوای نوشته"
    status: PUBLISH
    categories: { nodes: [{ slug: "technology" }] }
  }) {
    post {
      id
      title
      slug
      date
    }
  }
}

پاسخ Mutation، ساختاری مشابه کوئری دارد: شما دقیقاً انتخاب می‌کنید چه Fieldهایی از نتیجه برگردانده شود. این یکنواختی، تجربه توسعه‌دهنده را بسیار روان‌تر می‌کند.

Mutationهای WPGraphQL نیازمند احراز هویت هستند. برای ارسال Mutation، باید یک Token معتبر در هدر Authorization ارسال شود:

POST /graphql
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

اگر با راهنمای احراز هویت REST API آشنا باشید، می‌دانید که این ساختار استاندارد است و WPGraphQL از همان مکانیزم‌های امنیتی REST استفاده می‌کند.

Mutationهای سفارشی

برای افزودن Mutation سفارشی، از Hook graphql_register_types و تابع register_graphql_mutation() استفاده می‌شود:

add_action('graphql_register_types', function() {
    register_graphql_mutation( 'submitBookReview', [
        'inputFields' => [
            'bookId' => [
                'type'        => [ 'non_null' => 'Int' ],
                'description' => __( 'شناسه کتاب', 'textdomain' ),
            ],
            'rating' => [
                'type'        => [ 'non_null' => 'Int' ],
                'description' => __( 'امتیاز از ۱ تا ۵', 'textdomain' ),
            ],
        ],
        'outputFields' => [
            'success' => [
                'type'        => 'Boolean',
                'description' => __( 'وضعیت ثبت نظر', 'textdomain' ),
            ],
        ],
        'mutateAndGetPayload' => function( $input, $context, $info ) {
            // منطق ذخیره‌سازی
            return [ 'success' => true ];
        },
    ] );
} );

این الگو، به شما اجازه می‌دهد هر عملیات نوشتن سفارشی را با تمام قدرت Schema و Validation به GraphQL اضافه کنید.

Resolverها، DataLoader و N+1 Problem

Resolverها توابعی هستند که تعیین می‌کنند هر Field چگونه مقداردهی شود. در WPGraphQL، هر Field یک Resolver پیش‌فرض دارد که با توابع بومی وردپرس کار می‌کند. اما در پروژه‌های سفارشی، ممکن است نیاز به Resolver اختصاصی داشته باشید.

مشکل اصلی Resolverها، N+1 Query Problem است. فرض کنید کوئری زیر ارسال می‌شود:

query PostsWithAuthors {
  posts(first: 20) {
    nodes {
      title
      author {
        node {
          name
        }
      }
    }
  }
}

بدون DataLoader، این کوئری به ۲۱ کوئری SQL تبدیل می‌شود: یک کوئری برای دریافت ۲۰ نوشته، و ۲۰ کوئری جداگانه برای دریافت نویسنده هر نوشته. این همان N+1 Problem کلاسیک است که در REST هم رخ می‌دهد، اما در GraphQL چون کلاینت می‌تواند هر ساختاری درخواست کند، پتانسیل آن بسیار بالاتر است.

DataLoader با Batch کردن Resolverها این مشکل را حل می‌کند. مکانیزم آن به این شکل است:

  1. Resolver فیلد author به‌جای اجرای فوری، یک درخواست را به DataLoader اضافه می‌کند.
  2. DataLoader تمام درخواست‌های هم‌زمان (۲۰ نویسنده) را جمع می‌کند.
  3. در پایان چرخه اجرا (Tick)، DataLoader یک کوئری واحد با WHERE ID IN (...) اجرا می‌کند.
  4. نتیجه بین تمام Resolverهای منتظر توزیع می‌شود.

WPGraphQL این مکانیزم را به‌صورت داخلی با Deferred Resolvers پیاده‌سازی کرده است. اگر با بهینه‌سازی کوئری‌های وردپرس با کدنویسی آشنا باشید، این الگو برای شما آشناست: به‌جای حلقه foreach با get_userdata()، از get_users( [ 'include' => $ids ] ) استفاده می‌شود.

«N+1 Problem در GraphQL، خطرناک‌تر از REST است، چون کلاینت قدرت ایجاد ساختارهای عمیق را دارد. DataLoader تنها دفاع مؤثر است.»

ثبت DataLoader سفارشی

برای ثبت DataLoader سفارشی، از Hook graphql_data_loaders استفاده می‌شود:

add_action( 'graphql_data_loaders', function( $loaders, $context ) {
    $loaders['customBook'] = new CustomBookLoader( $context );
    return $loaders;
}, 10, 2 );

DataLoader سفارشی باید از کلاس WPGraphQLDataLoaderAbstractDataLoader ارث‌بری کند و متدهای loadKeys() و loadKey() را پیاده‌سازی نماید. این الگو، در پروژه‌هایی که با Entityهای سفارشی کار می‌کنند، حیاتی است.

امنیت در WPGraphQL: سطح حمله جدید

GraphQL سطح حمله متفاوتی نسبت به REST ایجاد می‌کند. در REST، هر Endpoint یک سطح حمله جداگانه است و اگر یک Endpoint ضعیف باشد، فقط همان آسیب‌پذیر است. در GraphQL، تمام Schema یک سطح حمله واحد است و اگر یک Field بدون کنترل دسترسی مناسب باشد، هر کلاینتی می‌تواند آن را استخراج کند.

سه تهدید اصلی در WPGraphQL:

۱. Introsepction Query. این کوئری، ساختار کامل Schema را برمی‌گرداند و به مهاجم اجازه می‌دهد تمام Fieldهای موجود را کشف کند. راه‌حل: غیرفعال کردن Introsepction در Production با فیلتر graphql_introspection_enabled.

۲. Query Depth Attack. یک کلاینت مخرب می‌تواند یک کوئری با عمق بسیار زیاد ارسال کند که سرور را زمین‌گیر کند. مثلاً کوئری‌ای که ۱۰ سطح تودرتو دارد و در هر سطح ۱۰۰ آیتم درخواست می‌کند. راه‌حل: تنظیم graphql_max_query_depth:

add_filter( 'graphql_max_query_depth', function() {
    return 10;
} );

۳. Query Complexity Attack. حتی یک کوئری با عمق کم می‌تواند پیچیدگی بالایی داشته باشد اگر تعداد Fieldها زیاد باشد. راه‌حل: استفاده از WPGraphQL Query Analyzer یا افزونه‌های مشابه که پیچیدگی کوئری را قبل از اجرا محاسبه می‌کنند.

علاوه بر این سه تهدید، نکات امنیتی زیر نیز باید رعایت شوند:

  • استفاده از HTTPS برای تمام درخواست‌ها
  • محدود کردن CORS به دامنه‌های مجاز
  • Rate Limiting در سطح Nginx یا Cloudflare
  • غیرفعال کردن Fieldهای حساس در Schema (مثل user.email برای کاربران مهمان)
  • استفاده از Persisted Queries در Production

اگر با روش‌های امن‌سازی REST API آشنا باشید، بسیاری از این نکات برای شما آشناست، اما در GraphQL باید به‌صورت صریح پیاده‌سازی شوند، نه به‌صورت پیش‌فرض.

عملکرد، کش و Persisted Queries

عملکرد در GraphQL دو لایه دارد: لایه Resolver (که با DataLoader بهینه می‌شود) و لایه کش (که در GraphQL چالش‌برانگیزتر از REST است).

چالش کش در GraphQL

در REST، هر URL یک Cache Key جداگانه دارد و می‌توان پاسخ‌ها را در CDN، پروکسی معکوس، یا مرورگر کش کرد. در GraphQL، تمام درخواست‌ها از یک Endpoint با متد POST ارسال می‌شوند و Cache Key وابسته به بدنه درخواست است. این یعنی کش سطح HTTP به‌سادگی REST کار نمی‌کند.

سه راه‌حل برای این چالش وجود دارد:

راه‌حل اول: Persisted Queries. کلاینت به‌جای ارسال متن کامل کوئری، یک هش (Hash) ارسال می‌کند و سرور آن را به کوئری اصلی نگاشت می‌کند. این تکنیک، هم امنیت را افزایش می‌دهد (چون کلاینت نمی‌تواند کوئری دلخواه بفرستد) و هم کش را ساده می‌کند (چون هر هش یک Cache Key یکتا است).

راه‌حل دوم: کش در سطح Resolver. هر Resolver می‌تواند نتیجه خود را در Object Cache (Redis یا Memcached) ذخیره کند. این روش انعطاف‌پذیر است اما پیچیدگی Invalidations دارد. اگر با کش هوشمند با Transient API آشنا باشید، می‌دانید که مدیریت Invalidations در وردپرس ظرافت خاص خود را دارد.

راه‌حل سوم: CDN اختصاصی GraphQL. سرویس‌هایی مثل Stellate (سابقاً GraphCDN) یک لایه کش اختصاصی برای GraphQL فراهم می‌کنند که کوئری‌ها را تحلیل و کش می‌کند. این رویکرد حرفه‌ای‌ترین راه‌حل است اما هزینه و وابستگی به سرویس شخص ثالث دارد.

بهینه‌سازی Performance

چند تکنیک عملی برای بهبود عملکرد WPGraphQL:

فعال‌سازی Object Cache. اگر Object Cache (مثل Redis) فعال نباشد، هر کوئری WPGraphQL به دیتابیس می‌رود. با فعال‌سازی Object Cache، نتایج کوئری‌های تکراری از حافظه خوانده می‌شوند. اگر با بهینه‌سازی پیشرفته دیتابیس وردپرس آشنا باشید، این اولین گام بهینه‌سازی است.

محدود کردن Query Depth. حتی اگر حمله‌ای رخ ندهد، کوئری‌های بسیار عمیق بار سرور را افزایش می‌دهند. تنظیم graphql_max_query_depth روی ۱۰ یا ۱۵، تعادل خوبی ایجاد می‌کند.

استفاده از Batch Resolvers. برای Entityهای سفارشی، از DataLoader استفاده کنید تا N+1 Problem کاهش یابد.

کش نتایج در سطح Field. برای Fieldهایی که داده‌شان به‌ندرت تغییر می‌کند (مثل تنظیمات سایت)، از Transient API برای کش استفاده کنید.

استفاده از CDN. اگر Persisted Queries فعال است، می‌توانید از CDN برای بهبود سرعت سایت استفاده کنید و پاسخ‌ها را در لبه شبکه کش کنید.

اکوسیستم و افزونه‌های مکمل

WPGraphQL به‌تنهایی یک API کامل است، اما اکوسیستم آن با افزونه‌های مکمل، دامنه کاربرد را بسیار گسترده‌تر می‌کند. مهم‌ترین افزونه‌های مکمل عبارتند از:

WPGraphQL for Advanced Custom Fields. این افزونه تمام فیلدهای ACF را به Schema اضافه می‌کند. اگر با ساخت فیلدهای سفارشی کار می‌کنید، این افزونه ضروری است.

WPGraphQL for WooCommerce. این افزونه تمام Entityهای WooCommerce — محصولات، سفارش‌ها، مشتریان، کوپن‌ها — را به گراف اضافه می‌کند. برای فروشگاه‌های Headless، این افزونه حیاتی است.

WPGraphQL for Gravity Forms. برای پروژه‌هایی که از Gravity Forms استفاده می‌کنند، این افزونه فرم‌ها و ورودی‌ها را به GraphQL متصل می‌کند.

WPGraphQL JWT Authentication. این افزونه احراز هویت با JWT را فراهم می‌کند و برای معماری Headless ضروری است. اگر با پیاده‌سازی JWT در APIهای مدرن آشنا هستید، این افزونه همان الگو را در وردپرس پیاده می‌کند.

WPGraphQL CORS. این افزونه تنظیمات CORS را ساده می‌کند و از ارسال هدرهای اشتباه جلوگیری می‌کند.

WPGraphQL Offset Pagination. به‌صورت پیش‌فرض، WPGraphQL از Cursor-based Pagination استفاده می‌کند. اگر نیاز به Offset-based دارید (مثلاً برای سازگاری با کتابخانه‌های قدیمی)، این افزونه آن را فراهم می‌کند.

در سمت کلاینت، ابزارهای GraphQL مثل Apollo Client، Relay، و urql به‌صورت بومی با WPGraphQL کار می‌کنند. این کتابخانه‌ها Normalized Cache دارند که از درخواست‌های تکراری جلوگیری می‌کند. اگر با تفاوت REST و GraphQL آشنا هستید، این Normalized Cache یکی از مزیت‌های کلیدی GraphQL در سمت کلاینت است.

پرسش‌های پرتکرار درباره WPGraphQL

آیا WPGraphQL برای پروژه‌های Production پایدار است؟

بله. WPGraphQL از سال ۲۰۱۶ در حال توسعه است و در پروژه‌های Production بسیاری — از جمله سایت‌های خبری پربازدید و فروشگاه‌های Headless — استفاده می‌شود. با این حال، باید توجه داشت که این افزونه یک وابستگی شخص ثالث است و اگر توسعه آن متوقف شود، پروژه شما با ریسک مواجه می‌شود. برای کاهش این ریسک، توصیه می‌شود Schema را در کد مستند کنید و Resolverهای حیاتی را در افزونه اختصاصی پیاده‌سازی نمایید.

آیا WPGraphQL جایگزین REST API وردپرس می‌شود؟

خیر. REST API بخشی از هسته وردپرس است و تمام ویرایشگر بلوک، اپلیکیشن موبایل، و بسیاری از افزونه‌های رسمی بر پایه آن ساخته شده‌اند. WPGraphQL یک لایه اضافی است که در کنار REST کار می‌کند، نه به‌جای آن. در بسیاری از پروژه‌ها، ترکیب هر دو — REST برای Mutation و GraphQL برای Query — بهترین نتیجه را می‌دهد.

چگونه امنیت WPGraphQL را در Production تضمین کنیم؟

چهار اقدام ضروری: اول، غیرفعال کردن Introsepction با فیلتر graphql_introspection_enabled. دوم، تنظیم graphql_max_query_depth روی ۱۰ یا ۱۵. سوم، استفاده از Persisted Queries برای محدود کردن کلاینت به کوئری‌های از پیش تأییدشده. چهارم، Rate Limiting در سطح Nginx یا Cloudflare. اگر با اصول امنیت API آشنا هستید، این اقدامات بخشی از یک استراتژی جامع هستند.

آیا WPGraphQL با WooCommerce سازگار است؟

بله، با افزونه WPGraphQL for WooCommerce. این افزونه تمام Entityهای WooCommerce — محصولات، سفارش‌ها، مشتریان، کوپن‌ها، دسته‌بندی‌ها — را به GraphQL متصل می‌کند. برای فروشگاه‌های Headless، این افزونه ضروری است و به شما اجازه می‌دهد یک صفحه محصول را با تمام داده‌های مرتبط (تنوع، ویژگی‌ها، نظرات، محصولات مرتبط) در یک کوئری دریافت کنید.

آیا GraphQL سرعت سایت را کاهش می‌دهد؟

پاسخ ساده «بله» یا «خیر» گمراه‌کننده است. GraphQL می‌تواند تعداد درخواست‌های شبکه را کاهش دهد و حجم داده منتقل‌شده را کم کند. اما اگر Resolverها به‌درستی پیاده‌سازی نشوند، می‌تواند N+1 Query Problem ایجاد کند و بار دیتابیس را چند برابر کند. بنابراین، عملکرد GraphQL بیش از هر چیز به کیفیت پیاده‌سازی Resolverها و فعال بودن DataLoader بستگی دارد.

چگونه از WPGraphQL در Next.js استفاده کنیم؟

سه گام اصلی: اول، یک Endpoint GraphQL در محیط Production راه‌اندازی کنید و Introsepction را برای محیط Development فعال نگه دارید. دوم، از @apollo/client یا urql برای مدیریت کوئری‌ها استفاده کنید. سوم، از Next.js ISR (Incremental Static Regeneration) یا SSG (Static Site Generation) برای کش صفحات استفاده کنید. این ترکیب، عملکرد بهینه‌ای در لبه شبکه ایجاد می‌کند.

آیا WPGraphQL با WordPress Multisite کار می‌کند؟

بله، اما با محدودیت‌هایی. به‌صورت پیش‌فرض، WPGraphQL روی هر سایت شبکه به‌صورت جداگانه کار می‌کند و داده‌های سایت‌های دیگر را برنمی‌گرداند. برای پرس‌وجوی داده‌های چند سایت، باید از افزونه‌های اختصاصی مثل WPGraphQL Multisite استفاده کنید یا Resolverهای سفارشی بنویسید.

نگاه پایانی به انقلاب GraphQL در وردپرس

WPGraphQL یک تغییر پارادایم در نحوه دسترسی به داده‌های وردپرس است. این تغییر، نه فقط در سطح فنی، بلکه در سطح معماری و تجربه توسعه‌دهنده رخ می‌دهد. در معماری سنتی، فرانت‌اند و بک‌اند از طریق REST با یکدیگر صحبت می‌کردند و هر تغییر در نیازهای فرانت‌اند، نیازمند تغییر در Endpointهای بک‌اند بود. در معماری GraphQL، فرانت‌اند با یک Schema خودتوصیف صحبت می‌کند و می‌تواند هر ساختاری از داده را درخواست کند، بدون نیاز به تغییر بک‌اند.

این انعطاف‌پذیری، WPGraphQL را به انتخاب اول برای پروژه‌های Headless مدرن تبدیل کرده است. اما این انتخاب، بدون هزینه نیست: پیچیدگی بیشتر در کش، امنیت، و Observability. تیم‌هایی که با این چالش‌ها آشنا هستند، WPGraphQL را به‌عنوان یک سرمایه‌گذاری بلندمدت می‌بینند؛ تیم‌هایی که با REST راحت‌ترند، ممکن است REST را ترجیح دهند. اگر با راهنمای کامل REST API در وردپرس آشنا هستید، می‌دانید که REST در بسیاری از سناریوها کافی است.

سه معیار برای تصمیم‌گیری:

۱. الگوی مصرف داده. اگر کلاینت‌ها نیاز به داده‌های از پیش تعریف‌شده دارند، REST کافی است. اگر نیازهای آن‌ها متنوع و متغیر است، GraphQL انعطاف بیشتری می‌دهد.

۲. استراتژی کش. اگر کش سطح CDN برای شما حیاتی است، REST ساده‌تر است. اگر می‌توانید از Persisted Queries استفاده کنید، GraphQL هم گزینه‌ای است.

۳. تجربه تیم. ابزار قدرتمند در دست تیم ناآشنا، به بدهی فنی تبدیل می‌شود. تیمی که GraphQL را عمیقاً می‌شناسد، با آن بهتر نتیجه می‌گیرد؛ تیمی که REST را می‌شناسد، ممکن است با REST سریع‌تر به نتیجه برسد.

اگر در حال ساخت یک سایت Headless با Next.js، Gatsby، یا یک اپلیکیشن موبایل هستید، WPGraphQL ارزش بررسی جدی دارد. اگر سایت شما یک وبلاگ یا فروشگاه کوچک است، REST کافی است. اگر بین این دو هستید، رویکرد ترکیبی — REST برای نوشتن، GraphQL برای خواندن — اغلب بهترین تعادل را ایجاد می‌کند.

نگاه مهندسی سطح بالا

از منظر معماری نرم‌افزار، WPGraphQL نمونه‌ای جالب از Schema-driven Development است: به‌جای تعریف Endpointهای جداگانه، یک Schema واحد به‌عنوان قرارداد بین سرور و کلاینت تعریف می‌شود و تمام تغییرات از طریق Schema منتشر می‌شوند. این رویکرد، به Contract-First API Design معروف است و مزایای روشنی دارد: تغییرات در Schema به‌صورت خودکار در ابزارهای کلاینت منعکس می‌شوند، نسخه‌بندی ساده‌تر است (با Deprecation به‌جای Breaking Change)، و مستندسازی به‌صورت زنده انجام می‌شود. اما این رویکرد، چالش‌هایی نیز دارد: Resolverها باید برای هر Field پیاده‌سازی شوند و مدیریت این Resolverها در مقیاس بزرگ نیازمند انضباط معماری است. در WPGraphQL، این انضباط با DataLoader و Deferred Resolvers تا حدی تضمین می‌شود، اما در پروژه‌های بسیار بزرگ — با هزاران Field و صدها Type سفارشی — نیازمند لایه‌های اضافی مثل Apollo Federation (در سمت کلاینت) یا Schema Stitching (در سمت سرور) است. اگر با طراحی معماری وب مقیاس‌پذیر آشنا باشید، می‌دانید که هر لایه انتزاعی، هزینه‌ای دارد و GraphQL نیز از این قاعده مستثنی نیست. با این حال، برای پروژه‌های Headless با مصرف‌کنندگان متنوع، این هزینه‌ها با مزایای انعطاف‌پذیری و تجربه توسعه‌دهنده توجیه می‌شوند.

اگر این تجربه را در یک پروژه واقعی داشته‌اید، برای علاقه‌مندی جالب است بدانید کدام بخش آن بیشترین زمان را از شما گرفت. تجربه خودتان را در دیدگاه‌ها بنویسید؛ به‌خصوص اگر راه‌حل دیگری پیدا کرده‌اید که می‌تواند برای خواننده بعدی مفید باشد. 🔍

همچنین اگر می‌خواهید در مورد پیاده‌سازی عملی GraphQL و REST بیشتر بدانید، راهنمای انتخاب GraphQL یا REST برای پروژه‌های واقعی و مفاهیم پایه API و کاربردهای آن می‌توانند مکمل خوبی باشند.