Lars Roettig

How to create a GraphQL Mutation Endpoint for Magento 2.3

By Lars Roettig1 min

In my last Magento 2 tutorial, I showed how to build a basic GraphQL module with filtering. The primary purpose of GraphQL is to retrieve data (query), but every complete data exchange format needs a way to change server-based data (mutations). Therefore I want to show now how we can change data or create new pickup stores via the frontend.

Currently, the Magento 2 GraphQL Endpoint supports only access controls based on customers. This GraphQL will only allow a query on the customer data if the customer’s token must proived in the header section.

Since I would like to show only exemplarily how such a thing could look, I did without access control!

Before we can start here, you can find the first module that we need ground for this tutorial.

This Class holds the logic for that endpoint is responsible for any GraphQL call that creates a new pickup store in the Database. Every Resolver Class is forced to implements Magento\Framework\GraphQl\Query\ResolverInterface to work correctly.

GraphQLStorePickup/Model/Resolver/CreatPickUpStore.php
1<?php
2
3declare(strict_types=1);
4
5namespace LarsRoettig\GraphQLStorePickup\Model\Resolver;
6
7use LarsRoettig\GraphQLStorePickup\Model\CreatePickUpStore as CreatPickUpStoreService;
8use Magento\Framework\GraphQl\Config\Element\Field;
9use Magento\Framework\GraphQl\Exception\GraphQlInputException;
10use Magento\Framework\GraphQl\Query\ResolverInterface;
11use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
12
13class CreatPickUpStore implements ResolverInterface
14{
15 /**
16 * @var CreatPickUpStoreService
17 */
18 private $creatPickUpStore;
19
20 /**
21 * @param CreatPickUpStore $creatPickUpStore
22 */
23 public function __construct(CreatPickUpStoreService $creatPickUpStore)
24 {
25 $this->creatPickUpStore = $creatPickUpStore;
26 }
27
28 /**
29 * @inheritDoc
30 */
31 public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
32 {
33 if (empty($args['input']) || !is_array($args['input'])) {
34 throw new GraphQlInputException(__('"input" value should be specified'));
35 }
36
37 return ['pick_up_store' => $this->creatPickUpStore->execute($args['input'])];
38 }
39}

The second step is to create a service class that will convert a given array to a pickup store and saves them in the database. A service class help us to have single small bussiness logic that can be used by differnt other php services.

GraphQLStorePickup/Model/CreatePickUpStore.php
1<?php
2declare(strict_types=1);
3
4namespace LarsRoettig\GraphQLStorePickup\Model;
5
6use LarsRoettig\GraphQLStorePickup\Api\Data\StoreInterface;
7use LarsRoettig\GraphQLStorePickup\Api\Data\StoreInterfaceFactory;
8use LarsRoettig\GraphQLStorePickup\Api\StoreRepositoryInterface;
9use Magento\Framework\Api\DataObjectHelper;
10use Magento\Framework\Exception\CouldNotSaveException;
11use Magento\Framework\Exception\LocalizedException;
12use Magento\Framework\GraphQl\Exception\GraphQlInputException;
13
14class CreatePickUpStore
15{
16
17 /**
18 * @var DataObjectHelper
19 */
20 private $dataObjectHelper;
21 /**
22 * @var StoreRepositoryInterface
23 */
24 private $storeRepository;
25 /**
26 * @var StoreInterfaceFactory
27 */
28 private $storeFactory;
29
30 /**
31 * @param DataObjectHelper $dataObjectHelper
32 * @param StoreRepositoryInterface $storeRepository
33 * @param StoreInterfaceFactory $storeInterfaceFactory
34 */
35 public function __construct(
36 DataObjectHelper $dataObjectHelper,
37 StoreRepositoryInterface $storeRepository,
38 StoreInterfaceFactory $storeInterfaceFactory
39 ) {
40 $this->dataObjectHelper = $dataObjectHelper;
41 $this->storeRepository = $storeRepository;
42 $this->storeFactory = $storeInterfaceFactory;
43 }
44
45 /**
46 * @param array $data
47 * @return StoreInterface
48 * @throws GraphQlInputException
49 */
50 public function execute(array $data): StoreInterface
51 {
52 try {
53 $this->vaildateData($data);
54 $store = $this->saveStore($this->createStore($data));
55 } catch (\Exception $e) {
56 throw new GraphQlInputException(__($e->getMessage()));
57 }
58
59 return $store;
60 }
61
62 /**
63 * Guard function to handle bad request.
64 * @param array $data
65 * @throws LocalizedException
66 */
67 private function vaildateData(array $data)
68 {
69 if (!isset($data[StoreInterface::NAME])) {
70 throw new LocalizedException(__('Name must be set'));
71 }
72 }
73
74 /**
75 * Persists the given store in the data base.
76 * +
77 * @param StoreInterface $store
78 * @return StoreInterface
79 * @throws CouldNotSaveException
80 */
81 private function saveStore(StoreInterface $store): StoreInterface
82 {
83 $this->storeRepository->save($store);
84
85 return $store;
86 }
87
88 /**
89 * Create a store dto by given data array.
90 *
91 * @param array $data
92 * @return StoreInterface
93 * @throws CouldNotSaveException
94 */
95 private function createStore(array $data): StoreInterface
96 {
97 /** @var StoreInterface $store */
98 $store = $this->storeFactory->create();
99 $this->dataObjectHelper->populateWithArray(
100 $store,
101 $data,
102 StoreInterface::class
103 );
104
105 return $store;
106 }
107}

