Extending the Magento 2 Rest Web API to Suit Your eCommerce Needs

You may recall from a couple years back that I had provided a tutorial here on our blog for extending Magento’s API to better suit your business needs. However, that was for Magento 1. With the recent release of the new Magento 2 eCommerce platform, and the differences in its architecture compared to previous version – I thought this would be a great opportunity to take the time to update you all with how to go about extending the Magento 2 Rest Web API.

How to Extend the Magento 2 Rest Web API

ecommerce, website design, demac media, magento 2, custom rest api endpoint, magento ecommerce,magento 2 rest api endpoint

This guide will act as a quick, introductory overview of how to extend the Magento 2’s API so that you may better meet your business requirements. We will build a new module to add a new REST web service to Magento’s API. This tutorial will cover two simple functions – getCatalogProductCount() and getCategoryProductCount() to demonstrate:

  • a GET method
  • a POST method with input
  • different levels of access control for security

Step 1) Module Creation
Let’s create our new module at app/code/Demac/WebService. Populate etc/module.xml with the following to declare your module:

<?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="Demac_WebService" setup_version="0.0.1" />
</config>

Next, we’ll need to ensure that our module is registered within the Magento framework through the registration.php file:

<?php
use \Magento\Framework\Component\ComponentRegistrar;
ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Demac_WebService', __DIR__);

Step 2) Dependency Injection (di.xml)
Create the di.xml file under the app/code/Demac/WebService/etc/ directory and populate it as follows:

<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Demac\WebService\Api\WebServiceRepositoryInterface" type="Demac\WebService\Model\Resource\WebServiceRepository" />
</config>

With this file we are enforcing our preference to instantiate the WebServiceRepository object when the WebServiceRepositoryInferface is called. The repository class can satisfy any request to the interface. We will create these classes shortly.

Step 3) Web API (webapi.xml)
Create the webapi.xml file under the app/code/Demac/WebService/etc/ directory and populate it as follows:

<?xml version="1.0" ?>
<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd">
<route url="/V1/demac/webservice/getCatalogProductCount/" method="GET">
<service class="Demac\WebService\Api\WebServiceRepositoryInterface" method="getCatalogProductCount"/>
<resources>
<resource ref="anonymous"/>
</resources>
</route>
<route url="/V1/demac/webservice/getCategoryProductCount/" method="POST">
<service class="Demac\WebService\Api\WebServiceRepositoryInterface" method="getCategoryProductCount"/>
<resources>
<resource ref="Magento_Backend::admin"/>
</resources>
</route>
</routes>

In this file we are creating a new URL route mapping that will call a specific function. Under resources we can define the access control of the endpoint. In this example, getCatalogProductCount() is an “anonymous” service that anyone can access. In comparison, getCategoryProductCount() the service is limited to users with Magento admin access to the site.

Step 4) Interface Class
Create WebServiceRepositoryInterface.php class file under the app/code/Demac/WebService/Api/ directory and populate it as follows:

<?php
namespace Demac\WebService\Api;
/**
* Interface WebServiceRepositoryInterface
* @package Demac\WebService\Api
*/
interface WebServiceRepositoryInterface
{
/**
* @return int
*/
public function getCatalogProductCount();
/**
* @param int $categoryId
*
* @return mixed
*/
public function getCategoryProductCount($categoryId);
}

Here, we are declaring a PHP interface class with two methods – getCatalogProductCount() which will return an int, and getCategoryProductCount() which will also return an int as well as accept an int as a parameter.

Please note that the presence and validity of the PHP DocBlocks on these functions is important. Without them, or if they are invalid the REST API will return that your “class does not exist”. Valid DocBlocks are required to proceed. For more information concerning Magento’s standards for inline documentation please refer to their Developer Documentation.

Step 5) Repository Class
Create WebServiceRepository.php class file under the app/code/Demac/WebService/Model/Resource/ directory and populate it as follows:

<?php
namespace Demac\WebService\Model\Resource;
use Demac\WebService\Api\WebServiceRepositoryInterface;
use Magento\Catalog\Model\CategoryFactory;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory;
use Magento\Framework\App\ResourceConnectionFactory;
/**
* Class WebServiceRepository
* @package Demac\WebService\Model
*/
class WebServiceRepository implements WebServiceRepositoryInterface
{
/**
* @var ResourceConnectionFactory
*/
protected $_resourceConnection;
/**
* @var ProductCollectionFactory
*/
protected $_productCollection;
/**
* @var CategoryFactory
*/
protected $_category;
/**
* WebServiceRepository constructor.
*
* @param ResourceConnectionFactory $_resourceConnection
*/
public function __construct(ResourceConnectionFactory $_resourceConnection, ProductCollectionFactory $_productCollection, CategoryFactory $_category)
{
$this->_resourceConnection = $_resourceConnection;
$this->_productCollection = $_productCollection;
$this->_category = $_category;
}
/**
* @return int
*/
public function getCatalogProductCount()
{
return $this->_productCollection->create()->getSize();
}
/**
* @param $categoryId
*
* @return int
*/
public function getCategoryProductCount($categoryId)
{
$size = 0;
$category = $this->_category->create()->load($categoryId);
if (isset($category) && !empty($category)) {
$size = $category->getProductCollection()->getSize();
}
return $size;
}
}

In declaring this class we are implementing the function defined within the interface.

With our implementation of getCatalogProductCount() we are simply returning a count of the number of products within the Magento installation’s catalogue.

With our implementation of getCategoryProductCount() we are also returning a product collection count, but limiting the results to only those product found within a specific category – as defined by the supplied category ID.

With our module create, it is now time to test it out.

Open a command prompt and curl your getCatalogProductCount service (i.e., curl http://local.mage2ce-dev.com/rest/V1/demac/webservice/getCatalogProductCount/). This should simply return an integer count of your entire product catalogue.

Next, let’s try getCategoryProductCount. As we defined during the creation of our module earlier, this function accept an integer input on POST for the desired category ID, and is also limited to use by administrators only.

First, let’s curl the request without the admin credentials to verify our security precautions work (i.e., curl -X POST -F “categoryId=2” “http://local.mage2ce-dev.com/rest/V1/demac/webservice/getCategoryProductCount/”).

You should receive a JSON response describing that the consumer is not authorized to access the Magento_Backend::admin resource. Success, we have successfully applied ACL to our API function. Alright, let’s try it again with the administrative authorization in place. To do this, first curl the following (although with your own admin account credentials, of course):

curl -X POST “http://local.mage2ce-dev.com/rest/V1/integration/admin/token” -H “Content-Type:application/json” -d ‘{“username”:”hfinch”, “password”:”dashwood”}’

This should return an admin token. Use this token for the next request. In this example, I am using Category ID #2:

curl -X POST -H “Authorization: Bearer ADMINTOKENGOESHERE” -F “categoryId=2” “http://local.mage2ce-dev.com/rest/V1/demac/webservice/getCategoryProductCount/”

You must include the “Bearer” text in your authorization header. Using this admin token, you’ll should now meet the ACL restrictions and be able to successfully run this request through the REST API. You should receive another integer count in return.

And that’s it. You have now successfully created (and used) a module to add new functionality to Magento’s REST API. I hope this tutorial may provide you with some valuable insight into further exploring and extending Magento’s REST API to better server your business needs. Using this module and these techniques as a basic framework, you can add additional functions and more complicated queries – all while maintaining appropriate levels of access to each resource.