PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

GraphQL – własny resolver, schemat, autoryzacja, testowanie w DDEV

by Henryk Tews / wtorek, 11 lutego 2020 / Opublikowano w Magento 2

Magento 2.3 wprowadziło obsługę GraphQL jako alternatywę dla REST API. O ile REST wymaga osobnych endpointów dla każdego zasobu, GraphQL pozwala klientowi samemu określić jakich danych potrzebuje w jednym zapytaniu. Pokazuję jak napisać własny resolver i udostępnić dane przez GraphQL w module Magento 2.

GraphQL vs REST w kontekście Magento 2

REST API Magento zwraca z góry określoną strukturę danych. Gdy frontend potrzebuje tylko nazwy i ceny produktu, i tak dostaje kilkadziesiąt pól. GraphQL pozwala zapytać dokładnie o to, co jest potrzebne:

# Zapytanie GraphQL - klient decyduje co dostaje
query {
    products(search: "buty", pageSize: 5) {
        items {
            name
            sku
            price_range {
                minimum_price {
                    final_price {
                        value
                        currency
                    }
                }
            }
        }
    }
}

Mniej danych przez sieć, mniej parsowania na frontendzie, jeden request zamiast kilku. To szczególnie istotne przy PWA Studio i headless commerce, gdzie frontend komunikuje się z Magento wyłącznie przez API.

Struktura modułu z własnym GraphQL

Vendor/Module/
  etc/
    module.xml
    schema.graphqls   <- definicja schematu
  Model/
    Resolver/
      Subscription.php   <- resolver
      Subscriptions.php  <- resolver kolekcji
  registration.php

Definicja schematu – schema.graphqls

type Query {
    newsletterSubscription(email: String!): SubscriptionOutput @resolver(class: "Vendor\\Module\\Model\\Resolver\\Subscription")
    newsletterSubscriptions(pageSize: Int = 20, currentPage: Int = 1): SubscriptionsOutput @resolver(class: "Vendor\\Module\\Model\\Resolver\\Subscriptions")
}

type SubscriptionOutput {
    id: Int
    email: String
    status: String
    created_at: String
}

type SubscriptionsOutput {
    items: [SubscriptionOutput]
    total_count: Int
    page_info: SearchResultPageInfo
}

input SubscribeInput {
    email: String!
    store_id: Int
}

type Mutation {
    subscribeNewsletter(input: SubscribeInput!): SubscriptionOutput @resolver(class: "Vendor\\Module\\Model\\Resolver\\Subscribe")
}

Resolver – implementacja w PHP

<?php

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;
use Magento\Framework\GraphQl\Query\Resolver\Argument\SearchCriteria\Builder;
use Vendor\Module\Api\SubscriptionRepositoryInterface;

class Subscription implements ResolverInterface
{
    public function __construct(
        private SubscriptionRepositoryInterface $subscriptionRepository
    ) {}

    public function resolve(
        Field $field,
        mixed $context,
        ResolveInfo $info,
        ?array $value = null,
        ?array $args = null
    ): array {
        $email = $args['email'] ?? null;

        if (!$email) {
            throw new \Magento\Framework\GraphQl\Exception\GraphQlInputException(
                __('Email is required')
            );
        }

        try {
            $subscription = $this->subscriptionRepository->getByEmail($email);
        } catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
            throw new \Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException(
                __('Subscription not found for email: %1', $email)
            );
        }

        return [
            'id'         => $subscription->getId(),
            'email'      => $subscription->getEmail(),
            'status'     => $subscription->getStatus(),
            'created_at' => $subscription->getCreatedAt(),
        ];
    }
}

Resolver kolekcji z paginacją

<?php

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;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Vendor\Module\Api\SubscriptionRepositoryInterface;

class Subscriptions implements ResolverInterface
{
    public function __construct(
        private SubscriptionRepositoryInterface $subscriptionRepository,
        private SearchCriteriaBuilder $searchCriteriaBuilder
    ) {}

