This website uses cookies

Our website, platform and/or any sub domains use cookies to understand how you use our services, and to improve both your experience and our marketing relevance.

How to Create and Use Custom GraphQL in Magento 2

Updated on April 30, 2026

14 Min Read
How to Create and Use Custom GraphQL in Magento 2

Key Takeaways

  • GraphQL gives developers more control over the data returned, which helps build faster and more flexible frontends.
  • A custom GraphQL module in Magento 2 gives you a practical way to extend the API beyond the built-in queries.
  • A custom GraphQL setup in Magento 2 works only when the schema, resolver, and response structure all stay in sync.

Magento 2 is a powerful ecommerce platform with many built-in features. However, there may be times when you need to extend the platform to meet specific business requirements. This is where custom GraphQL comes in.

If you’re unfamiliar with GraphQL, it’s a query language for APIs developed by Facebook (now Meta). It allows developers to request only the data they need. Magento 2 supports GraphQL for querying store data through a more structured API layer.

In this article, I’ll show you how to use GraphQL in Magento 2, from running basic queries to creating a custom GraphQL module with your own schema and resolver.

Overview of GraphQL in Magento 2

GraphQL is an alternative to REST and SOAP in Magento 2. It allows developers to query and mutate data using a standardized syntax. It makes API responses more streamlined and helps reduce network overhead. A common example is a Progressive Web App (PWA) with client side rendering.

Magento 2 GraphQL mainly uses two operations:

  • Queries: For reading and retrieving data.
  • Mutations: For taking actions, creating data, and updating information, such as a customer’s email.

Experience the Cloudways Magento 2 Demo Store – No tech skills needed!

Experience a fully functional Magento 2 store built on top of renowned Cloudways hosting to deliver the fastest speeds.

​​Recent GraphQL Improvements in Magento 2

Magento has continued to improve GraphQL support across recent releases of both Magento Open Source and Adobe Commerce. Magento Open Source is the free version of Magento, while Adobe Commerce is the paid version that includes additional enterprise and B2B features.

In Magento Open Source 2.4.6, category tree rendering and bulk cart operations were improved for better query performance. In Adobe Commerce 2.4.6, purchase order functionality was fully exposed in the GraphQL layer by adding approval rules to the API.

Magento Open Source 2.4.7 and Adobe Commerce 2.4.7 increased GraphQL coverage for custom attributes and GraphQL resolver caches.

In Magento Open Source 2.4.8, support was added for custom scalar types in GraphQL schemas, which makes it easier to define and validate values such as emails, URLs, dates, and other custom data types. Adobe Commerce 2.4.8 also included GraphQL fixes around customer queries and resolver cache invalidation, which help make GraphQL responses more reliable when customer data or custom attributes change.

Benefits of GraphQL for Developers

Magento 2 GraphQL is useful for developers for the following reasons.

  • Developers can request only the data they need, which results in smaller and more efficient data transfers. Instead of pulling a full response and using only part of it, they can ask for exactly what the frontend needs. This is especially important for mobile and low-bandwidth devices.
  • It works well when a page needs data from several places at once. A product page, for example, may need the product name, price, stock status, related items, and customer-specific cart details in the same flow. GraphQL lets developers shape that response around the page and makes it easier to build complex data-driven applications.
  • GraphQL uses a structured and readable query format, which makes it easier to understand once developers get familiar with it.
  • Developers can create custom GraphQL queries, mutations, and types, allowing them to extend the platform’s default schema and create custom functionality that meets their specific requirements.
  • It supports optimized queries, which can significantly improve application performance. This is because it reduces the number of API requests required to fetch data.

Don’t let outdated APIs hold your Magento store back!

Sign up for Cloudways and experience the power of faster and more efficient data retrieval with GraphQL.

GraphQL vs REST API

Magento 2 supports both GraphQL and REST APIs for working with store data. While both serve the same general purpose, they differ in how requests are made, how responses are structured, and how developers work with them day to day.

