How to create a GraphQL Endpoint for Magento 2.3
Tags: magento, magento2, graphql
Published: 2019-11-03 19:45:59

---
title: How to create a GraphQL Endpoint for Magento 2.3
published: true
date: 03-11-2019
tags: magento,magento2,GraphQL
canonical_url: https://larsroettig.dev/how-to-create-a-graph-ql-endpoint-for-magento-2-3
---

<picture>
        <source srcset="https://larsroettig.dev/static/5138abb2995d746ba613de2a24c05826/85df1/store-984393_1920.webp 250w,https://larsroettig.dev/static/5138abb2995d746ba613de2a24c05826/b1e7c/store-984393_1920.webp 500w,https://larsroettig.dev/static/5138abb2995d746ba613de2a24c05826/de8c6/store-984393_1920.webp 1000w,https://larsroettig.dev/static/5138abb2995d746ba613de2a24c05826/64b80/store-984393_1920.webp 1500w,https://larsroettig.dev/static/5138abb2995d746ba613de2a24c05826/3c602/store-984393_1920.webp 1920w" sizes="(max-width: 1000px) 100vw, 1000px" type="image/webp"></source>
        <source srcset="https://larsroettig.dev/static/5138abb2995d746ba613de2a24c05826/8768e/store-984393_1920.jpg 250w,https://larsroettig.dev/static/5138abb2995d746ba613de2a24c05826/08e51/store-984393_1920.jpg 500w,https://larsroettig.dev/static/5138abb2995d746ba613de2a24c05826/0f6c6/store-984393_1920.jpg 1000w,https://larsroettig.dev/static/5138abb2995d746ba613de2a24c05826/ddbde/store-984393_1920.jpg 1500w,https://larsroettig.dev/static/5138abb2995d746ba613de2a24c05826/d57f7/store-984393_1920.jpg 1920w" sizes="(max-width: 1000px) 100vw, 1000px" type="image/jpeg"></source>
        <img src="https://larsroettig.dev/static/5138abb2995d746ba613de2a24c05826/0f6c6/store-984393_1920.jpg" alt="Store Sample" title="GrapQL Editor" loading="lazy">
      </picture>

In this tutorial, I will show how you can build your GraphQL for Magento 2.3 and extend them with a filter logic. Our use case is a Pickup from Store endpoint what our frontend team needs to create an interactive map.

**In the story, we have the following acceptance criteria.**

As a frontend developer, I need Endpoint to search for the next Pickup Store in a Postcode Area. Use a setup script initial import Allow search for Postcode or Name. API will return the following attributes for a Pickup Store

| Arrribute Name | GraphQL field |
| --- | --- |
| Name | name |
| Postcode | postcode |
| Street | street |
| Street Number | street\_num |
| City | city |
| Longitude | longitude |
| Latitude | latitude |

**System Requirements:**

