Module Structure
Custom GraphQL Module
app/code/Vendor/CustomGraphQl/
├── registration.php
├── etc/
│ ├── module.xml
│ ├── schema.graphqls
│ └── di.xml
├── GraphQl/
│ ├── Resolver/
│ │ ├── Query/
│ │ │ ├── ItemResolver.php
│ │ │ └── ItemListResolver.php
│ │ └── Mutation/
│ │ ├── CreateItemResolver.php
│ │ ├── UpdateItemResolver.php
│ │ └── DeleteItemResolver.php
│ └── Model/
│ └── Data/
│ └── Item.php
├── Api/
│ ├── ItemRepositoryInterface.php
│ └── Data/
│ └── ItemDataInterface.php
└── Model/
├── ItemRepository.php
└── ResourceModel/
└── Item.php
registration.php
<?php
use Magento\Framework\Component\ComponentRegistrar;
ComponentRegistrar::register(
ComponentRegistrar::MODULE,
'Vendor_CustomGraphQl',
__DIR__
);
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="Vendor_CustomGraphQl" setup_version="1.0.0">
<sequence>
<module name="Magento_GraphQl"/>
</sequence>
</module>
</config>
Schema Definition
schema.graphqls
# Query type
type Query {
# Get single item by ID
customItem(id: ID!): CustomItem
# Get items list
customItems(
filter: CustomItemFilterInput
sort: CustomItemSortInput
pageSize: Int
currentPage: Int
): CustomItemSearchResult!
}
# Mutation type
type Mutation {
# Create new item
createCustomItem(
input: CreateCustomItemInput!
): CustomItem!
# Update existing item
updateCustomItem(
input: UpdateCustomItemInput!
): CustomItem!
# Delete item
deleteCustomItem(
input: DeleteCustomItemInput!
): Boolean!
}
# Object types
type CustomItem {
id: ID!
name: String!
email: String!
status: CustomItemStatus!
description: String
created_at: DateTime
updated_at: DateTime
}
type CustomItemStatus {
code: String!
label: String!
}
type CustomItemSearchResult {
items: [CustomItem]!
total_count: Int
page_info: PageInfo
}
type PageInfo {
page_size: Int
current_page: Int
total_pages: Int
}
# Input types
input CreateCustomItemInput {
name: String!
email: String!
description: String
status: String
}
input UpdateCustomItemInput {
id: ID!
name: String
email: String
description: String
status: String
}
input DeleteCustomItemInput {
id: ID!
}
input CustomItemFilterInput {
name: FilterMatchTypeInput
email: FilterEqualTypeInput
status: FilterEqualTypeInput
}
input CustomItemSortInput {
name: SortOrder
created_at: SortOrder
}
# Enum
enum SortOrder {
ASC
DESC
}
Query Resolvers
Single Item Resolver
<?php
namespace Vendor\CustomGraphQl\GraphQl\Resolver\Query;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\GraphQl\Resolver\ResolverInterface;
use Vendor\CustomGraphQl\Api\ItemRepositoryInterface;
class ItemResolver implements ResolverInterface
{
public function __construct(
private ItemRepositoryInterface $itemRepository,
) {
}
public function resolve(
Field $field,
$context,
ResolveInfo $info,
array $value = null,
array $args = null
) {
$id = (int) $args['id'];
$item = $this->itemRepository->getById($id);
return [
'id' => $item->getId(),
'name' => $item->getName(),
'email' => $item->getEmail(),
'status' => [
'code' => $item->getStatus(),
'label' => $this->getStatusLabel($item->getStatus())
],
'description' => $item->getDescription(),
'created_at' => $item->getCreatedAt(),
'updated_at' => $item->getUpdatedAt()
];
}
private function getStatusLabel(string $code): string
{
$statuses = [
'active' => 'Active',
'inactive' => 'Inactive'
];
return $statuses[$code] ?? $code;
}
}
List Resolver
<?php
namespace Vendor\CustomGraphQl\GraphQl\Resolver\Query;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\GraphQl\Resolver\ResolverInterface;
use Vendor\CustomGraphQl\Api\ItemRepositoryInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
class ItemListResolver implements ResolverInterface
{
public function __construct(
private ItemRepositoryInterface $itemRepository,
private SearchCriteriaBuilder $searchCriteriaBuilder,
) {
}
public function resolve(
Field $field,
$context,
ResolveInfo $info,
array $value = null,
array $args = null
) {
$searchCriteria = $this->searchCriteriaBuilder->create();
// Apply filters
if (isset($args['filter'])) {
$this->applyFilters($searchCriteria, $args['filter']);
}
// Apply sorting
if (isset($args['sort'])) {
$this->applySorting($searchCriteria, $args['sort']);
}
// Apply pagination
$pageSize = $args['pageSize'] ?? 20;
$currentPage = $args['currentPage'] ?? 1;
$searchCriteria->setPageSize($pageSize);
$searchCriteria->setCurrentPage($currentPage);
$searchResult = $this->itemRepository->getList($searchCriteria);
$items = [];
foreach ($searchResult->getItems() as $item) {
$items[] = $this->formatItem($item);
}
return [
'items' => $items,
'total_count' => $searchResult->getTotalCount(),
'page_info' => [
'page_size' => $pageSize,
'current_page' => $currentPage,
'total_pages' => ceil($searchResult->getTotalCount() / $pageSize)
]
];
}
}
Mutation Resolvers
Create Resolver
<?php
namespace Vendor\CustomGraphQl\GraphQl\Resolver\Mutation;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\GraphQl\Resolver\ResolverInterface;
use Vendor\CustomGraphQl\Api\ItemRepositoryInterface;
use Vendor\CustomGraphQl\Api\Data\ItemDataInterfaceFactory;
class CreateItemResolver implements ResolverInterface
{
public function __construct(
private ItemRepositoryInterface $itemRepository,
private ItemDataInterfaceFactory $itemFactory,
) {
}
public function resolve(
Field $field,
$context,
ResolveInfo $info,
array $value = null,
array $args = null
) {
$input = $args['input'];
$item = $this->itemFactory->create();
$item->setName($input['name']);
$item->setEmail($input['email']);
$item->setDescription($input['description'] ?? null);
$item->setStatus($input['status'] ?? 'active');
$savedItem = $this->itemRepository->save($item);
return [
'id' => $savedItem->getId(),
'name' => $savedItem->getName(),
'email' => $savedItem->getEmail(),
'status' => [
'code' => $savedItem->getStatus(),
'label' => $this->getStatusLabel($savedItem->getStatus())
],
'created_at' => $savedItem->getCreatedAt()
];
}
}
Update Resolver
public function resolve(
Field $field,
$context,
ResolveInfo $info,
array $value = null,
array $args = null
) {
$input = $args['input'];
$id = (int) $input['id'];
$item = $this->itemRepository->getById($id);
if (isset($input['name'])) {
$item->setName($input['name']);
}
if (isset($input['email'])) {
$item->setEmail($input['email']);
}
if (isset($input['description'])) {
$item->setDescription($input['description']);
}
if (isset($input['status'])) {
$item->setStatus($input['status']);
}
$savedItem = $this->itemRepository->save($item);
return $this->formatItem($savedItem);
}
Delete Resolver
public function resolve(
Field $field,
$context,
ResolveInfo $info,
array $value = null,
array $args = null
) {
$id = (int) $args['input']['id'];
$item = $this->itemRepository->getById($id);
$this->itemRepository->delete($item);
return true;
}
Quiz
1. Where is the GraphQL schema defined?
2. What must resolvers implement?
3. How do you define a query in schema?
Flashcards
Question
Where is the schema file?
Click to reveal answer
Answer
etc/schema.graphqls
Question
What interface do resolvers implement?
Click to reveal answer
Answer
ResolverInterface
Question
How do you define queries?
Click to reveal answer
Answer
type Query { fieldName: ReturnType }
Question
How do you define mutations?
Click to reveal answer
Answer
type Mutation { fieldName(args): ReturnType }
Question
How do you register resolvers?
Click to reveal answer
Answer
In schema.graphqls with resolver attribute
Revision Notes
Key Takeaways
- 1. schema.graphqls defines types and operations
- 2. Resolvers implement ResolverInterface
- 3. Queries read data, mutations write data
- 4. Input types define mutation arguments
- 5. Register resolvers in schema.graphqls
Interview Tips
- • Know the module structure for GraphQL
- • Understand schema definition patterns
- • Be ready to create resolvers
- • Discuss error handling in mutations
Cheat Sheet
Module structure:
etc/schema.graphqls - Schema
GraphQl/Resolver/Query/ - Query resolvers
GraphQl/Resolver/Mutation/ - Mutation resolvers
Schema:
type Query { item(id: ID!): Item }
type Mutation { createItem(input: Input!): Item }
Resolver:
implements ResolverInterface
resolve(field, context, info, value, args)