GraphQL vs REST API comparison

Here are some of the key differences between REST API and GraphQL.

GraphQL REST API
Querying With GraphQL, you can retrieve all the data you need in a single request and specify which fields you want. With REST APIs, you typically make separate requests for each resource or endpoint you want to access.
Caching With GraphQL, caching can be more challenging because each request is unique and may require a different data set. REST APIs are often easier to cache because URLs can be used as cache keys.
Learning Curve GraphQL has a steeper learning curve than REST because developers need to understand schemas, types, and query structure before using it effectively. REST APIs are simpler to understand and use, and there is a wide range of documentation available.
Performance GraphQL can be more efficient for some use cases because it reduces over-fetching and under-fetching of data. REST APIs can be faster when only a small amount of data is required.

The better choice comes down to your use case. GraphQL gives developers more control over the response data, which can be helpful in headless storefronts and more dynamic frontend builds. REST is often easier to work with in traditional setups where fixed endpoints are enough.

Security features like authentication, authorization, rate limiting, and input limiting are handled at the Magento platform level, not by GraphQL or REST alone.

Requirements for GraphQL in Magento 2

You need a GraphQL IDE such as GraphiQL or a browser extension to run the code samples in this article. These tools are needed because GraphQL requests are usually sent in a structured format, so a regular browser window is not enough for proper testing in most cases.If you install a browser extension, make sure it supports request headers. That matters because some Magento GraphQL requests may need headers for authentication or store-specific context. Altair GraphQL Client is one of the extensions on the Chrome Web Store that can do the job.

Note: For this article, I’m using a Magento store hosted on Cloudways. Reliable Magento hosting can help keep GraphQL responses fast and consistent, especially as your store grows.

Access the GraphQL Endpoint in Magento 2

The GraphQL endpoint in Magento 2 is /graphql. This is the URL where Magento receives GraphQL requests and returns the response. To access it, enter the following URL in your GraphQL IDE or browser extension:

http://<magento2-server>/graphql

Native Magento GraphQL query example

Request:

The following query asks Magento to return a list of countries and their available region details.

{

 countries {

   available_regions {

     code

     id

     name

   }

   full_name_locale

   full_name_english

   id

   two_letter_abbreviation

   three_letter_abbreviation

 }

}

Response:

The response returns the requested country data, including country codes, names, and region information where available.

{

  "data": {

    "countries": [

      {

        "available_regions": null,

        "full_name_english": "Andorra",

        "full_name_locale": "Andorra",

        "id": "AD",

        "three_letter_abbreviation": "AND",

        "two_letter_abbreviation": "AD"

      },

      {

        "available_regions": null,

        "full_name_english": "United Arab Emirates",

        "full_name_locale": "United Arab Emirates",

        "id": "AE",

        "three_letter_abbreviation": "ARE",

        "two_letter_abbreviation": "AE"

      },

      {

        "available_regions": null,

        "full_name_english": "Antigua & Barbuda",

        "full_name_locale": "Antigua & Barbuda",

        "id": "AG",

        "three_letter_abbreviation": "ATG",

        "two_letter_abbreviation": "AG"

      }

    ]

  }

}

The following example uses the Altair GraphQL client to fetch country data and display the JSON response.

Native GraphQL query example in Altair showing a countries query on the left and JSON response on the right.

Run GraphQL Requests in Magento 2

When making a GraphQL request in Magento, you can use both HTTP GET and POST methods for queries. Mutations should be sent with POST only. In simple terms, queries are used to read data, while mutations are used to create, update, or change data. You can also send a GET query in the URL.

For example:

http://<host>/graphql?query=%7Bproducts%7Bitems%7Bname,sku%7D%7D%7D

The query in this example is URL-encoded because it is being sent directly in the URL. Characters like { and } are converted into encoded values such as %7B and %7D.

Native Magento GraphQL query example

Request:

The following query asks Magento to return the product with SKU 24-WB01 and only show its name and sku.

