Lars Roettig

How to create a GraphQL Endpoint for Magento 2.3

By Lars Roettig3 min

In this Magento 2 GraphQL Tutorial, I will show how to build a GraphQL API endpoint and extend it with a custom filter logic. Our use case for this Tutorial is a Pickup from Store endpoint what our frontend team needs to create an interactive map. The team decided to provide data to the frontend via a GraphQL API.

In the story we have the following acceptance criteria:

As a frontend developer, I need an Endpoint to search for the next Pickup Store in a Postcode Area.

  • Use a setup script initial import
  • Allow search for Postcode or Name.

The API will return the following attributes for a Pickup Store:

Attribute NameGraphQL field
Namename
Postcodepostcode
Streetstreet
Street Numberstreet_num
Citycity
Longitudelongitude
Latitudelatitude

System Requirements:

  • Installed Magento 2.3
  • PHP 7.1 | 7.2 | 7.3
  • Mysql 5.6

For local development I recommend to use the development mode:

1bin/magento deploy:mode:set developer

Table of Content:

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

1. How to create the basic Magento 2 Module

This section is not GraphQL specific and can be applied to any Magento2 Module! In this part, we learn how to creat a new Database table and fill it with some sample data.

  • Repository pattern for Magento 2
  • Database Model and Collection
  • How to write a Data Patch for Magento2

Lets start with a new folder under app/code/LarsRoettig/GraphQLStorePickup in your installed magento.

app/code/LarsRoettig/GraphQLStorePickup/registration.php
1<?php
2
3declare(strict_types=1);
4
5Magento\Framework\Component\ComponentRegistrar::register(
6 Magento\Framework\Component\ComponentRegistrar::MODULE,
7 'LarsRoettig_GraphQLStorePickup',
8 __DIR__
9);
GraphQLStorePickup/etc/module.xml
1<?xml version="1.0" ?>
2<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
3 <module name="LarsRoettig_GraphQLStorePickup" setup_version="1.0.0">
4 <sequence>
5 <module name="Magento_GraphQl"/>
6 </sequence>
7 </module>
8</config>

3.Create a new database table with db_schema.xml

The new declarative schema approach allows us as developers to declare the final desired state of the database. Magento adjusts the database automatically without performing redundant operations. The declaration will change by running bin/magento setup:install or bin/magento setup:upgrade. We, as Developers, are no longer forced to write PHP scripts for every new version. Additionally, this approach handles that the data is deleted when a module is uninstalled.

