一键导入
api-graphql-resolver
Implement Magento 2 GraphQL resolvers — define schema.graphqls types, write query and mutation resolvers, and handle authorization
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Implement Magento 2 GraphQL resolvers — define schema.graphqls types, write query and mutation resolvers, and handle authorization
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Build Magento 2 admin grids using UI Component listing XML, data providers, and admin controllers with proper ACL integration
Run Magento 2 CLI commands through Warden's Docker environment: warden shell, bin/magento, composer, Redis/Valkey, Varnish, OpenSearch, RabbitMQ, n98-magerun2, mutagen sync, and env lifecycle.
Adapt Luma-oriented Magento 2 modules to work with Hyva themes — replace RequireJS and Knockout with Alpine.js, Tailwind CSS, and ViewModels
Configure Magento 2 dependency injection — preferences, virtual types, constructor arguments, and proxy generation via di.xml
Implement Magento 2 plugins (interceptors) following best practices for before, after, and around method interception
Create a new Magento 2 module with the required directory structure, registration files, composer autoloading, and module sequence declarations
| name | api-graphql-resolver |
| description | Implement Magento 2 GraphQL resolvers — define schema.graphqls types, write query and mutation resolvers, and handle authorization |
| installed_version | 1.0.0 |
| magehub_version | 0.1.13 |
Magento's GraphQL layer maps schema fields to PHP resolver classes. The
framework merges all etc/schema.graphqls files from enabled modules into a
single schema, then dispatches incoming queries to the resolver class specified
in the @resolver directive.
Define types, queries, and mutations in etc/schema.graphqls. Extend existing
Magento types using extend type Query or extend type Mutation. Use
@resolver(class:) to bind each field to a PHP class. Input types group
mutation arguments into a single typed object for cleaner signatures.
Implement Magento\Framework\GraphQl\Query\ResolverInterface for query
fields. The resolve() method receives the field, context, resolution info,
and arguments. Return an associative array matching the declared GraphQL type
fields. Keep business logic in service classes — resolvers should only
translate between GraphQL arguments and service method calls.
Mutation resolvers follow the same interface but perform write operations.
Validate input arguments early and throw GraphQlInputException for user
errors. Wrap the service call and return the created or modified entity data
as an associative array matching the mutation return type.
Check $context->getExtensionAttributes()->getIsCustomer() for customer-only
fields. Throw GraphQlAuthorizationException when access is denied. For
admin-only fields, use bearer token authentication and verify the admin
context.
Use GraphQlInputException for validation errors, GraphQlNoSuchEntityException
for missing resources, and GraphQlAuthorizationException for permission
failures. These exception types produce structured GraphQL error responses
with appropriate HTTP status codes.
GraphQL schema defining a custom type, a query field with resolver binding, and a mutation with input type
type Query {
customEntity(id: Int! @doc(description: "Entity ID")): CustomEntity
@resolver(class: "Vendor\\Module\\Model\\Resolver\\CustomEntity")
@doc(description: "Retrieve a custom entity by ID")
}
type Mutation {
createCustomEntity(input: CustomEntityInput!): CustomEntity
@resolver(class: "Vendor\\Module\\Model\\Resolver\\CreateCustomEntity")
@doc(description: "Create a new custom entity")
}
type CustomEntity @doc(description: "Custom entity type") {
id: Int @doc(description: "Entity ID")
name: String @doc(description: "Entity name")
is_active: Boolean @doc(description: "Active status")
created_at: String @doc(description: "Creation timestamp")
}
input CustomEntityInput
@doc(description: "Input for creating a custom entity") {
name: String! @doc(description: "Entity name")
is_active: Boolean @doc(description: "Active status, defaults to true")
}
Resolver class that fetches a single entity by ID, delegates to a repository, and returns the typed result array
<?php
declare(strict_types=1);
namespace Vendor\Module\Model\Resolver;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Vendor\Module\Api\EntityRepositoryInterface;
class CustomEntity implements ResolverInterface
{
public function __construct(
private readonly EntityRepositoryInterface $entityRepository
) {
}
public function resolve(
Field $field,
$context,
ResolveInfo $info,
?array $value = null,
?array $args = null
): array {
if (!isset($args['id'])) {
throw new GraphQlInputException(__('Entity ID is required.'));
}
try {
$entity = $this->entityRepository->getById((int) $args['id']);
} catch (NoSuchEntityException $e) {
throw new GraphQlNoSuchEntityException(
__('Entity with ID "%1" does not exist.', $args['id'])
);
}
return [
'id' => (int) $entity->getId(),
'name' => $entity->getName(),
'is_active' => (bool) $entity->getIsActive(),
'created_at' => $entity->getCreatedAt(),
];
}
}
Resolver that validates input, delegates entity creation to a service class, and returns the new entity data
<?php
declare(strict_types=1);
namespace Vendor\Module\Model\Resolver;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Vendor\Module\Api\EntityRepositoryInterface;
use Vendor\Module\Api\Data\EntityInterfaceFactory;
class CreateCustomEntity implements ResolverInterface
{
public function __construct(
private readonly EntityRepositoryInterface $entityRepository,
private readonly EntityInterfaceFactory $entityFactory
) {
}
public function resolve(
Field $field,
$context,
ResolveInfo $info,
?array $value = null,
?array $args = null
): array {
$input = $args['input'] ?? [];
if (empty($input['name'])) {
throw new GraphQlInputException(__('Name is required.'));
}
$entity = $this->entityFactory->create();
$entity->setName($input['name']);
$entity->setIsActive($input['is_active'] ?? true);
$saved = $this->entityRepository->save($entity);
return [
'id' => (int) $saved->getId(),
'name' => $saved->getName(),
'is_active' => (bool) $saved->getIsActive(),
'created_at' => $saved->getCreatedAt(),
];
}
}
Embedding repository calls, data formatting, and business rules directly in the resolver: Fat resolvers become untestable and cannot share logic with REST endpoints. Changes to business rules require modifying the transport layer, increasing the risk of regressions in API behavior. Solution: Extract business logic into service classes injected via the constructor. The resolver should only translate between GraphQL arguments and service method parameters.
Returning raw model objects instead of typed arrays from resolve(): Magento's GraphQL framework expects associative arrays keyed by the GraphQL type field names. Returning model objects causes null values for all fields because the framework cannot map object properties to the schema. Solution: Always return an associative array matching the schema: return [ 'id' => (int) $entity->getId(), 'name' => $entity->getName(), ];
Missing authorization checks in resolvers that handle customer or admin data: Without explicit authorization checks, guest users can access customer data and unauthenticated requests can trigger admin operations. GraphQL introspection reveals the available fields, making unprotected resolvers easy to exploit. Solution: Check authentication context at the start of resolve(): if (false === $context->getExtensionAttributes()->getIsCustomer()) { throw new GraphQlAuthorizationException(__('Customer login required.')); }
Path template:
etc/schema.graphqls
GraphQL schema defining custom types, query fields, and mutations with resolver bindings
type Query {
{{entityName}}(id: Int! @doc(description: "Entity ID")): {{typeName}}
@resolver(class: "{{vendor}}\\{{module}}\\Model\\Resolver\\{{typeName}}")
@doc(description: "Retrieve {{entityName}} by ID")
}
type {{typeName}} @doc(description: "{{typeName}} entity type") {
id: Int @doc(description: "Entity ID")
name: String @doc(description: "Entity name")
}
Path template:
Model/Resolver/{{typeName}}.php
Query resolver class implementing ResolverInterface
<?php
declare(strict_types=1);
namespace {{vendor}}\{{module}}\Model\Resolver;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
class {{typeName}} implements ResolverInterface
{
public function resolve(
Field $field,
$context,
ResolveInfo $info,
?array $value = null,
?array $args = null
): array {
// Delegate to service class and return typed array
return [];
}
}