{

 products(

   filter: { sku: { eq: "24-WB01" } }

 ) {

   items {

     name

     sku

   }

 }

}

Response:

The response returns the matching product data Magento found for that SKU.

{

 "data": {

   "products": {

     "items": [

       {

         "name": "Voyage Yoga Bag",

         "sku": "24-WB01"

       }

     ]

   }

 }

}

Create Custom GraphQL in Magento 2

So far, we’ve looked at Magento’s built in GraphQL support. If you want to go beyond the default queries and add your own schema, resolver, and output, you’ll need to create a custom module. This is required because Magento does not automatically know about your custom query or the logic behind it.

This part is more advanced and is meant for developers who are already comfortable working with Magento 2 modules and basic GraphQL concepts.

Follow these steps to create a custom GraphQL module in Magento 2.

Before you begin, create a folder named Cloudways/Graphql inside app/code.

GraphQL File Structure

Step 1: Create a registration.php File

This file is required because Magento will not recognize your custom module unless it is registered first. In simple terms, registration.php tells Magento that the Cloudways_Graphql module exists and should be included when Magento loads available modules.

Without this file, Magento will ignore the module folder completely, even if the rest of the files are present.

Create a registration.php file at: app/code/Cloudways/Graphql/registration.php

<?php

use Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(

   ComponentRegistrar::MODULE,

   'Cloudways_Graphql',

   __DIR__

);

Step 2: Create an etc/module.xml File

This file defines the basic information about your module, such as its name, version, and dependencies. Magento uses module.xml to identify the module and load it correctly during setup and compilation.

Create a module.xml file at: app/code/Cloudways/Graphql/etc/module.xml

<?xml version="1.0"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">

   <module name="Cloudways_Graphql" setup_version="1.0.0">

       <sequence>

           <module name="Magento_Catalog"/>

           <module name="Magento_GraphQl"/>

       </sequence>

   </module>

</config>

Step 3: Define the GraphQL Schema

A GraphQL schema defines the types, queries, and mutations your API will support.

Create a file at: app/code/Cloudways/Graphql/etc/schema.graphqls

This file defines:

  • the structure of your queries and mutations.
  • the input and output types used by those queries.
  • the resolvers that handle the request.
  • which query results can be cached.

This file acts as the entry point for your custom GraphQL API in Magento. When a GraphQL request comes in, Magento reads this schema to understand which operations are available, what data should be returned, and which resolver class should process the request.

Without schema.graphqls, Magento has no GraphQL definition for your module. That means your custom query or mutation will not exist in the API, even if the resolver class has already been created.

Custom GraphQL module example

For this tutorial, we’ll create a custom query called customProducts. It will return a paginated list of products with a few basic fields.

Use the following schema in schema.graphqls:

type Query {

   customProducts(

       pageSize: Int = 20

       currentPage: Int = 1

   ): CustomProductsOutput

   @resolver(class: "Cloudways\\Graphql\\Model\\Resolver\\Products")

   @cache(cacheIdentity: "Cloudways\\Graphql\\Model\\Resolver\\Block\\Identity")

}

type CustomProductsOutput {

   items: [CustomProductItem]

   total_count: Int

   page_info: SearchResultPageInfo

}

type CustomProductItem {

   id: Int

   name: String

   sku: String

   price: Float

}

type SearchResultPageInfo {

   page_size: Int

   current_page: Int

   total_pages: Int

}

Define Query

A query definition can be simple or more detailed, depending on what you need.

You can define a generic custom query like this:

type Query {

  myCustomQuery(input: MyCustomInput!): MyCustomOutput

    @resolver(class: "Vendor\\Module\\Model\\Resolver\\MyCustomQuery")

}

You can also create a simple custom query like this:

type Query {

   hello: String

}

Define Input Parameters

In this schema, the customProducts query accepts pageSize and currentPage as input parameters.

  • pageSize: Controls how many products are returned.
  • currentPage: Controls which page of results is loaded.

