Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Extending Core Types: Using extend type Instead of Duplicating Types

Extending Core Types: Using extend type Instead of Duplicating Types

~7 Min. Lesezeit Zuletzt aktualisiert am August 9, 2026

So far, every custom query has declared a brand-new type. Just as common - in practice, even more common - is the reverse case: an existing core type such as Product, Category, or CustomerOutput should get an extra field of your own. That's what extend type is for.

Why not just create a new type?

A brand-new, custom type MironsoftProductExtra with its own field in the query would technically work, but it would force every client to fire a second, completely separate query for data that logically belongs together - exactly the underfetching problem from chapter 1 that GraphQL is supposed to solve. extend type Product { ... } instead attaches the custom field directly to the existing type, so it can be queried in the same request as every other product field.

The extend type syntax

app/code/Mironsoft/GraphqlDemo/etc/schema.graphqls
extend type Product {
    mironsoft_badge_text: String
        @resolver(class: "Mironsoft\\GraphqlDemo\\Model\\Resolver\\Product\\BadgeText")
        @doc(description: "Custom marketing badge text shown on the product tile")
}

Important: Product is itself already an extension of ProductInterface from Magento_CatalogGraphQl - Magento's own product type comes into being through the exact same pattern used here. Without the module dependency on Magento_CatalogGraphQl set in chapter 4, extend type Product would fail silently, because the base type wouldn't exist yet at load time.

Resolvers for extended fields

app/code/Mironsoft/GraphqlDemo/Model/Resolver/Product/BadgeText.php
<?php

declare(strict_types=1);

namespace Mironsoft\GraphqlDemo\Model\Resolver\Product;

use Magento\Catalog\Model\Product;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;

/**
 * Resolves the custom mironsoft_badge_text field on the Product type.
 */
class BadgeText implements ResolverInterface
{
    /**
     * Derives a marketing badge from the resolved product model.
     *
     * @param Field $field Resolved GraphQL field configuration
     * @param mixed $context Resolver context
     * @param ResolveInfo $info GraphQL resolve tree info
     * @param array|null $value Parent Product resolver's value
     * @param array|null $args Arguments passed to this field
     * @return string|null
     */
    public function resolve(
        Field $field,
        $context,
        ResolveInfo $info,
        ?array $value = null,
        ?array $args = null
    ): ?string {
        /** @var Product|null $product */
        $product = $value['model'] ?? null;

        if ($product === null || !$product->getData('is_new_arrival')) {
            return null;
        }

        return 'New arrival';
    }
}

The key difference from chapters 5 and 6: $value is now not empty. It contains whatever the parent resolver (Magento\CatalogGraphQl\Model\Resolver\Products) supplied for the product currently being processed - including the key model, under which many core resolvers make the full product model available to downstream field resolvers. Chapter 10 covers $value in detail.

Achtung: The key under which a parent object sits in the value array (model for products, different keys for other core types) isn't a fixed convention of the GraphQL specification - it's a decision made by the respective core module. Before writing your first custom field resolver on a core type, it's worth looking at the resolver that fills the parent field to find the correct array key.

Querying the extended field

query {
  products(filter: { sku: { eq: "24-MB01" } }) {
    items {
      name
      mironsoft_badge_text
    }
  }
}

To the client, mironsoft_badge_text is indistinguishable from a native core field - that's exactly the point of extend type: custom extensions blend seamlessly into existing types.

Tipp: extend type works for any type present in the merged schema - including custom-defined ones. The events project in block 4 later uses this itself, to extend its own Event type with further fields without touching the original type declaration.

Chapter 9 dives deeper into this pattern on three concrete, particularly commonly extended core types: product, category, and customer.