Web API Architecture
Magento 2 provides two API interfaces: REST and GraphQL. Both are handled through the Web API area, which loads when API requests are detected.
Web API Area Files:
app/code/Vendor/Module/etc/
├── webapi.xml # REST route definitions
├── webapi_graphql/
│ └── schema.graphql # GraphQL type definitions
├── webapi_rest/
│ └── di.xml # REST-specific DI
└── webapi_graphql/
└── di.xml # GraphQL-specific DI
Service Contract Pattern:
The Web API exposes service contracts - PHP interfaces that define business operations. Service contracts decouple API logic from implementation.
app/code/Vendor/Module/Api/
├── Data/
│ └── ItemInterface.php # Data transfer object interface
├── ItemRepositoryInterface.php # CRUD operations
└── ItemManagementInterface.php # Business logic operations
When a REST request arrives:
- Area code set to
webapi_rest webapi.xmlroutes matched against the URL- Service class instantiated via DI
- Method called with parameters from request body
- Response serialized as JSON
For GraphQL:
- Area code set to
webapi_graphql - Schema parsed from
schema.graphql - Resolver class handles the query
- Response follows GraphQL response format
REST Routes with webapi.xml
REST API routes are defined in webapi.xml using a resource-based approach.
<!-- app/code/Vendor/Module/etc/webapi.xml -->
<?xml version="1.0"?>
<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd">
<!-- GET - Retrieve item -->
<route url="/V1/items/:itemId" method="GET">
<service class="Vendor\Module\Api\ItemRepositoryInterface" method="getById"/>
<resources>
<resource ref="anonymous"/>
</resources>
</route>
<!-- POST - Create item -->
<route url="/V1/items" method="POST">
<service class="Vendor\Module\Api\ItemRepositoryInterface" method="save"/>
<resources>
<resource ref="Vendor_Module::items_manage"/>
</resources>
</route>
<!-- PUT - Update item -->
<route url="/V1/items/:itemId" method="PUT">
<service class="Vendor\Module\Api\ItemRepositoryInterface" method="save"/>
<resources>
<resource ref="Vendor_Module::items_manage"/>
</resources>
</route>
<!-- DELETE - Delete item -->
<route url="/V1/items/:itemId" method="DELETE">
<service class="Vendor\Module\Api\ItemRepositoryInterface" method="deleteById"/>
<resources>
<resource ref="Vendor_Module::items_manage"/>
</resources>
</route>
<!-- Custom action -->
<route url="/V1/items/:itemId/activate" method="POST">
<service class="Vendor\Module\Api\ItemManagementInterface" method="activate"/>
<resources>
<resource ref="Vendor_Module::items_manage"/>
</resources>
</route>
</routes>
URL Parameters:
:parameterName- Required path parameter{parameterName}- Optional query parameter
Resource References:
anonymous- No authentication requiredself- User can only access own resourcesMagento\InventoryApi::...- Module-specific ACL resource
Service Contract Implementation
Service contracts are PHP interfaces that define how external systems interact with your module.
Data Interface:
<?php
namespace Vendor\Module\Api\Data;
interface ItemInterface
{
const ENTITY_ID = 'entity_id';
const NAME = 'name';
const STATUS = 'status';
const CREATED_AT = 'created_at';
public function getEntityId(): ?int;
public function setEntityId(int $id): ItemInterface;
public function getName(): ?string;
public function setName(string $name): ItemInterface;
public function getStatus(): ?int;
public function setStatus(int $status): ItemInterface;
}
Repository Interface:
<?php
namespace Vendor\Module\Api;
use Vendor\Module\Api\Data\ItemInterface;
interface ItemRepositoryInterface
{
public function getById(int $itemId): ItemInterface;
public function save(ItemInterface $item): ItemInterface;
public function delete(ItemInterface $item): bool;
public function deleteById(int $itemId): bool;
public function getList(
\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
): \Magento\Framework\Api\SearchResultsInterface;
}
Repository Implementation:
<?php
namespace Vendor\Module\Model;
use Vendor\Module\Api\ItemRepositoryInterface;
use Vendor\Module\Api\Data\ItemInterface;
use Vendor\Module\Model\ResourceModel\Item as ItemResource;
class ItemRepository implements ItemRepositoryInterface
{
private $resource;
private $itemFactory;
private $searchResultsFactory;
public function __construct(
ItemResource $resource,
\Vendor\Module\Model\ItemFactory $itemFactory,
\Magento\Framework\Api\SearchResultsFactory $searchResultsFactory
) {
$this->resource = $resource;
$this->itemFactory = $itemFactory;
$this->searchResultsFactory = $searchResultsFactory;
}
public function getById(int $itemId): ItemInterface
{
$item = $this->itemFactory->create();
$this->resource->load($item, $itemId);
if (!$item->getId()) {
throw new \Magento\Framework\Exception\NoSuchEntityException(
__('Item with id %1 does not exist.', $itemId)
);
}
return $item;
}
}
GraphQL Schema and Resolvers
GraphQL in Magento 2 uses schema definitions and resolver classes to handle queries and mutations.
GraphQL Schema:
# app/code/Vendor/Module/etc/webapi_graphql/schema.graphql
type Query {
vendorItem(id: Int!): VendorItem @resolver(class: "Vendor\\Module\\GraphQl\\ItemResolver")
vendorItems(searchCriteria: VendorItemSearchInput): VendorItemSearchResult
@resolver(class: "Vendor\\Module\\GraphQl\\ItemSearchResolver")
}
type Mutation {
createVendorItem(input: VendorItemInput!): VendorItem
@resolver(class: "Vendor\\Module\\GraphQl\\ItemCreateResolver")
}
type VendorItem {
id: Int!
name: String!
status: Int!
created_at: String
}
input VendorItemInput {
name: String!
status: Int
}
input VendorItemSearchInput {
page_size: Int
current_page: Int
}
type VendorItemSearchResult {
items: [VendorItem!]!
total_count: Int
}
Resolver Class:
<?php
namespace Vendor\Module\GraphQl;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Vendor\Module\Api\ItemRepositoryInterface;
class ItemResolver
{
private $itemRepository;
public function __construct(ItemRepositoryInterface $itemRepository)
{
$this->itemRepository = $itemRepository;
}
public function resolve(
Field $field,
$context,
ResolveInfo $info,
array $value = null,
array $args = null
) {
$item = $this->itemRepository->getById($args['id']);
return [
'id' => $item->getEntityId(),
'name' => $item->getName(),
'status' => $item->getStatus(),
'created_at' => $item->getCreatedAt(),
];
}
}
GraphQL queries:
query {
vendorItem(id: 1) {
id
name
status
}
}
Quiz
1. Which file defines REST API routes in Magento 2?
2. What does the 'anonymous' resource reference mean in webapi.xml?
3. What naming convention do GraphQL schema files follow?
Flashcards
Question
What is the URL format for Magento 2 REST APIs?
Click to reveal answer
Answer
/rest/{storeCode}/V1/{resource} (e.g., /rest/default/V1/items/1)
Question
What interface must repository classes implement?
Click to reveal answer
Answer
Repository interfaces in the module's Api/ directory (e.g., ItemRepositoryInterface)
Question
How do GraphQL resolvers reference schema types?
Click to reveal answer
Answer
Via @resolver(class: "Namespace\\Module\\GraphQl\\ResolverClass") annotation
Question
What does setup:di:compile generate for service contracts?
Click to reveal answer
Answer
Proxy and factory classes for all service interfaces
Revision Notes
Key Takeaways
- 1. Web API uses service contracts (interfaces) to define API methods
- 2. REST routes defined in etc/webapi.xml with resource-based access control
- 3. GraphQL uses schema.graphql files and resolver classes
- 4. Service contracts decouple API from implementation details
- 5. anonymous resource allows unauthenticated access
- 6. Both REST and GraphQL share the same service layer
Interview Tips
- • Explain the service contract pattern and why it's important
- • Describe how to create a custom REST API endpoint
- • Discuss the difference between REST and GraphQL approaches
- • Explain how authentication works in Magento APIs
- • Know the webapi.xml structure and resource references
Cheat Sheet
Web API Cheat Sheet
REST Config: etc/webapi.xml
GraphQL Config: etc/webapi_graphql/schema.graphql
Service Layer: Api/ directory interfaces
Route Pattern:
<route url="/V1/items/:id" method="GET">
<service class="Vendor\Module\Api\ItemRepositoryInterface" method="getById"/>
<resources><resource ref="anonymous"/></resources>
</route>
GraphQL Query:
query { vendorItem(id: 1) { id name } }
Access Levels:
- anonymous → No auth
- self → Own resources
- ACL resource → Permission required