GraphQLStorePickup/etc/db_schema.xml
1<?xml version="1.0"?>
2<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
4 <table name="pickup_stores" resource="default" engine="innodb" comment="Pick Up Stores">
5 <column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false" identity="true"
6 comment="Entity ID"/>
7 <column xsi:type="varchar" name="name" nullable="true" length="64"/>
8 <column xsi:type="varchar" name="street" nullable="true" length="64"/>
9 <column xsi:type="int" name="street_num" nullable="true"/>
10 <column xsi:type="varchar" name="city" nullable="true" length="64"/>
11 <column xsi:type="varchar" name="postcode" nullable="true" length="10"/>
12 <column xsi:type="decimal" name="latitude" default="0" scale="4" precision="20" />
13 <column xsi:type="decimal" name="longitude" default="0" scale="4" precision="20" />
14 <constraint xsi:type="primary" referenceId="PRIMARY">
15 <column name="entity_id"/>
16 </constraint>
17 </table>
18</schema>
GraphQLStorePickup/Api/Data/StoreInterface.php
1<?php
2
3declare(strict_types=1);
4
5namespace LarsRoettig\GraphQLStorePickup\Api\Data;
6
7/**
8 * Represents a store and properties
9 *
10 * @api
11 */
12interface StoreInterface
13{
14 /**
15 * Constants for keys of data array. Identical to the name of the getter in snake case
16 */
17 const NAME = 'name';
18 const STREET = 'street';
19 const STREET_NUM = 'street_num';
20 const CITY = 'city';
21 const POSTCODE = 'postcode';
22 const LATITUDE = 'latitude';
23 const LONGITUDE = 'longitude';
24
25 /**#@-*/
26
27 public function getName(): ?string;
28
29 public function setName(?string $name): void;
30
31 public function getStreet(): ?string;
32
33 public function setStreet(?string $street): void;
34
35 public function getStreetNum(): ?int;
36
37 public function setStreetNum(?int $streetNum): void;
38
39 public function getCity(): ?string;
40
41 public function setCity(?string $city): void;
42
43 public function getPostCode(): ?int;
44
45 public function setPostcode(?int $postCode): void;
46
47 public function getLatitude(): ?float;
48
49 public function setLatitude(?float $latitude): void;
50
51 public function getLongitude(): ?float;
52
53 public function setLongitude(?float $longitude): void;
54}
GraphQLStorePickup/Model/Store.php
1<?php
2
3declare(strict_types=1);
4
5namespace LarsRoettig\GraphQLStorePickup\Model;
6
7use LarsRoettig\GraphQLStorePickup\Api\Data\StoreInterface;
8use LarsRoettig\GraphQLStorePickup\Model\ResourceModel\Store as StoreResourceModel;
9use Magento\Framework\Model\AbstractExtensibleModel;
10
11class Store extends AbstractExtensibleModel implements StoreInterface
12{
13
14 protected function _construct()
15 {
16 $this->_init(StoreResourceModel::class);
17 }
18
19 public function getName(): ?string
20 {
21 return $this->getData(self::NAME);
22 }
23
24 public function setName(?string $name): void
25 {
26 $this->setData(self::NAME, $name);
27 }
28
29 public function getStreet(): ?string
30 {
31 return $this->getData(self::STREET);
32 }
33
34 public function setStreet(?string $street): void
35 {
36 $this->setData(self::STREET, $street);
37 }
38
39 public function getStreetNum(): ?int
40 {
41 return $this->getData(self::STREET_NUM);
42 }
43
44 public function setStreetNum(?int $streetNum): void
45 {
46 $this->setData(self::STREET_NUM, $streetNum);
47 }
48
49 public function getCity(): ?string
50 {
51 return $this->getData(self::CITY);
52 }
53
54 public function setCity(?string $city): void
55 {
56 $this->setData(self::CITY, $city);
57 }
58
59 public function getPostCode(): ?int
60 {
61 return $this->getData(self::POSTCODE);
62 }
63
64 public function setPostcode(?int $postCode): void
65 {
66 $this->setData(self::POSTCODE, $postCode);
67 }
68
69 public function getLatitude(): ?float
70 {
71 return $this->getData(self::LATITUDE);
72 }
73
74 public function setLatitude(?float $latitude): void
75 {
76 $this->setData(self::LATITUDE, $latitude);
77 }
78
79 public function getLongitude(): ?float
80 {
81 return $this->getData(self::LONGITUDE);
82 }
83
84 public function setLongitude(?float $longitude): void
85 {
86 $this->setData(self::LONGITUDE, $longitude);
87 }
88}
GraphQLStorePickup/Model/ResourceModel/Store.php
1<?php
2declare(strict_types=1);
3
4namespace LarsRoettig\GraphQLStorePickup\Model\ResourceModel;
5
6use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
7use Magento\Framework\Model\ResourceModel\PredefinedId;
8
9class Store extends AbstractDb
10{
11 /**
12 * Provides possibility of saving entity with predefined/pre-generated id
13 */
14 use PredefinedId;
15
16 /**#@+
17 * Constants related to specific db layer
18 */
19 private const TABLE_NAME_STOCK = 'pickup_stores';
20 /**#@-*/
21
22 /**
23 * @inheritdoc
24 */
25 protected function _construct()
26 {
27 $this->_init(self::TABLE_NAME_STOCK, 'entity_id');
28 }
29}
GraphQLStorePickup/Model/ResourceModel/StoreCollection.php
1<?php
2declare(strict_types=1);
3
4namespace LarsRoettig\GraphQLStorePickup\Model\ResourceModel;
5
6use LarsRoettig\GraphQLStorePickup\Model\ResourceModel\Store as StoreResourceModel;
7use LarsRoettig\GraphQLStorePickup\Model\Store as StoreModel;
8use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
9
10class StoreCollection extends AbstractCollection
11{
12 /**
13 * @inheritdoc
14 */
15 protected function _construct()
16 {
17 $this->_init(StoreModel::class, StoreResourceModel::class);
18 }
19}
GraphQLStorePickup/Api/StoreRepositoryInterface.php
1<?php
2declare(strict_types=1);
3
4namespace LarsRoettig\GraphQLStorePickup\Api;
5
6use LarsRoettig\GraphQLStorePickup\Api\Data\StoreInterface;
7use Magento\Framework\Api\SearchCriteriaInterface;
8use Magento\Framework\Api\SearchResultsInterface;
9
10/**
11 * @api
12 */
13interface StoreRepositoryInterface
14{
15 /**
16 * Save the Store data.
17 *
18 * @param \Magento\InventoryApi\Api\Data\SourceInterface $source
19 * @return void
20 * @throws \Magento\Framework\Exception\CouldNotSaveException
21 */
22 public function save(StoreInterface $store): void;
23
24 /**
25 * Find Stores by given SearchCriteria
26 * SearchCriteria is not required because load all stores is useful case
27 *
28 * @param \Magento\Framework\Api\SearchCriteriaInterface|null $searchCriteria
29 * @return \Magento\Framework\Api\SearchResultsInterface
30 */
31 public function getList(SearchCriteriaInterface $searchCriteria = null): SearchResultsInterface;
32}

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.