Define Output Attributes

In a schema.graphqls file, the output type defines the top level fields returned by the query. Each field must have a data type, whether it is a scalar, object, or array.

In the example above, CustomProductsOutput is the top level response type. It returns:

  • items: Returns the list of products.
  • total_count: Returns the total number of products.
  • page_info: Returns pagination details.

Each object inside items follows the CustomProductItem type. The CustomProductItem type defines the product fields returned for each item:

  • id:  The unique identifier of the product.
  • name: The name of the product.
  • sku: The stock keeping unit used to identify the product internally.
  • price: The product price returned in the query response.

The SearchResultPageInfo type defines the pagination fields:

  • page_size: The number of products returned per page.
  • current_page: The current page of results being viewed.
  • total_pages: The total number of pages available in the result set.

For example, page_info follows the SearchResultPageInfo type.

Response Example

The following example shows the response returned by the customProducts query:

{

  "data": {

    "customProducts": {

      "total_count": 3,

      "page_info": {

        "page_size": 20,

        "current_page": 1,

        "total_pages": 1

      },

      "items": [

        {

          "id": 1,

          "name": "Strive Shoulder Pack",

          "sku": "24-MB04",

          "price": 32

        },

        {

          "id": 2,

          "name": "Voyage Yoga Bag",

          "sku": "24-WB01",

          "price": 45

        },

        {

          "id": 3,

          "name": "Fusion Backpack",

          "sku": "24-MB02",

          "price": 59

        }

      ]

    }

  }

}

Define Annotations

You can describe an attribute, type, or field in a schema.graphqls file by using the @doc annotation.

Example:

sku: String @doc(description: "A number or code assigned to identify the product.")

Step 4: Query Caching

The @cache directive defines whether the result of a query can be cached. Queries related to products, categories, and CMS content may be cached.

In the schema above, caching is defined like this:

@cache(cacheIdentity: "Cloudways\\Graphql\\Model\\Resolver\\Block\\Identity")

The cache identity points to the class responsible for returning cache tags. A query without a cache identity will not be cached.

This is why the Identity.php file needs to be created. When you mark a GraphQL query as cacheable, Magento needs a class that tells it which cache tag should be attached to the response. That tag is then used to store the cached result and clear it when the related data changes.

Without this identity class, Magento has no way to connect the cached query result to a cache tag, so the query will not participate in the caching flow even if the @cache directive is present.

Create the following file:

app/code/Cloudways/Graphql/Model/Resolver/Block/Identity.php

<?php

declare(strict_types=1);

namespace Cloudways\Graphql\Model\Resolver\Block;

use Magento\Framework\GraphQl\Query\Resolver\IdentityInterface;

class Identity implements IdentityInterface

{

   private string $cacheTag = 'CLOUDWAYS_CUSTOM_PRODUCTS';

   public function getIdentities(array $resolvedData): array

   {

       return [$this->cacheTag];

   }

}

This class implements Magento’s IdentityInterface and returns the cache tags for the resolved query data. In this example, it returns the custom tag CLOUDWAYS_CUSTOM_PRODUCTS. That means Magento will treat the query response as part of that cache group. If that cache tag is invalidated later, Magento can refresh the related GraphQL cache as well.

Step 5: Define the Schema Resolver

Resolvers are responsible for handling GraphQL queries and mutations. In Magento 2, resolvers are defined as PHP classes. You can place them in a directory such as Model/Resolver.

A resolver is required because defining a query in schema.graphqls only tells Magento that the query exists. The schema defines the query name, its inputs, and the type of response it should return, but it does not contain the logic to fetch that data. The resolver handles that part. When the query is called, the resolver reads the input values, fetches the required data, and returns it in the format defined in the schema.

Create the following file:

app/code/Cloudways/Graphql/Model/Resolver/Products.php

Custom GraphQL module resolver example

<?php

declare(strict_types=1);

