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

Defining a First Custom Query and Writing a Matching Resolver

Defining a First Custom Query and Writing a Matching Resolver

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

This chapter fills the schema.graphqls from chapter 4 with the first custom query: shopGreeting, a deliberately simple example that shows the full path from schema through resolver to response, without the distraction of database access.

Declaring the query in the schema

app/code/Mironsoft/GraphqlDemo/etc/schema.graphqls
type Query {
    shopGreeting(
        name: String
    ): String
        @resolver(class: "Mironsoft\\GraphqlDemo\\Model\\Resolver\\ShopGreeting")
        @doc(description: "Returns a friendly greeting, optionally personalized by name")
}

type Query { ... } extends - just like extend type in block 3 - the already-existing Query type from Magento_GraphQl additively with a new field. Since Magento 2.3, type Query { ... } can be repeated across multiple modules without conflict, as long as field names stay unique - the schemas get merged.

Implementing the resolver

app/code/Mironsoft/GraphqlDemo/Model/Resolver/ShopGreeting.php
<?php

declare(strict_types=1);

namespace Mironsoft\GraphqlDemo\Model\Resolver;

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

/**
 * Resolves the shopGreeting query field.
 */
class ShopGreeting implements ResolverInterface
{
    /**
     * Builds a greeting string, optionally personalized with the given name.
     *
     * @param Field $field Resolved GraphQL field configuration
     * @param mixed $context Resolver context (store, customer, request headers)
     * @param ResolveInfo $info GraphQL resolve tree info
     * @param array|null $value Parent resolver's value, unused here
     * @param array|null $args Arguments passed to the shopGreeting field
     * @return string
     */
    public function resolve(
        Field $field,
        $context,
        ResolveInfo $info,
        ?array $value = null,
        ?array $args = null
    ): string {
        $name = $args['name'] ?? null;

        return $name !== null
            ? sprintf('Hello, %s! Welcome to Mironsoft.', $name)
            : 'Hello! Welcome to Mironsoft.';
    }
}

Notice there's no constructor dependency injection needed, because this resolver has no external dependencies. Once real data starts coming from the database (starting in chapter 13), the actual logic moves into a DataProvider class that the resolver receives via constructor property promotion - the resolver itself stays deliberately thin.

Testing the query

bin/cache-clean config
query {
  shopGreeting(name: "Team")
}
{
  "data": {
    "shopGreeting": "Hello, Team! Welcome to Mironsoft."
  }
}

Scalar return values vs. objects

shopGreeting returns a plain String - so the resolver delivers the scalar value directly, not an array. As soon as a field returns a custom GraphQL type (instead of a scalar), the resolver must instead return an associative array whose keys match the field names of the target type - chapter 6 shows exactly that case.

Tipp: @doc(description: "...") isn't cosmetic: this text shows up in every schema introspection (chapter 3) and therefore in every GraphQL client's autocomplete. A good description saves later consumers of your own API a lot of back-and-forth questions.

Chapter 6 builds on this pattern and introduces complex types as well as custom input types - the next step beyond plain scalar fields.