Extending Types as a Customer

In case you need to add fields to data types provided by another module (even OXID’s GraphQL modules) you can use the Extending a type feature in GraphQLite.

Extending output types (DataTypes)

There are two approaches to add fields to an existing DataType.

Using class inheritance

If you need to add multiple fields that all require data from the same source, extending the DataType class can be more efficient. The extended class receives all additional data as constructor arguments, so everything can be loaded in a single query before the object is created.

Note

This approach is only possible if the DataType class is not final.

Warning

Class inheritance creates a new GraphQL type (e.g. ExtendedProduct). Existing queries and mutations in the module still return the original type (e.g. Product), so the new fields will never appear in their responses. This approach is therefore only useful when you also build your own query or mutation that explicitly returns the extended type or the new created interface (see Class inheritance (automatic interface generation)). If you simply want to add fields to the responses of existing queries/mutations, use @ExtendType instead.

/** @Type() */
class ExtendedProduct extends Product
{
    public function __construct(
        private string $id,
        private string $title,
        private string $customColor,
        private float $customWeight,
    ) {
        parent::__construct($id, $title);
    }

    /** @Field */
    public function getCustomColor(): string
    {
        return $this->customColor;
    }

    /** @Field */
    public function getCustomWeight(): float
    {
        return $this->customWeight;
    }
}

Consumers access the new fields through inline fragments:

query {
    product(productId: "abc") {
        title
        ... on ExtendedProduct {
            customColor
            customWeight
        }
    }
}

Advantages:

  • All additional data can be loaded in one go (e.g. a single database query) before constructing the object, rather than one query per field.

Disadvantages:

  • The schema becomes more complex — GraphQLite automatically generates an additional interface and type (e.g. ProductInterface, Product, ExtendedProduct).

  • __typename changes from Product to ExtendedProduct, which can affect client-side caching.

  • Consumers need to use inline fragments to access the new fields.

  • Existing queries and mutations are not affected — they still return the original type, so you must build your own query or mutation that returns the extended type or their interface.

Extending input types

Unlike output types, GraphQLite does not provide an @ExtendInput annotation. There is no way to transparently add fields to an existing input type.

The available options are:

  • @Decorate — allows post-processing of an input object after creation, but does not add new fields to the GraphQL schema. Useful for validation or value transformation only.

  • New input type + new mutation — create your own input type with the additional fields and expose it through a new mutation in a new controller.

/** @Input() */
class MyCustomProductInput
{
    public string $title;
    public string $customColor;
}

class MyProductMutationController
{
    /** @Mutation() */
    public function myCustomCreateProduct(
        MyCustomProductInput $input
    ): Product {
        // your custom logic
    }
}

This means the original mutation remains unchanged, and a new mutation is added alongside it. It is not possible to modify the parameter signature of an existing mutation without overriding every query and mutation that uses the original input type.

Note

The GraphQL specification does not support interfaces on input types. PHP interfaces on input classes can still be useful for testability and shared type hints at the code level, but they will never appear in the GraphQL schema.

Best practice

Try to withstand the urge to create another module for this case and integrate the GraphQL type mapping into your existing module. That way you do not end up with an invalid state when activating/deactivating a module. Otherwise you might deactivate the module that adds the subtitle() method to the OxidEsales\Eshop\Application\Model\Article class. The subtitle field is still in your GraphQL schema, but requesting that field will result in a PHP Fatal Error.