namespace Cloudways\Graphql\Model\Resolver;

use Magento\Catalog\Api\ProductRepositoryInterface;

use Magento\Framework\Api\SearchCriteriaBuilder;

use Magento\Framework\GraphQl\Config\Element\Field;

use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;

use Magento\Framework\GraphQl\Query\ResolverInterface;

use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;

use Exception;

class Products implements ResolverInterface

{

   private ProductRepositoryInterface $productRepository;

   private SearchCriteriaBuilder $searchCriteriaBuilder;

   public function __construct(

       ProductRepositoryInterface $productRepository,

       SearchCriteriaBuilder $searchCriteriaBuilder

   ) {

       $this->productRepository = $productRepository;

       $this->searchCriteriaBuilder = $searchCriteriaBuilder;

   }

   public function resolve(

       Field $field,

       $context,

       ResolveInfo $info,

       array $value = null,

       array $args = null

   ): array {

       $pageSize = $args['pageSize'] ?? 20;

       $currentPage = $args['currentPage'] ?? 1;

       try {

           $searchCriteria = $this->searchCriteriaBuilder

               ->setPageSize($pageSize)

               ->setCurrentPage($currentPage)

               ->create();

           $searchResults = $this->productRepository->getList($searchCriteria);

           $products = $searchResults->getItems();

           $items = [];

           foreach ($products as $product) {

               $items[] = [

                   'id' => (int) $product->getId(),

                   'sku' => $product->getSku(),

                   'name' => $product->getName(),

                   'price' => (float) $product->getPrice(),

               ];

           }

           $totalCount = (int) $searchResults->getTotalCount();

           $totalPages = $pageSize > 0 ? (int) ceil($totalCount / $pageSize) : 1;

           return [

               'items' => $items,

               'total_count' => $totalCount,

               'page_info' => [

                   'page_size' => $pageSize,

                   'current_page' => $currentPage,

                   'total_pages' => $totalPages,

               ],

           ];

       } catch (Exception $e) {

           throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e);

       }

   }

}

In this example, the resolver is connected to the customProducts query through the @resolver directive in schema.graphqls. The array returned by the resolver must match the fields defined in the GraphQL schema. When the query runs, Magento calls the resolve() method in this class. That method reads the pagination values, loads the products, and returns them in the structure defined for CustomProductsOutput.

Step 6: Run the Commands

Run the following commands from the root directory of your Magento 2 instance to enable the module and refresh the generated code and cache:

bin/magento module:enable Cloudways_Graphql

bin/magento setup:upgrade

bin/magento setup:di:compile

bin/magento cache:clean

These commands make Magento recognize the new module and load the GraphQL files correctly. They enable the module, update Magento’s setup, refresh generated code, and clear old cache. Without running them, the custom query may not appear or work properly.

Step 7: Test and Run the Custom GraphQL Query

The final step is to test the custom query and make sure it returns the expected response. You can do this with tools such as GraphiQL or Altair GraphQL Client.

Magento 2 GraphQL Request Lifecycle

This step helps you confirm that the module is working correctly. If the query runs as expected, it means Magento has loaded the schema, connected the resolver, and returned the data in the right format. It can also help you spot setup or response errors early.

Custom GraphQL module query example

Request Payload:

{

 customProducts(

   pageSize: 20

   currentPage: 1

 ) {

   total_count

   page_info {

     page_size

     current_page

     total_pages

   }

   items {

     id

     name

     sku

     price

   }

 }

}

Response:

The response returns a list of products with pagination details.

{

 "data": {

   "customProducts": {

     "total_count": 1,

     "page_info": {

       "page_size": 20,

       "current_page": 1,

       "total_pages": 1

     },

     "items": [

       {

         "id": 1,

         "name": "Strive Shoulder Pack",

         "sku": "24-MB04",

         "price": 32

       }

     ]

   }

 }

}

Create a GraphQL Mutation