    public function resolve(
        Field $field,
        mixed $context,
        ResolveInfo $info,
        ?array $value = null,
        ?array $args = null
    ): array {
        $pageSize    = $args['pageSize'] ?? 20;
        $currentPage = $args['currentPage'] ?? 1;

        $searchCriteria = $this->searchCriteriaBuilder
            ->setPageSize($pageSize)
            ->setCurrentPage($currentPage)
            ->create();

        $result = $this->subscriptionRepository->getList($searchCriteria);

        $items = array_map(
            fn($sub) => [
                'id'         => $sub->getId(),
                'email'      => $sub->getEmail(),
                'status'     => $sub->getStatus(),
                'created_at' => $sub->getCreatedAt(),
            ],
            $result->getItems()
        );

        return [
            'items'       => $items,
            'total_count' => $result->getTotalCount(),
            'page_info'   => [
                'page_size'    => $pageSize,
                'current_page' => $currentPage,
                'total_pages'  => (int) ceil($result->getTotalCount() / $pageSize),
            ],
        ];
    }
}

Autoryzacja w resolverach

Jeśli endpoint wymaga zalogowanego użytkownika, sprawdzasz to przez kontekst:

<?php

public function resolve(Field $field, mixed $context, ResolveInfo $info, ?array $value = null, ?array $args = null): array
{
    // Sprawdzenie czy użytkownik jest zalogowany
    if (false === $context->getExtensionAttributes()->getIsCustomer()) {
        throw new \Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException(
            __('The current customer isn\'t authorized.')
        );
    }

    $customerId = $context->getUserId();
    // ... dalsza logika
}

Testowanie GraphQL w DDEV

# Zapytanie przez curl
curl -X POST \
  https://magento2-dev.ddev.site/graphql \
  -H 'Content-Type: application/json' \
  -d '{"query":"{ products(search: \"test\", pageSize: 3) { items { name sku } } }"}'

Do wygodnego testowania GraphQL polecam Altair GraphQL Client lub wbudowany playground dostępny pod /graphql w trybie developer.

Podsumowanie

GraphQL w Magento 2 to solidna alternatywa dla REST szczególnie przy projektach headless i PWA. Schemat .graphqls jasno dokumentuje co API udostępnia, resolwery implementują dokładnie jeden kontrakt, a klient pobiera tylko potrzebne dane. Jeśli planujesz projekt z Vue Storefront lub Magento PWA Studio - GraphQL to jedyna rozsądna droga.

About Henryk Tews

Co możesz przeczytać następne

Hyvä vs Luma benchmark – twarde liczby, LCP 3.5x szybszy, k6 load test, konwersja
PageBuilder – własny typ zawartości, XML config
AI-assisted optymalizacja SQL – LLM + EXPLAIN + Blackfire, 5x przyspieszenie
  • Publikacje
  • O autorze
  • Kontakt

© 2026 Created by

GÓRA
Zarządzaj zgodą
Aby zapewnić jak najlepsze wrażenia, korzystamy z technologii, takich jak pliki cookie, do przechowywania i/lub uzyskiwania dostępu do informacji o urządzeniu. Zgoda na te technologie pozwoli nam przetwarzać dane, takie jak zachowanie podczas przeglądania lub unikalne identyfikatory na tej stronie. Brak wyrażenia zgody lub wycofanie zgody może niekorzystnie wpłynąć na niektóre cechy i funkcje.
Funkcjonalne Zawsze aktywne
Przechowywanie lub dostęp do danych technicznych jest ściśle konieczny do uzasadnionego celu umożliwienia korzystania z konkretnej usługi wyraźnie żądanej przez subskrybenta lub użytkownika, lub wyłącznie w celu przeprowadzenia transmisji komunikatu przez sieć łączności elektronicznej.
Preferencje
Przechowywanie lub dostęp techniczny jest niezbędny do uzasadnionego celu przechowywania preferencji, o które nie prosi subskrybent lub użytkownik.
Statystyka
Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do celów statystycznych. Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do anonimowych celów statystycznych. Bez wezwania do sądu, dobrowolnego podporządkowania się dostawcy usług internetowych lub dodatkowych zapisów od strony trzeciej, informacje przechowywane lub pobierane wyłącznie w tym celu zwykle nie mogą być wykorzystywane do identyfikacji użytkownika.
Marketing
Przechowywanie lub dostęp techniczny jest wymagany do tworzenia profili użytkowników w celu wysyłania reklam lub śledzenia użytkownika na stronie internetowej lub na kilku stronach internetowych w podobnych celach marketingowych.
  • Zarządzaj opcjami
  • Zarządzaj serwisami
  • Zarządzaj {vendor_count} dostawcami
  • Przeczytaj więcej o tych celach
Zobacz preferencje
  • {title}
  • {title}
  • {title}