- Installed [Magento 2.3](https://devdocs.magento.com/guides/v2.3/install-gde/composer.html)
- PHP 7.1 | 7.2 | 7.3
- Mysql 5.6

For Lokal Development I Recommended use Development Mode: Run the following comand `bin/magento deploy:mode:set developer`

**Table of Content:**

1. How to create the basis Magento 2 Module
2. How add Magento 2 GraphQL specific impelemention
3. Github Repo and how to install
4. How to use GraphQL with Magento

## <svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>1. How to create the basis Magento 2 Module

**This section is not GraphQL specific and and apply to any Magento2 Module!**

In this part, we learn how to created a new Database table and fill this with some sample data.

- Write and Repository Pattern for Magento 2
- Write Database Model and Collection
- Write and Setup 

Lets start with new Folder under **app/code/LarsRoettig/GraphQLStorePickup** in your installed magento.

**1. registration.php - Path: (app/code/LarsRoettig/GraphQLStorePickup/registration.php)**

```
<?php

declare(strict_types=1);

Magento\Framework\Component\ComponentRegistrar::register(
    Magento\Framework\Component\ComponentRegistrar::MODULE,
    'LarsRoettig_GraphQLStorePickup',
    __DIR__
);
```

**2. module.xml - Path: (app/code/LarsRoettig/GraphQLStorePickup/etc/module.xml)**

```
<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
	<module name="LarsRoettig_GraphQLStorePickup" setup_version="1.0.0">
		<sequence>
			<module name="Magento_GraphQl"/>
		</sequence>
	</module>
</config>
```

**3.Create new Database Table with db\_schema.xml - Path: (app/code/LarsRoettig/GraphQLStorePickup/etc/db\_schema.xml)**

```
<?xml version="1.0"?>
<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
    <table name="pickup_stores" resource="default" engine="innodb" comment="Pick Up Stores">
        <column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false" identity="true"
                comment="Entity ID"/>
        <column xsi:type="varchar" name="name" nullable="true" length="64"/>
        <column xsi:type="varchar" name="street" nullable="true" length="64"/>
        <column xsi:type="int" name="street_num" nullable="true"/>
        <column xsi:type="varchar" name="city" nullable="true" length="64"/>
        <column xsi:type="varchar" name="postcode" nullable="true" length="10"/>
        <column xsi:type="decimal" name="latitude" default="0" scale="4" precision="20" />
        <column xsi:type="decimal" name="longitude" default="0" scale="4" precision="20" />
        <constraint xsi:type="primary" referenceId="PRIMARY">
            <column name="entity_id"/>
        </constraint>
    </table>
</schema>
```

**4. Store Interface - Path: (app/code/LarsRoettig/GraphQLStorePickup/Api/Data/StoreInterface.php)**

```
<?php

declare(strict_types=1);

namespace LarsRoettig\GraphQLStorePickup\Api\Data;

/**
 * Represents a store and properties
 *
 * @api
 */
interface StoreInterface
{
    /**
     * Constants for keys of data array. Identical to the name of the getter in snake case
     */
    const NAME = 'name';
    const STREET = 'street';
    const STREET_NUM = 'street_num';
    const CITY = 'city';
    const POSTCODE = 'postcode';
    const LATITUDE = 'latitude';
    const LONGITUDE = 'longitude';

    /**#@-*/

    public function getName(): ?string;

    public function setName(?string $name): void;

    public function getStreet(): ?string;

    public function setStreet(?string $street): void;

    public function getStreetNum(): ?int;

    public function setStreetNum(?int $streetNum): void;

    public function getCity(): ?string;

    public function setCity(?string $city): void;

    public function getPostCode(): ?int;

    public function setPostcode(?int $postCode): void;

    public function getLatitude(): ?float;

    public function setLatitude(?float $latitude): void;

    public function getLongitude(): ?float;

    public function setLongitude(?float $longitude): void;
}
```

**5. Model - Path: (app/code/LarsRoettig/GraphQLStorePickup/Model/Store.php)**

```
<?php

declare(strict_types=1);

namespace LarsRoettig\GraphQLStorePickup\Model;

use LarsRoettig\GraphQLStorePickup\Api\Data\StoreInterface;
use LarsRoettig\GraphQLStorePickup\Model\ResourceModel\Store as StoreResourceModel;
use Magento\Framework\Model\AbstractExtensibleModel;

class Store extends AbstractExtensibleModel implements StoreInterface
{

    protected function _construct()
    {
        $this->_init(StoreResourceModel::class);
    }

    public function getName(): ?string
    {
        return $this->getData(self::NAME);
    }

    public function setName(?string $name): void
    {
        $this->setData(self::NAME, $name);
    }

    public function getStreet(): ?string
    {
        return $this->getData(self::STREET);
    }

    public function setStreet(?string $street): void
    {
        $this->setData(self::STREET, $street);
    }

    public function getStreetNum(): ?int
    {
        return $this->getData(self::STREET_NUM);
    }

    public function setStreetNum(?int $streetNum): void
    {
        $this->setData(self::STREET_NUM, $streetNum);
    }

    public function getCity(): ?string
    {
        return $this->getData(self::CITY);
    }

    public function setCity(?string $city): void
    {
        $this->setData(self::CITY, $city);
    }

    public function getPostCode(): ?int
    {
        return $this->getData(self::POSTCODE);
    }

    public function setPostcode(?int $postCode): void
    {
        $this->setData(self::POSTCODE, $postCode);
    }

    public function getLatitude(): ?float
    {
        return $this->getData(self::LATITUDE);
    }

    public function setLatitude(?float $latitude): void
    {
        $this->setData(self::LATITUDE, $latitude);
    }

    public function getLongitude(): ?float
    {
        return $this->getData(self::LONGITUDE);
    }

    public function setLongitude(?float $longitude): void
    {
        $this->setData(self::LONGITUDE, $longitude);
    }
}
```

**6. Resource Model - Path: (app/code/LarsRoettig/GraphQLStorePickup/Model/ResourceModel/Store.php)**

```
<?php
declare(strict_types=1);

namespace LarsRoettig\GraphQLStorePickup\Model\ResourceModel;

use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
use Magento\Framework\Model\ResourceModel\PredefinedId;

class Store extends AbstractDb
{
    /**
     * Provides possibility of saving entity with predefined/pre-generated id
     */
    use PredefinedId;

    /**#@+
     * Constants related to specific db layer
     */
    private const TABLE_NAME_STOCK = 'pickup_stores';
    /**#@-*/

    /**
     * @inheritdoc
     */
    protected function _construct()
    {
        $this->_init(self::TABLE_NAME_STOCK, 'entity_id');
    }
}
```

**7. Resource Collection - Path: (app/code/LarsRoettig/GraphQLStorePickup/Model/ResourceModel/StoreCollection.php)**

```
<?php
declare(strict_types=1);

namespace LarsRoettig\GraphQLStorePickup\Model\ResourceModel;

use LarsRoettig\GraphQLStorePickup\Model\ResourceModel\Store as StoreResourceModel;
use LarsRoettig\GraphQLStorePickup\Model\Store as StoreModel;
use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;

class StoreCollection extends AbstractCollection
{
    /**
     * @inheritdoc
     */
    protected function _construct()
    {
        $this->_init(StoreModel::class, StoreResourceModel::class);
    }
}
```

**8. StoreRepositoryInterface - Path: (app/code/LarsRoettig/GraphQLStorePickup/Api/StoreRepositoryInterface.php)**

```
<?php
declare(strict_types=1);

namespace LarsRoettig\GraphQLStorePickup\Api;

use LarsRoettig\GraphQLStorePickup\Api\Data\StoreInterface;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SearchResultsInterface;

/**
 * @api
 */
interface StoreRepositoryInterface
{
    /**
     * Save the Store data.
     *
     * @param \Magento\InventoryApi\Api\Data\SourceInterface $source
     * @return void
     * @throws \Magento\Framework\Exception\CouldNotSaveException
     */
    public function save(StoreInterface $store): void;

    /**
     * Find Stores by given SearchCriteria
     * SearchCriteria is not required because load all stores is useful case
     *
     * @param \Magento\Framework\Api\SearchCriteriaInterface|null $searchCriteria
     * @return \Magento\InventoryApi\Api\Data\SourceSearchResultsInterface
     */
    public function getList(SearchCriteriaInterface $searchCriteria = null): SearchResultsInterface;
}
```

**8. StoreRepository - Path: (app/code/LarsRoettig/GraphQLStorePickup/Model/StoreRepository.php)**

A repository is an architecture layer that handles communication between the application and the data source (DataBase). Repository Pattern helps to switch to another data source or making structural changes to the existing data source.

For all my repositories, usually, i have an interface that helps to decouple the implementation.

```
<?php
declare(strict_types=1);

namespace LarsRoettig\GraphQLStorePickup\Model;

use LarsRoettig\GraphQLStorePickup\Api\Data\StoreInterface;
use LarsRoettig\GraphQLStorePickup\Api\StoreRepositoryInterface;
use LarsRoettig\GraphQLStorePickup\Model\ResourceModel\Store as StoreResourceModel;
use LarsRoettig\GraphQLStorePickup\Model\ResourceModel\StoreCollection;
use LarsRoettig\GraphQLStorePickup\Model\ResourceModel\StoreCollectionFactory;
use Magento\Framework\Api\Search\SearchCriteriaBuilder;
use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SearchResultsInterface;
use Magento\Framework\Api\SearchResultsInterfaceFactory;
use Magento\Framework\Exception\CouldNotSaveException;

class StoreRepository implements StoreRepositoryInterface
{
    /**
     * @var StoreCollectionFactory
     */
    private $storeCollectionFactory;
    /**
     * @var CollectionProcessorInterface
     */
    private $collectionProcessor;
    /**
     * @var SearchCriteriaBuilder
     */
    private $searchCriteriaBuilder;
    /**
     * @var SearchResultsInterfaceFactory
     */
    private $storeSearchResultsInterfaceFactory;
    /**
     * @var StoreResourceModel
     */
    private $storeResourceModel;

    public function __construct(
        StoreCollectionFactory $storeCollectionFactory,
        CollectionProcessorInterface $collectionProcessor,
        SearchCriteriaBuilder $searchCriteriaBuilder,
        SearchResultsInterfaceFactory $storeSearchResultsInterfaceFactory,
        StoreResourceModel $storeResourceModel
    ) {
        $this->storeCollectionFactory = $storeCollectionFactory;
        $this->collectionProcessor = $collectionProcessor;
        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
        $this->storeSearchResultsInterfaceFactory = $storeSearchResultsInterfaceFactory;
        $this->storeResourceModel = $storeResourceModel;
    }

    /**
     * @inheritDoc
     */
    public function getList(SearchCriteriaInterface $searchCriteria = null): SearchResultsInterface
    {
        /** @var StoreCollection $storeCollection */
        $storeCollection = $this->storeCollectionFactory->create();
        if (null === $searchCriteria) {
            $searchCriteria = $this->searchCriteriaBuilder->create();
        } else {
            $this->collectionProcessor->process($searchCriteria, $storeCollection);
        }
        /** @var SearchResultsInterface $searchResult */
        $searchResult = $this->storeSearchResultsInterfaceFactory->create();
        $searchResult->setItems($storeCollection->getItems());
        $searchResult->setTotalCount($storeCollection->getSize());
        $searchResult->setSearchCriteria($searchCriteria);

        return $searchResult;
    }

    /**
     * @inheritDoc
     */
    public function save(StoreInterface $store): void
    {
        try {
            $this->storeResourceModel->save($store);
        } catch (\Exception $e) {
            throw new CouldNotSaveException(__('Could not save Source'), $e);
        }
    }
}
```

**9. Create di.xml to link interface to impelemention - Path: (app/code/LarsRoettig/GraphQLStorePickup/etc/di.xml)**

```
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="LarsRoettig\GraphQLStorePickup\Api\Data\StoreInterface" type="LarsRoettig\GraphQLStorePickup\Model\Store"/>
    <preference for="LarsRoettig\GraphQLStorePickup\Api\StoreRepositoryInterface" type="\LarsRoettig\GraphQLStorePickup\Model\StoreRepository"/>
</config>
```

**10. Setup Patch with Sample Data - Path: (app/code/LarsRoettig/GraphQLStorePickup/Setup/Patch/Data/InitializePickUpStores.php)**

```
<?php

declare(strict_types=1);

namespace LarsRoettig\GraphQLStorePickup\Setup\Patch\Data;

use LarsRoettig\GraphQLStorePickup\Api\Data\StoreInterface;
use LarsRoettig\GraphQLStorePickup\Api\Data\StoreInterfaceFactory;
use LarsRoettig\GraphQLStorePickup\Api\StoreRepositoryInterface;
use Magento\Framework\Api\DataObjectHelper;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;

class InitializePickUpStores implements DataPatchInterface
{
    /**
     * @var ModuleDataSetupInterface
     */
    private $moduleDataSetup;
    /**
     * @var StoreInterfaceFactory
     */
    private $storeInterfaceFactory;
    /**
     * @var StoreRepositoryInterface
     */
    private $storeRepository;
    /**
     * @var DataObjectHelper
     */
    private $dataObjectHelper;

    /**
     * EnableSegmentation constructor.
     *
     * @param ModuleDataSetupInterface $moduleDataSetup
     */
    public function __construct(
        ModuleDataSetupInterface $moduleDataSetup,
        StoreInterfaceFactory $storeInterfaceFactory,
        StoreRepositoryInterface $storeRepository,
        DataObjectHelper $dataObjectHelper
    ) {
        $this->moduleDataSetup = $moduleDataSetup;
        $this->storeInterfaceFactory = $storeInterfaceFactory;
        $this->storeRepository = $storeRepository;
        $this->dataObjectHelper = $dataObjectHelper;
    }

    /**
     * {@inheritdoc}
     */
    public static function getDependencies()
    {
        return [];
    }

    /**
     * {@inheritdoc}
     * @throws Exception
     * @throws Exception
     */
    public function apply()
    {
        $this->moduleDataSetup->startSetup();
        $maxStore = 50;

        $citys = ['Rosenheim', 'Kolbermoor', 'München', 'Erfurt', 'Berlin'];

        for ($i = 1; $i <= $maxStore; $i++) {

            $storeData = [
                StoreInterface::NAME => 'Brick and Mortar ' . $i,
                StoreInterface::STREET => 'Test Street' . $i,
                StoreInterface::STREET_NUM => $i * random_int(1, 100),
                StoreInterface::CITY => $citys[random_int(0, 4)],
                StoreInterface::POSTCODE => $i * random_int(1000, 9999),
                StoreInterface::LATITUDE => random_int(4757549, 5041053) / 100000,
                StoreInterface::LONGITUDE => random_int(1157549, 1341053) / 100000,
            ];
            /** @var StoreInterface $store */
            $store = $this->storeInterfaceFactory->create();
            $this->dataObjectHelper->populateWithArray($store, $storeData, StoreInterface::class);
            $this->storeRepository->save($store);
        }

        $this->moduleDataSetup->endSetup();
    }

    /**
     * {@inheritdoc}
     */
    public function getAliases()
    {
        return [];
    }
}
```

## <svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>2. How add Magento 2 GraphQL specific impelemention

**1.Create GraphQL Schema File - Path: (app/code/LarsRoettig/GraphQLStorePickup/etc/schema.graphqls)**

This the _schema.graphql_ contains the following information

- Defines the structure of queries and mutations.
- Determines which attributes can be used for input and output in GraphQL queries and mutations. Requests and responses contain separate lists of valid attributes.
- Points to the resolvers that verify and process the input data and response.
- Serves as the source for displaying the schema in a GraphQL browser.
- Defines which objects are cached.

Full Magento Dokumentation:[https://devdocs.magento.com/guides/v2.3/graphql/develop/create-graphqls-file.html](https://devdocs.magento.com/guides/v2.3/graphql/develop/create-graphqls-file.html)

```
type Query {
    pickUpStores(
        filter: PickUpStoresFilterInput @doc(description: "")
        pageSize: Int = 5 @doc(description: "How many items should show on the page")
        currentPage: Int = 1 @doc(description: "Allows to ussing paging it start with 1")
    ):pickUpStoresOutput @resolver(class: "\\LarsRoettig\\GraphQLStorePickup\\Model\\Resolver\\PickUpStores") @doc(description: "The Impelemention to resolve PickUp stores")
}

input PickUpStoresFilterInput {
    name: FilterTypeInput @doc(description: "")
    postcode: FilterTypeInput @doc(description: ""),
    latitude:FilterTypeInput @doc(description: ""),
    longitude: FilterTypeInput @doc(description: ""),
    or: PickUpStoresFilterInput
}

type pickUpStoresOutput {
    total_count: Int @doc(description: "")
    items: [PickUpStore] @doc(description: "")
}

type PickUpStore {
    name: String @doc(description: ""),
    street: String @doc(description: ""),
    street_num: Int @doc(description: ""),
    city: String @doc(description: ""),
    postcode: String @doc(description: ""),
    latitude:Float @doc(description: ""),
    longitude: Float @doc(description: ""),
}
```

**2.Create GraphQL Resolver File - Path: (app/code/LarsRoettig/GraphQLStorePickup/Model/Resolver/PickUpStores.php)**

```
<?php

declare(strict_types=1);

namespace LarsRoettig\GraphQLStorePickup\Model\Resolver;

use LarsRoettig\GraphQLStorePickup\Api\StoreRepositoryInterface;
use LarsRoettig\GraphQLStorePickup\Model\Store\GetList;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Query\Resolver\Argument\SearchCriteria\Builder as SearchCriteriaBuilder;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;

class PickUpStores implements ResolverInterface
{

    /**
     * @var GetListInterface
     */
    private $storeRepository;
    /**
     * @var SearchCriteriaBuilder
     */
    private $searchCriteriaBuilder;

    /**
     * PickUpStoresList constructor.
     * @param GetList $storeRepository
     * @param SearchCriteriaBuilder $searchCriteriaBuilder
     */
    public function __construct(StoreRepositoryInterface $storeRepository, SearchCriteriaBuilder $searchCriteriaBuilder)
    {
        $this->storeRepository = $storeRepository;
        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
    }

    /**
     * @inheritdoc
     */
    public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
    {

        $this->vaildateArgs($args);

        $searchCriteria = $this->searchCriteriaBuilder->build('pickup_stores', $args);
        $searchCriteria->setCurrentPage($args['currentPage']);
        $searchCriteria->setPageSize($args['pageSize']);
        $searchResult = $this->storeRepository->getList($searchCriteria);

        return [
            'total_count' => $searchResult->getTotalCount(),
            'items' => $searchResult->getItems(),
        ];
    }

    /**
     * @param array $args
     * @throws GraphQlInputException
     */
    private function vaildateArgs(array $args): void
    {
        if (isset($args['currentPage']) && $args['currentPage'] < 1) {
            throw new GraphQlInputException(__('currentPage value must be greater than 0.'));
        }

        if (isset($args['pageSize']) && $args['pageSize'] < 1) {
            throw new GraphQlInputException(__('pageSize value must be greater than 0.'));
        }
    }
}
```

**3.Create Dependencies Injection File (di.xml) - Path: (app/code/LarsRoettig/GraphQLStorePickup/etc/di.xml)**

This file we need to inject our FilterArgument Class that maps the graph attribute for filtering. Currently there is no general implementation.

```
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="LarsRoettig\GraphQLStorePickup\Api\Data\StoreInterface" type="LarsRoettig\GraphQLStorePickup\Model\Store"/>
    <preference for="LarsRoettig\GraphQLStorePickup\Api\StoreRepositoryInterface" type="\LarsRoettig\GraphQLStorePickup\Model\StoreRepository"/>
    <type name="Magento\Framework\GraphQl\Query\Resolver\Argument\FieldEntityAttributesPool">
        <arguments>
            <argument name="attributesInstances" xsi:type="array">
                <item name="pickup_stores" xsi:type="object">
                    \LarsRoettig\GraphQLStorePickup\Model\Resolver\FilterArgument
                </item>
            </argument>
        </arguments>
    </type>
</config>
```

**4. Create FilterArgument File - Path: (app/code/LarsRoettig/GraphQLStorePickup/Model/Resolver/FilterArgument.php)**

```
<?php
declare(strict_types=1);

namespace LarsRoettig\GraphQLStorePickup\Model\Resolver;

use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\ConfigInterface;
use Magento\Framework\GraphQl\Query\Resolver\Argument\FieldEntityAttributesInterface;

class FilterArgument implements FieldEntityAttributesInterface
{
    /** @var ConfigInterface */
    private $config;

    public function __construct(ConfigInterface $config)
    {
        $this->config = $config;
    }

    public function getEntityAttributes(): array
    {
        $fields = [];
        /** @var Field $field */
        foreach ($this->config->getConfigElement('PickUpStore')->getFields() as $field) {
            $fields[$field->getName()] = '';
        }

        return array_keys($fields);
    }
}
```

### <svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>3. Installation:

```
bin/magento module:enable LarsRoettig_GraphQLStorePickup
bin/magento setup:upgrade
```

The repo can founded at Github:

[https://github.com/larsroettig/module-graphqlstorepickup](https://github.com/larsroettig/module-graphqlstorepickup)

## <svg aria-hidden="true" focusable="false" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg>4. How to use GraphQL

**Test for your new Endpoint (Client Sample Call)**

I recommend as Testing client tool to use [Altair GraphQL Client](https://altair.sirmuel.design/)

**Endpoint Url: [https://your\_domain.test/graphql](https://your_domain.test/graphql)**

<picture>
        <source srcset="https://larsroettig.dev/static/4cbd147816395f3268022e2da78289a1/85df1/GraphQL_Playground.webp 250w,https://larsroettig.dev/static/4cbd147816395f3268022e2da78289a1/b1e7c/GraphQL_Playground.webp 500w,https://larsroettig.dev/static/4cbd147816395f3268022e2da78289a1/de8c6/GraphQL_Playground.webp 1000w,https://larsroettig.dev/static/4cbd147816395f3268022e2da78289a1/64b80/GraphQL_Playground.webp 1500w,https://larsroettig.dev/static/4cbd147816395f3268022e2da78289a1/fe035/GraphQL_Playground.webp 1821w" sizes="(max-width: 1000px) 100vw, 1000px" type="image/webp"></source>
        <source srcset="https://larsroettig.dev/static/4cbd147816395f3268022e2da78289a1/8768e/GraphQL_Playground.jpg 250w,https://larsroettig.dev/static/4cbd147816395f3268022e2da78289a1/08e51/GraphQL_Playground.jpg 500w,https://larsroettig.dev/static/4cbd147816395f3268022e2da78289a1/0f6c6/GraphQL_Playground.jpg 1000w,https://larsroettig.dev/static/4cbd147816395f3268022e2da78289a1/ddbde/GraphQL_Playground.jpg 1500w,https://larsroettig.dev/static/4cbd147816395f3268022e2da78289a1/3039f/GraphQL_Playground.jpg 1821w" sizes="(max-width: 1000px) 100vw, 1000px" type="image/jpeg"></source>
        <img src="https://larsroettig.dev/static/4cbd147816395f3268022e2da78289a1/0f6c6/GraphQL_Playground.jpg" alt="GraphQL_Playground Sample" title="GrapQL Editor" loading="lazy">
      </picture>

**Simple Query without an filter:**

```
{
  pickUpStores {
    total_count
      items {
        name
        street
        street_num
        postcode
      }
  }
}
```

**Query with an filter:**

```
{
  pickUpStores(
    filter: { name: { like: "Brick and Mortar 1%" } }
    pageSize: 2
    currentPage: 1
  ) {
    total_count
    items {
      name
      street
      postcode
    }
  }
}
```

**Query with an longitude filter:**

```
{
  pickUpStores(
    filter: { longitude: {
    gt: "11.66"
  }
   }
    pageSize: 2
    currentPage: 1
  ) {
    total_count
    
    items {
      name
      street
      postcode
      latitude
      longitude
    }
  }
}
```

(This article was posted to my blog at [https://larsroettig.dev](https://larsroettig.dev). You can [read it online by clicking here](https://larsroettig.dev/how-to-create-a-graph-ql-endpoint-for-magento-2-3).)