The custom module example above focused on a custom query. Magento also supports native GraphQL mutations for creating or updating data.

Query vs Mutation comparison

A mutation is a GraphQL operation used to change data. Unlike a query, which is used to fetch information, a mutation is used when you want Magento to perform an action. In Magento, mutations can be used to create new entities, update existing ones, or perform actions such as adding items to a cart.

Native Magento mutation example

The following mutation creates a new customer account:

mutation {

 createCustomer(

   input: {

     email: "[email protected]"

     firstname: "John"

     lastname: "Doe"

     password: "Admin1234@"

   }

 ) {

   customer {

     firstname

     lastname

     email

   }

 }

}

Response:

{

 "data": {

   "createCustomer": {

     "customer": {

       "firstname": "John",

       "lastname": "Doe",

       "email": "[email protected]"

     }

   }

 }

}

In this example, the mutation creates a new customer account with the specified email, first name, last name, and password. The response includes the new customer’s first name, last name, and email address.

This means the request is not only fetching data from Magento. It is sending new data and asking Magento to create a new record. After the account is created, Magento returns part of the customer data so you can verify that the mutation was successful.

Final Thoughts

By using GraphQL in Magento 2, you can create APIs that are more efficient and flexible than traditional REST APIs. This is because GraphQL allows developers to request exactly what data they need, instead of receiving a fixed response that may include extra data they do not use.

In addition to performance benefits, custom GraphQL modules can also make development more straightforward. They give developers more control over the data structure and make it easier to understand how to query the API and return the required data. This can reduce the time and effort needed to build integrations and applications on top of your Magento store.

If your project needs more than Magento’s built in GraphQL queries, creating a custom GraphQL module gives you a practical way to extend the API based on your own requirements. Just make sure your schema, resolver, and response structure stay aligned, so the implementation works as expected.

Frequently Asked Questions

Q1: What is the main purpose of GraphQL?

The main purpose of GraphQL is to let clients ask for exactly the data they need from an API, in a predictable structure. It uses a typed schema to describe what data and operations are available, which makes APIs more flexible for apps that need different combinations of data.

Q2: Why use GraphQL instead of REST?

GraphQL is often used instead of REST when the frontend needs more control over the response shape. A single GraphQL query can fetch related data in one request, which can reduce extra round trips and help avoid getting fields the app does not need. That is especially useful for complex UIs, headless builds, and apps with multiple client types. REST can still be the better choice when you want simpler endpoints and easier HTTP caching.

Q3: What are the disadvantages of GraphQL?

GraphQL can be harder to learn and work with than REST because it introduces schemas, types, and custom query structure. Caching can also take more setup, since requests are not always tied to simple URLs the way REST requests often are. And if queries are not designed carefully, GraphQL can become harder to debug and maintain, especially when query size, aliases, and pagination are not controlled properly.

Q4: Is GraphQL more secure than REST?

No, not by default. Choosing GraphQL over REST does not automatically make an API safer. The level of security depends on how the API is configured, protected, and managed. In Magento, those protections are applied through the platform itself rather than being built into GraphQL or REST by default.

Share your opinion in the comment section. COMMENT NOW

Share This Article

Nisha Thomas

Nisha is a technical content writer with a passion for translating complex technology into content that’s clear, practical, and enjoyable to read. With strong technical insight and a user-first mindset, she crafts guides that help readers understand and use modern tools and platforms.

×

Webinar: How to Get 100% Scores on Core Web Vitals

Join Joe Williams & Aleksandar Savkovic on 29th of March, 2021.

Do you like what you read?

Get the Latest Updates

Share Your Feedback

Please insert Content

Thank you for your feedback!

Do you like what you read?

Get the Latest Updates

Share Your Feedback

Please insert Content

Thank you for your feedback!

Want to Experience the Cloudways Platform in Its Full Glory?

Take a FREE guided tour of Cloudways and see for yourself how easily you can manage your server & apps on the leading cloud-hosting platform.

Start my tour