CREATING A NEW REST WEB SERVICE IN MAGENTO 2
In this post I work through a working example of how to create a new REST web service in Magento 2. My goal is to show how easy it is define a new RESTful service. The examples start with a simple integers, moves on to arrays, then ends with a more complex data type to pass into and return from a service call. This post focusses on JSON, but REST with XML and SOAP are also supported by Magento 2. Full documentation can be found on http://devdocs.magento.com under the “Web Services Developer”.
To keep the following examples as simple as possible, the new APIs perform some mathematical operations on input parameters. There are no database tables for storage, and no authentication. (Magento 2 supports three forms of authentication, as described in the full documentation on the site. The three forms are suitable for AJAX calls from the user’s web session, from a mobile device, and from an external application where an authentication token must be stored in a file on disk.)
Example 1: Add 2 Numbers
The first example is to add two integers, where the two arguments are passed on the URL. This illustrates how to extract parameters from the URL, and shows a very simple function prototype. I will show all the files first, then explain some of the less obvious points. Some parts are omitted as they will be introduced below in later steps.
I used the vendor name “AlanKent” for my module, with a module name of “CalculatorWebService”.
app/code/AlanKent/CalculatorWebService/etc/module.xml
<?xml version="1.0"?> <!-- /** * @copyright Copyright (c) 2015 X.commerce, Inc. (http://www.magentocommerce.com) */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/module.xsd"> <module name="AlanKent_CalculatorWebService" setup_version="2.0.0"/> </config>
app/code/AlanKent/CalculatorWebService/etc/di.xml
<?xml version="1.0"?> <!-- /** * Copyright 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd"> <preference for="AlanKent\CalculatorWebService\Api\CalculatorInterface" type="AlanKent\CalculatorWebService\Model\Calculator" /> </config>
app/code/AlanKent/CalculatorWebService/etc/webapi.xml
<?xml version="1.0"?> <!-- /** * Copyright 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ --> <routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../app/code/Magento/Webapi/etc/webapi.xsd"> <!-- Example: curl http://127.0.0.1/index.php/rest/V1/calculator/add/1/2 --> <route url="/V1/calculator/add/:num1/:num2" method="GET"> <service class="AlanKent\CalculatorWebService\Api\CalculatorInterface" method="add"/> <resources> <resource ref="anonymous"/> </resources> </route> </routes>
app/code/AlanKent/CalculatorWebService/Api/CalculatorInterface.php
<?php /** * Copyright 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ namespace AlanKent\CalculatorWebService\Api; use AlanKent\CalculatorWebService\Api\Data\PointInterface; /** * Defines the service contract for some simple maths functions. The purpose is * to demonstrate the definition of a simple web service, not that these * functions are really useful in practice. The function prototypes were therefore * selected to demonstrate different parameter and return values, not as a good * calculator design. */ interface CalculatorInterface { /** * Return the sum of the two numbers. * * @api * @param int $num1 Left hand operand. * @param int $num2 Right hand operand. * @return int The sum of the numbers. */ public function add($num1, $num2); }
app/code/AlanKent/CalculatorWebService/Model/Calculator.php
<?php /** * Copyright 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ namespace AlanKent\CalculatorWebService\Model; use AlanKent\CalculatorWebService\Api\CalculatorInterface; /** * Defines the implementaiton class of the calculator service contract. */ class Calculator implements CalculatorInterface { /** * Return the sum of the two numbers. * * @api * @param int $num1 Left hand operand. * @param int $num2 Right hand operand. * @return int The sum of the two values. */ public function add($num1, $num2) { return $num1 + $num2; } }
Description
The above is the complete definition of a new module exposing a new REST web service to add two numbers. Briefly the files have the following purposes.
- etc/module.xml – declares the module, it’s name, it’s dependencies, and it’s DB schema version (currently needed even if there is no DB).
- etc/di.xml – declares that any request for the calculator service contract interface can be satisfied by instantiating the calculator class. (Other modules could swap in a new implementation by overriding this preference.
- etc/webapi.xml – binds a URL with two parameters as path segments to the add() method of the calculator interface. “Resources” is the Magento terminology for access control lists – “anonymous” means anyone can access the service.
- Api/CalculatorInterface.php – declares a PHP interface with an add(num1, num2) method. Note that the PHP doc for the method is very important, as it is used to determine the types of the function arguments and return value.
- Model/Calculator.php – declares a class implementing the interface (it actually implements theadd() function).
The Magento framework will then parse the URL, extract the arguments from the URL, and provide as arguments to the add() function call. Here is a sample of running the service using CURL.
$ curl http://127.0.0.1/index.php/rest/V1/calculator/add/1/2 3
The return value will be “3”, rather than a full JSON object. This is because the return value is a simple type (an integer).
Note there is no code required to serialize or deserialize JSON – that is done by the Magento framework. The same code will work with SOAP by changing the etc/webapi.xml file.
And that is it. A complete definition of a module that defines a web service for other applications to access in 5 fairly short files.
Example 2: Sum an Array of Floating-point Numbers
Let’s now add a new method where we POST a JSON object holding an array of values to be added up, with the total sum returned. This is fairly straightforward, as Magento knows also how to encode/decode arrays and objects as well. The additional lines are highlighted in bold.
app/code/AlanKent/CalculatorWebService/etc/webapi.xml
<?xml version="1.0"?> <!-- /** * Copyright 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ --> <routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../app/code/Magento/Webapi/etc/webapi.xsd"> <!-- Example: curl http://127.0.0.1/index.php/rest/V1/calculator/add/1/2 --> <route url="/V1/calculator/add/:num1/:num2" method="GET"> <service class="AlanKent\CalculatorWebService\Api\CalculatorInterface" method="add"/> <resources> <resource ref="anonymous"/> </resources> </route> <!-- Example: curl -d '{"nums":[1.1,2.2,3.3]}' -H 'Content-Type: application/json' http://127.0.0.1/index.php/rest/V1/calculator/sum --> <route url="/V1/calculator/sum" method="POST"> <service class="AlanKent\CalculatorWebService\Api\CalculatorInterface" method="sum"/> <resources> <resource ref="anonymous"/> </resources> </route> </routes>
app/code/AlanKent/CalculatorWebService/Api/CalculatorInterface.php
<?php /** * Copyright 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ namespace AlanKent\CalculatorWebService\Api; use AlanKent\CalculatorWebService\Api\Data\PointInterface; /** * Defines the service contract for some simple maths functions. The purpose is * to demonstrate the definition of a simple web service, not that these * functions are really useful in practice. The function prototypes were therefore * selected to demonstrate different parameter and return values, not as a good * calculator design. */ interface CalculatorInterface { /** * Return the sum of the two numbers. * * @api * @param int $num1 Left hand operand. * @param int $num2 Right hand operand. * @return int The sum of the numbers. */ public function add($num1, $num2); /** * Sum an array of numbers. * * @api * @param float[] $nums The array of numbers to sum. * @return float The sum of the numbers. */ public function sum($nums); }
app/code/AlanKent/CalculatorWebService/Model/Calculator.php
<?php /** * Copyright 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ namespace AlanKent\CalculatorWebService\Model; use AlanKent\CalculatorWebService\Api\CalculatorInterface; /** * Defines the implementaiton class of the calculator service contract. */ class Calculator implements CalculatorInterface { /** * Return the sum of the two numbers. * * @api * @param int $num1 Left hand operand. * @param int $num2 Right hand operand. * @return int The sum of the two values. */ public function add($num1, $num2) { return $num1 + $num2; } /** * Sum an array of numbers. * * @api * @param float[] $nums The array of numbers to sum. * @return float The sum of the numbers. */ public function sum($nums) { $total = 0.0; foreach ($nums as $num) { $total += $num; } return $total; } }
Description
As can be seen, you simply add a new method to the interface and class implementing the interface, then add a new route in the webapi.xml file to bind a URL to the new sum() method. This new service can be invoked as follows:
$ curl -d '{"nums":[1.1,2.2,3.3]}' -H 'Content-Type: application/json' http://127.0.0.1/index.php/rest/V1/calculator/sum 6.6
Instead of passing parameters on the URL, this example sends a JSON object with a HTTP POST to the specified URL. The arguments are encoded in a JSON object, with a field per function call parameter. In this case, the function sum() is declared with a single parameter “nums” (see the interface definition) which is an array of floating point numbers. The framework again does all the JSON parsing of input data and encoding of the return value, handling a range of simple types..
Example 3: Mid-point Between two Points
As an example of passing in and returning a more complex JSON object, consider a functionmidPoint() that takes two Point instances (holding x and y coordinate values) and returns a new Point instance that represents the mid-point between the two coordinates. Again, I will show the code first with changes from example 2 in bold.
app/code/AlanKent/CalculatorWebService/etc/di.xml
<?xml version="1.0"?> <!-- /** * Copyright 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd"> <preference for="AlanKent\CalculatorWebService\Api\CalculatorInterface" type="AlanKent\CalculatorWebService\Model\Calculator" /> <preference for="AlanKent\CalculatorWebService\Api\Data\PointInterface" type="AlanKent\CalculatorWebService\Model\Point" /> </config>
app/code/AlanKent/CalculatorWebService/etc/webapi.xml
<?xml version="1.0"?> <!-- /** * Copyright 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ --> <routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../app/code/Magento/Webapi/etc/webapi.xsd"> <!-- Example: curl http://127.0.0.1/index.php/rest/V1/calculator/add/1/2 --> <route url="/V1/calculator/add/:num1/:num2" method="GET"> <service class="AlanKent\CalculatorWebService\Api\CalculatorInterface" method="add"/> <resources> <resource ref="anonymous"/> </resources> </route> <!-- Example: curl -d '{"nums":[1.1,2.2,3.3]}' -H 'Content-Type: application/json' http://127.0.0.1/index.php/rest/V1/calculator/sum --> <route url="/V1/calculator/sum" method="POST"> <service class="AlanKent\CalculatorWebService\Api\CalculatorInterface" method="sum"/> <resources> <resource ref="anonymous"/> </resources> </route> <!-- Example: curl -d '{"point1":{"x":10,"y":10},"point2":{"x":30,"y":50}}' -H 'Content-Type: application/json' http://127.0.0.1/index.php/rest/V1/calculator/midpoint --> <route url="/V1/calculator/midpoint" method="POST"> <service class="AlanKent\CalculatorWebService\Api\CalculatorInterface" method="midPoint"/> <resources> <resource ref="anonymous"/> </resources> </route> </routes>
app/code/AlanKent/CalculatorWebService/Api/CalculatorInterface.php
<?php /** * Copyright 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ namespace AlanKent\CalculatorWebService\Api; use AlanKent\CalculatorWebService\Api\Data\PointInterface; /** * Defines the service contract for some simple maths functions. The purpose is * to demonstrate the definition of a simple web service, not that these * functions are really useful in practice. The function prototypes were therefore * selected to demonstrate different parameter and return values, not as a good * calculator design. */ interface CalculatorInterface { /** * Return the sum of the two numbers. * * @api * @param int $num1 Left hand operand. * @param int $num2 Right hand operand. * @return int The sum of the numbers. */ public function add($num1, $num2); /** * Sum an array of numbers. * * @api * @param float[] $nums The array of numbers to sum. * @return float The sum of the numbers. */ public function sum($nums); /** * Compute mid-point between two points. * * @api * @param AlanKent\CalculatorWebService\Api\Data\PointInterface $point1 The first point. * @param AlanKent\CalculatorWebService\Api\Data\PointInterface $point2 The second point. * @return AlanKent\CalculatorWebService\Api\Data\PointInterface The mid-point. */ public function midPoint($point1, $point2); }
app/code/AlanKent/CalculatorWebService/Api/Data/PointInterface.php
<?php /** * Copyright 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ namespace AlanKent\CalculatorWebService\Api\Data; /** * Defines a data structure representing a point, to demonstrating passing * more complex types in and out of a function call. */ interface PointInterface { /** * Get the x coordinate. * * @api * @return float The x coordinate. */ public function getX(); /** * Set the x coordinate. * * @api * @param $value float The new x coordinate. * @return null */ public function setX($value); /** * Get the y coordinate. * * @api * @return float The y coordinate. */ public function getY(); /** * Set the y coordinate. * * @api * @param $value float The new y coordinate. * @return null */ public function setY($value); }
app/code/AlanKent/CalculatorWebService/Model/Calculator.php
<?php /** * Copyright 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ namespace AlanKent\CalculatorWebService\Model; use AlanKent\CalculatorWebService\Api\CalculatorInterface; use AlanKent\CalculatorWebService\Api\Data\PointInterface; use AlanKent\CalculatorWebService\Api\Data\PointInterfaceFactory; /** * Defines the implementation class of the calculator service contract. */ class Calculator implements CalculatorInterface { /** * @var PointInterfaceFactory * Factory for creating new Point instances. This code will be automatically * generated because the type ends in "Factory". */ private $pointFactory; /** * Constructor. * * @param PointInterfaceFactory Factory for creating new Point instances. */ public function __construct(PointInterfaceFactory $pointFactory) { $this->pointFactory = $pointFactory; } /** * Return the sum of the two numbers. * * @api * @param int $num1 Left hand operand. * @param int $num2 Right hand operand. * @return int The sum of the two values. */ public function add($num1, $num2) { return $num1 + $num2; } /** * Sum an array of numbers. * * @api * @param float[] $nums The array of numbers to sum. * @return float The sum of the numbers. */ public function sum($nums) { $total = 0.0; foreach ($nums as $num) { $total += $num; } return $total; } /** * Compute mid-point between two points. * * @api * @param PointInterface $point1 The first point. * @param PointInterface $point2 The second point. * @return PointInterface The mid-point. */ public function midPoint($point1, $point2) { $point = $this->pointFactory->create(); $point->setX(($point1->getX() + $point2->getX()) / 2.0); $point->setY(($point1->getY() + $point2->getY()) / 2.0); return $point; } }
app/code/AlanKent/CalculatorWebService/Model/Point.php
<?php /** * Copyright 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ namespace AlanKent\CalculatorWebService\Model; use AlanKent\CalculatorWebService\Api\Data\PointInterface; /** * Defines a data structure representing a point, to demonstrating passing * more complex types in and out of a function call. */ class Point implements PointInterface { private $x; private $y; /** * Constructor. */ public function __construct() { $this->x = 0.0; $this->y = 0.0; } /** * Get the x coordinate. * * @api * @return float The x coordinate. */ public function getX() { return $this->x; } /** * Set the x coordinate. * * @api * @param $value float The new x coordinate. * @return null */ public function setX($value) { $this->x = $value; } /** * Get the y coordinate. * * @api * @return float The y coordinate. */ public function getY() { return $this->y; } /** * Set the y coordinate. * * @api * @param $value float The new y coordinate. * @return null */ public function setY($value) { $this->y = $value; } }
Description
To invoke the service using CURL, you can use the following command.
curl -d '{"point1":{"x":10,"y":10},"point2":{"x":30,"y":50}}' -H 'Content-Type: application/json' http://127.0.0.1/index.php/rest/V1/calculator/midpoint {"x":20,"y":30}
The -d option causes CURL to do a POST of the provided JSON object (two arguments called point1 and point2). The returned object is also a Point instance with x and y coordinates. Running quickly through the changes again,
- etc/di.xml – adds the class reference for Points.
- etc/webapi.xml – adds the new URL to function call binding.
- Api/CalculatorInterface.php – adds the new mid-point function.
- Api/Data/PointInterface.php – an important new file, acting as the definition of the encoding and decoding of JSON objects. This class is in the Api/Data directory to identify it as an interface for holding a data structure for function call arguments and/or return values.
- Module/Calculator.php – has the new method added, but also now has a constructor that takes a ‘PointInterfaceFactory’ argument. I have not supplied this file as the Magento framework will automatically create the class for us. It will have a single create() method that returns an instance of the requested Point instance, but calling the Magento object manager (which will examine the di.xml file to work out the right class to create for PointInterface).
- Module/Point.php – contains setters and getters to manipulate a ‘Point’ instance (an x, y pair).
Conclusions
This post does not try to explain every concept with writing a web service – please see the online documentation http://devdocs.magento.com/ for that. The goal of this was to provide a few simple examples of writing your own web service, to demonstrate how easy it is to define a new set of functions and expose them as REST (or SOAP) web services. I did not cover how to use SOAP instead of REST+JSON, and I also did not cover how authentication works. Please consult the manuals for this additional information.