GraphQLStorePickup/Model/StoreRepository.ph
1<?php
2declare(strict_types=1);
3
4namespace LarsRoettig\GraphQLStorePickup\Model;
5
6use LarsRoettig\GraphQLStorePickup\Api\Data\StoreInterface;
7use LarsRoettig\GraphQLStorePickup\Api\StoreRepositoryInterface;
8use LarsRoettig\GraphQLStorePickup\Model\ResourceModel\Store as StoreResourceModel;
9use LarsRoettig\GraphQLStorePickup\Model\ResourceModel\StoreCollection;
10use LarsRoettig\GraphQLStorePickup\Model\ResourceModel\StoreCollectionFactory;
11use Magento\Framework\Api\Search\SearchCriteriaBuilder;
12use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface;
13use Magento\Framework\Api\SearchCriteriaInterface;
14use Magento\Framework\Api\SearchResultsInterface;
15use Magento\Framework\Api\SearchResultsInterfaceFactory;
16use Magento\Framework\Exception\CouldNotSaveException;
17
18class StoreRepository implements StoreRepositoryInterface
19{
20 /**
21 * @var StoreCollectionFactory
22 */
23 private $storeCollectionFactory;
24 /**
25 * @var CollectionProcessorInterface
26 */
27 private $collectionProcessor;
28 /**
29 * @var SearchCriteriaBuilder
30 */
31 private $searchCriteriaBuilder;
32 /**
33 * @var SearchResultsInterfaceFactory
34 */
35 private $storeSearchResultsInterfaceFactory;
36 /**
37 * @var StoreResourceModel
38 */
39 private $storeResourceModel;
40
41 public function __construct(
42 StoreCollectionFactory $storeCollectionFactory,
43 CollectionProcessorInterface $collectionProcessor,
44 SearchCriteriaBuilder $searchCriteriaBuilder,
45 SearchResultsInterfaceFactory $storeSearchResultsInterfaceFactory,
46 StoreResourceModel $storeResourceModel
47 ) {
48 $this->storeCollectionFactory = $storeCollectionFactory;
49 $this->collectionProcessor = $collectionProcessor;
50 $this->searchCriteriaBuilder = $searchCriteriaBuilder;
51 $this->storeSearchResultsInterfaceFactory = $storeSearchResultsInterfaceFactory;
52 $this->storeResourceModel = $storeResourceModel;
53 }
54
55 /**
56 * @inheritDoc
57 */
58 public function getList(SearchCriteriaInterface $searchCriteria = null): SearchResultsInterface
59 {
60 /** @var StoreCollection $storeCollection */
61 $storeCollection = $this->storeCollectionFactory->create();
62 if (null === $searchCriteria) {
63 $searchCriteria = $this->searchCriteriaBuilder->create();
64 } else {
65 $this->collectionProcessor->process($searchCriteria, $storeCollection);
66 }
67 /** @var SearchResultsInterface $searchResult */
68 $searchResult = $this->storeSearchResultsInterfaceFactory->create();
69 $searchResult->setItems($storeCollection->getItems());
70 $searchResult->setTotalCount($storeCollection->getSize());
71 $searchResult->setSearchCriteria($searchCriteria);
72
73 return $searchResult;
74 }
75
76 /**
77 * @inheritDoc
78 */
79 public function save(StoreInterface $store): void
80 {
81 try {
82 $this->storeResourceModel->save($store);
83 } catch (\Exception $e) {
84 throw new CouldNotSaveException(__('Could not save Source'), $e);
85 }
86 }
87}