This need to be added to the file:

GraphQLStorePickup/etc/schema.graphqls
1type Mutation {
2 createPickUpStores(input: PickUpStoreInput!): PickUpStoreOutput
3 @resolver(
4 class: "\\LarsRoettig\\GraphQLStorePickup\\Model\\Resolver\\CreatPickUpStore"
5 )
6 @doc(description: "Create a new pickup store")
7}
8
9type PickUpStoreOutput {
10 pick_up_store: PickUpStore!
11}
12
13input PickUpStoreInput {
14 name: String @doc(description: "")
15 street: String @doc(description: "")
16 street_num: Int @doc(description: "")
17 city: String @doc(description: "")
18 postcode: String @doc(description: "")
19 latitude: Float @doc(description: "")
20 longitude: Float @doc(description: "")
21}

Full File:

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: "Allow to query for a pickup store.")
13}
14
15type Mutation {
16 createPickUpStores(input: PickUpStoreInput!): PickUpStoreOutput
17 @resolver(
18 class: "\\LarsRoettig\\GraphQLStorePickup\\Model\\Resolver\\CreatPickUpStore"
19 )
20 @doc(description: "Create a new pickup store")
21}
22
23type PickUpStoreOutput {
24 pick_up_store: PickUpStore!
25}
26
27input PickUpStoreInput {
28 name: String @doc(description: "")
29 street: String @doc(description: "")
30 street_num: Int @doc(description: "")
31 city: String @doc(description: "")
32 postcode: String @doc(description: "")
33 latitude: Float @doc(description: "")
34 longitude: Float @doc(description: "")
35}
36
37input PickUpStoresFilterInput {
38 name: FilterTypeInput @doc(description: "")
39 postcode: FilterTypeInput @doc(description: "")
40 street: FilterTypeInput @doc(description: "")
41 street_num: FilterTypeInput @doc(description: "")
42 city: FilterTypeInput @doc(description: "")
43 latitude: FilterTypeInput @doc(description: "")
44 longitude: FilterTypeInput @doc(description: "")
45 or: PickUpStoresFilterInput
46}
47
48type PickUpStoresOutput {
49 total_count: Int @doc(description: "")
50 items: [PickUpStore] @doc(description: "")
51}
52
53type PickUpStore {
54 entity_id: Int @doc(description: "")
55 name: String @doc(description: "")
56 street: String @doc(description: "")
57 street_num: Int @doc(description: "")
58 city: String @doc(description: "")
59 postcode: String @doc(description: "")
60 latitude: Float @doc(description: "")
61 longitude: Float @doc(description: "")
62}

3. Installation:

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

How to call your GraphQL Endpoint

Attention the Url https://your_domain.test/graphql work only with an valid GraphQL-Request!

GrapQL Mutation

GraphQL-Mutation:

1mutation {
2 createPickUpStores(
3 input: {
4 name: "Mustation Store"
5 street: "sweswq"
6 street_num: 12
7 postcode: "83059"
8 latitude: 22.3
9 longitude: 22.3
10 }
11 ) {
12 pick_up_store {
13 name
14 }
15 }
16}

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: