Magento2 How to create a custom GraphQL – Part1: Create a simple graphql query
GraphQL is a query language for APIs, gives clients the power to ask for exactly what they need and nothing more. GraphQL uses mutations to perform CRUD operations.
Today we will only talk about creating and query a simple GraphQL api. Let us begin by creating schema.graphqls under etc directory:
Know/ModuleGraphql/etc/schema.graphqls
Now add following content to the file:
type Query { getPostById(id: Int @doc(descrition: "Post ID. Must be an integer")): Posts @resolver(class: "Know\\ModuleGraphql\\Model\\Resolver\\PostById") @doc(description: "Retrieve a post by id.") } type Posts { id: Int title: String categories: String description: String }
Next add the Post resolver model: Know/ModuleGraphql/Model/Resolver/PostById.php
<?php namespace Know\ModuleGraphql\Model\Resolver; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; class Post implements ResolverInterface { public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { if (!isset($args['id'])) { throw new GraphQlInputException(__('Invalid Parameter provided.')); } $id = $args['id']; $allposts = $this->getAllPosts(); $post = []; foreach ($allposts as $item) { if ($id == $item['id']) { $post = $item; break; } } return $post; } public function getAllPosts(): array { return [ [ "id" => 1, "title" => "My Post 1", "categories" => "my posts, custom posts", "description" => "Lorem Ipsum is simply dummy text of the printing and typesetting industry." ], [ "id" => 2, "title" => "My Post 2", "categories" => "my posts, custom post2", "description" => "Lorem Ipsum is simply dummy text of the printing and typesetting industry." ], ]; } }
Time to run our graphQL api. Use postman or a GraphQL client to test the request. I usually use Altair GraphQL chrome extension to run the request.
In url specify {base_url}/graphql and add following query under query or request body section:
query { getPostById( id: 2 ) { title categories description } }
The output will be:
Query the dynamic posts
The database is going to involve for dynamic posts. For this purpose let us create a table called “posts”. To follow best practices create a separate module “Know_Module”.
Create Know/Module/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="Know_Module" setup_version="1.0.0" /> </config>
Register the module Know/Module/registration.php:
<?php \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::MODULE, 'Know_Module', __DIR__ );
Add posts table using db_schema.xml. Create db schema Know/Module/etc/db_schema.xml
<?xml version="1.0" encoding="UTF-8" ?> <schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd"> <table name="posts" resource="default" engine="innodb" comment="Posts Entity"> <column xsi:type="int" name="entity_id" unsigned="true" nullable="false" identity="true" comment="Entity ID" /> <column xsi:type="varchar" name="title" nullable="false" comment="Title" length="255" /> <column xsi:type="text" name="description" nullable="true" comment="Title" /> <column xsi:type="varchar" name="categories" nullable="false" comment="Title" length="255" /> <column xsi:type="timestamp" name="created_at" nullable="false" comment="Creation datetime" default="CURRENT_TIMESTAMP" /> <column xsi:type="timestamp" name="updated_at" nullable="false" on_update="true" default="CURRENT_TIMESTAMP" comment="Update datetime" /> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id" /> </constraint> <constraint xsi:type="unique" referenceId="POSTS_ENTT_ID"> <column name="entity_id"/> </constraint> </table> </schema>
Run setup upgrade command to install the module.
php bin/magento setup:upgrade
Insert some dummy data in posts table.
INSERT INTO `posts` (`entity_id`, `title`, `description`, `categories`, `created_at`, `updated_at`) VALUES (null, 'What is Lorem Ipsum?', 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s', 'Lorem Ipsum', '2022-09-01 12:34:19', '2022-09-01 12:34:19'), (null, 'Why do we use it?', 'It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using \'Content here, content here\', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for \'lorem ipsum\' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).', 'Sample text, Text tree', '2022-09-01 12:34:19', '2022-09-01 12:34:19');
Create Post model: Know/Module/Model/Post.php
<?php namespace Know\Module\Model; use Magento\Framework\Model\AbstractModel; class Post extends AbstractModel { public function _construct() { $this->_init(\Know\Module\Model\ResourceModel\Post::class); } }
Next create a Post Resource model: Know/Module/Model/ResourceModel/Post.php
<?php namespace Know\Module\Model\ResourceModel; class Post extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb { protected function _construct() { $this->_init("posts", "entity_id"); } }
Now create a Collection model: Know/Module/Model/ResourceModel/Post/Collection.php
<?php namespace Know\Module\Model\ResourceModel\Post; class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection { public function _construct() { $this->_init( \Know\Module\Model\Post::class, \Know\Module\Model\ResourceModel\Post::class ); } }
Modify the resolver model and replace with the code below: Know/Module/Model/Resolver/PostById.php
<?php namespace Know\Module\Model\Resolver; use Know\Module\Model\ResourceModel\Post\Collection; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; class PostById implements \Magento\Framework\GraphQl\Query\ResolverInterface { /** * @var Collection */ private Collection $collection; /** * @param Collection $collection */ public function __construct(Collection $collection) { $this->collection = $collection; } /** * @inheritDoc */ public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) { if (!isset($args['id'])) { throw new GraphQlInputException(__("Id is required field.")); } $id = $args['id']; $collection = $this->collection->addFieldToFilter("entity_id", $id); if (!$collection->count()) { return []; } $postData = []; foreach ($collection->getData() as $item) { $postData = [ "id" => $item["entity_id"], "title" => $item["title"], "description" => $item["description"], "categories" => $item["categories"], "created" => $item["created_at"], "updated" => $item["updated_at"], ]; } return $postData; } }
Flush the cache:
php bin/magento c:c
Implement and test the query. The result should look similar to the screenshot below.
2 Comments
Hi,
If you give wrong ID in request what is the error data structure?
How can we add the custom error data if request parameter is wrong.?
Can u please help on this?
The easiest way to customize the error data is to create an interceptor or plugin for Magento\Framework\GraphQl\Query\ErrorHandler class. You can customize it your own way. We will go with a customization example for this scenario:
Create di.xml under etc folder:
Know/Module/etc/di.xml
Now create the ErrorHandlerPlugin interceptor.
Know/Module/Plugin/Graphql/Query/ErrorHandlerPlugin.php
Flush the cache or remove the generated or static content to see the changes reflected.
The output can be similar to the screenshots below:
Mutation:
Default exception in mutation looks like:
After Changes:
Query:
I hope this will help you.