9. Create di.xml to link interface to impelemention

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

10. Setup Patch with Sample Data

Since Magento 2.3 we have the possibility to define data and schema patches. This approach allows to have better controll over data changes.

Magento executes updates from the db_schema.xml before the data and schema patches.

Optional Step: In Step this we will generate Sample Data This code should be not shipped to production!

GraphQLStorePickup/Setup/Patch/Data/InitializePickUpStores.php
1<?php
2
3declare(strict_types=1);
4
5namespace LarsRoettig\GraphQLStorePickup\Setup\Patch\Data;
6
7use LarsRoettig\GraphQLStorePickup\Api\Data\StoreInterface;
8use LarsRoettig\GraphQLStorePickup\Api\Data\StoreInterfaceFactory;
9use LarsRoettig\GraphQLStorePickup\Api\StoreRepositoryInterface;
10use Magento\Framework\Api\DataObjectHelper;
11use Magento\Framework\Setup\ModuleDataSetupInterface;
12use Magento\Framework\Setup\Patch\DataPatchInterface;
13
14class InitializePickUpStores implements DataPatchInterface
15{
16 /**
17 * @var ModuleDataSetupInterface
18 */
19 private $moduleDataSetup;
20 /**
21 * @var StoreInterfaceFactory
22 */
23 private $storeInterfaceFactory;
24 /**
25 * @var StoreRepositoryInterface
26 */
27 private $storeRepository;
28 /**
29 * @var DataObjectHelper
30 */
31 private $dataObjectHelper;
32
33 /**
34 * EnableSegmentation constructor.
35 *
36 * @param ModuleDataSetupInterface $moduleDataSetup
37 */
38 public function __construct(
39 ModuleDataSetupInterface $moduleDataSetup,
40 StoreInterfaceFactory $storeInterfaceFactory,
41 StoreRepositoryInterface $storeRepository,
42 DataObjectHelper $dataObjectHelper
43 ) {
44 $this->moduleDataSetup = $moduleDataSetup;
45 $this->storeInterfaceFactory = $storeInterfaceFactory;
46 $this->storeRepository = $storeRepository;
47 $this->dataObjectHelper = $dataObjectHelper;
48 }
49
50 /**
51 * {@inheritdoc}
52 */
53 public static function getDependencies()
54 {
55 return [];
56 }
57
58 /**
59 * {@inheritdoc}
60 * @throws Exception
61 * @throws Exception
62 */
63 public function apply()
64 {
65 $this->moduleDataSetup->startSetup();
66 $maxStore = 50;
67
68 $citys = ['Rosenheim', 'Kolbermoor', 'München', 'Erfurt', 'Berlin'];
69
70 for ($i = 1; $i <= $maxStore; $i++) {
71
72 $storeData = [
73 StoreInterface::NAME => 'Brick and Mortar ' . $i,
74 StoreInterface::STREET => 'Test Street' . $i,
75 StoreInterface::STREET_NUM => $i * random_int(1, 100),
76 StoreInterface::CITY => $citys[random_int(0, 4)],
77 StoreInterface::POSTCODE => $i * random_int(1000, 9999),
78 StoreInterface::LATITUDE => random_int(4757549, 5041053) / 100000,
79 StoreInterface::LONGITUDE => random_int(1157549, 1341053) / 100000,
80 ];
81 /** @var StoreInterface $store */
82 $store = $this->storeInterfaceFactory->create();
83 $this->dataObjectHelper->populateWithArray($store, $storeData, StoreInterface::class);
84 $this->storeRepository->save($store);
85 }
86
87 $this->moduleDataSetup->endSetup();
88 }
89
90 /**
91 * {@inheritdoc}
92 */
93 public function getAliases()
94 {
95 return [];
96 }
97}

2. How to add Magento 2 GraphQL specific impelemention

1.Create GraphQL Schema File

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.

GraphQLStorePickup/etc/schema.graphqls
1type Query {
2 pickUpStores(
3 filter: PickUpStoresFilterInput @doc(description: "")
4 pageSize: Int = 5
5 @doc(description: "How many items should show on the page")
6 currentPage: Int = 1
7 @doc(description: "Allows to ussing paging it start with 1")
8 ): pickUpStoresOutput
9 @resolver(
10 class: "\\LarsRoettig\\GraphQLStorePickup\\Model\\Resolver\\PickUpStores"
11 )
12 @doc(description: "The Impelemention to resolve PickUp stores")
13}
14
15input PickUpStoresFilterInput {
16 name: FilterTypeInput @doc(description: "")
17 postcode: FilterTypeInput @doc(description: "")
18 latitude: FilterTypeInput @doc(description: "")
19 longitude: FilterTypeInput @doc(description: "")
20 or: PickUpStoresFilterInput
21}
22
23type pickUpStoresOutput {
24 total_count: Int @doc(description: "")
25 items: [PickUpStore] @doc(description: "")
26}
27
28type PickUpStore {
29 name: String @doc(description: "")
30 street: String @doc(description: "")
31 street_num: Int @doc(description: "")
32 city: String @doc(description: "")
33 postcode: String @doc(description: "")
34 latitude: Float @doc(description: "")
35 longitude: Float @doc(description: "")
36}

2.Create GraphQL Resolver File)

This PHP Class represents the Service implementation of our GraphQL Query Endpoint pickUpStores. This Class is called in every query to pickUpStores. Every resolver needs to implement the Magento\Framework\GraphQl\Query\ResolverInterface to work correctly.

GraphQLStorePickup/Model/Resolver/PickUpStores.php
1<?php
2
3declare(strict_types=1);
4
5namespace LarsRoettig\GraphQLStorePickup\Model\Resolver;
6
7use LarsRoettig\GraphQLStorePickup\Api\StoreRepositoryInterface;
8use LarsRoettig\GraphQLStorePickup\Model\Store\GetList;
9use Magento\Framework\GraphQl\Config\Element\Field;
10use Magento\Framework\GraphQl\Exception\GraphQlInputException;
11use Magento\Framework\GraphQl\Query\Resolver\Argument\SearchCriteria\Builder as SearchCriteriaBuilder;
12use Magento\Framework\GraphQl\Query\ResolverInterface;
13use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
14
15class PickUpStores implements ResolverInterface
16{
17
18 /**
19 * @var GetListInterface
20 */
21 private $storeRepository;
22 /**
23 * @var SearchCriteriaBuilder
24 */
25 private $searchCriteriaBuilder;
26
27 /**
28 * PickUpStoresList constructor.
29 * @param GetList $storeRepository
30 * @param SearchCriteriaBuilder $searchCriteriaBuilder
31 */
32 public function __construct(StoreRepositoryInterface $storeRepository, SearchCriteriaBuilder $searchCriteriaBuilder)
33 {
34 $this->storeRepository = $storeRepository;
35 $this->searchCriteriaBuilder = $searchCriteriaBuilder;
36 }
37
38 /**
39 * @inheritdoc
40 */
41 public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
42 {
43
44 $this->vaildateArgs($args);
45
46 $searchCriteria = $this->searchCriteriaBuilder->build('pickup_stores', $args);
47 $searchCriteria->setCurrentPage($args['currentPage']);
48 $searchCriteria->setPageSize($args['pageSize']);
49 $searchResult = $this->storeRepository->getList($searchCriteria);
50
51 return [
52 'total_count' => $searchResult->getTotalCount(),
53 'items' => $searchResult->getItems(),
54 ];
55 }
56
57 /**
58 * @param array $args
59 * @throws GraphQlInputException
60 */
61 private function vaildateArgs(array $args): void
62 {
63 if (isset($args['currentPage']) && $args['currentPage'] < 1) {
64 throw new GraphQlInputException(__('currentPage value must be greater than 0.'));
65 }
66
67 if (isset($args['pageSize']) && $args['pageSize'] < 1) {
68 throw new GraphQlInputException(__('pageSize value must be greater than 0.'));
69 }
70 }
71}

3.Create Dependencies Injection File (di.xml)

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

GraphQLStorePickup/etc/di.xml
1<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2 xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
3 <preference for="LarsRoettig\GraphQLStorePickup\Api\Data\StoreInterface" type="LarsRoettig\GraphQLStorePickup\Model\Store"/>
4 <preference for="LarsRoettig\GraphQLStorePickup\Api\StoreRepositoryInterface" type="\LarsRoettig\GraphQLStorePickup\Model\StoreRepository"/>
5 <type name="Magento\Framework\GraphQl\Query\Resolver\Argument\FieldEntityAttributesPool">
6 <arguments>
7 <argument name="attributesInstances" xsi:type="array">
8 <item name="pickup_stores" xsi:type="object">
9 \LarsRoettig\GraphQLStorePickup\Model\Resolver\FilterArgument
10 </item>
11 </argument>
12 </arguments>
13 </type>
14</config>

4. Create FilterArgument File

This Class is needed to add our filter fields as attributes to Magento's argument resolver (Magento\Framework\GraphQl\Query\Resolver\Argument\FieldEntityAttributesPool). Currently, there is no implementation in the Magento core that will do it automatically.

GraphQLStorePickup/Model/Resolver/FilterArgument.php
1<?php
2declare(strict_types=1);
3
4namespace LarsRoettig\GraphQLStorePickup\Model\Resolver;
5
6use Magento\Framework\GraphQl\Config\Element\Field;
7use Magento\Framework\GraphQl\ConfigInterface;
8use Magento\Framework\GraphQl\Query\Resolver\Argument\FieldEntityAttributesInterface;
9
10class FilterArgument implements FieldEntityAttributesInterface
11{
12 /** @var ConfigInterface */
13 private $config;
14
15 public function __construct(ConfigInterface $config)
16 {
17 $this->config = $config;
18 }
19
20 public function getEntityAttributes(): array
21 {
22 $fields = [];
23 /** @var Field $field */
24 foreach ($this->config->getConfigElement('PickUpStore')->getFields() as $field) {
25 $fields[$field->getName()] = '';
26 }
27
28 return array_keys($fields);
29 }
30}

3. Installation:

1bin/magento module:enable LarsRoettig_GraphQLStorePickup
2bin/magento setup:db-declaration:generate-whitelist --module-name=LarsRoettig_GraphQLStorePickup
3bin/magento setup:upgrade

4. How to use GraphQL

Test for your new endpoint (Client Sample Call)

I recommend to use Altair GraphQL Client as testing client tool.

If you like Pto use Postman, please use the following link:

Sending API requests

GraphQL_Playground Sample

Simple GraphQL-Query without an filter:

1{
2 pickUpStores {
3 total_count
4 items {
5 name
6 street
7 street_num
8 postcode
9 }
10 }
11}

GraphQL-Query with a filter:

1{
2 pickUpStores(
3 filter: { name: { like: "Brick and Mortar 1%" } }
4 pageSize: 2
5 currentPage: 1
6 ) {
7 total_count
8 items {
9 name
10 street
11 postcode
12 }
13 }
14}

Complex GraphQL-Query with a longitude filter:

1{
2 pickUpStores(
3 filter: { longitude: { gt: "11.66" } }
4 pageSize: 2
5 currentPage: 1
6 ) {
7 total_count
8
9 items {
10 name
11 street
12 postcode
13 latitude
14 longitude
15 }
16 }
17}

Summary

In conclusion,let me just say that we were able to add a new GraphQL Endpoint to Magento 2 within 15 minutes. From my point of view the response times from the API are slow. As far as I know the core team wants to improve the perfomance 2.4.x release.

Edit post on GitHub

Written by
Lars Roettig
Software Engineer at TechDivision GmbH and Maintainer of the Community Engineering Team at Magento. He has 8 years of professional SoftwareEngineering experience. Lars is passionate about Magento and Open Source.
You may also like

Useful? Share it!

Lars Roettig
Legal
  • Imprint
  • Privacy Statement
Employee at: