Creating a New REST Web Service in Magento 2

Soap RestIn 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 the add() 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 function midPoint() 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.

78 comments

  1. Bojjaiah · · Reply

    Hi Alen,

    Here good to see the articles.

    Please let me know how to invoke magento 2 SOAP Token/OAuth/Session based API (example: Customer API).

    or

    Please give me some reference sites

    Thanks.

    1. Hi you looked at devdocs.magento.com? http://devdocs.magento.com/guides/v2.0/get-started/authentication/gs-authentication.html talks about the three authentication techniques. You can also try http://magento.stackexchange.com/questions/49730/magento-2-wsdl-link. If still no luck let me know and I will knock up a blog post.

      1. Bojjaiah · ·

        Hi Alan,

        Yes I have fallowed http://devdocs.magento.com/guides/v2.0/get-started/soap/soap-web-api-calls.html to invoke SOAP Web Api as given on example, but I’m unable to get results even I posted the thread on http://magento.stackexchange.com/questions/76569/class-magento-testframework-testcase-webapi-adapter-soap-not-found-in-magento, but unable to get results.

        By entering http://192.168.0.52/magento-2/soap/?services=customerAccountManagementV1&wsdl this it’s displaying wsdl fine but how to invoke this?

        In Magento 1.0 we fallowed like below code to get a result

        $client = new SoapClient(‘http://magentohost/api/soap/?wsdl’);

        // If somestuff requires api authentification,
        // then get a session token
        $session = $client->login(‘apiUser’, ‘apiKey’);

        $result = $client->call($session, ‘customer.list’);
        var_dump ($result);

        So how can i do this in Magento 2.0?

        Can you please explain me with example (if possible) how to invoke REST & SOAP API’s?

        Thanks.

      2. I am about to hop in a plane, so might be few days delay replying – harass me in a few days time if I don’t follow up!

      3. Hi Alan,

        How can i do above thread in Magento 2.0?

        Can you please explain me with example (if possible) how to invoke Magento 2 REST & SOAP API’s?

  2. Bojjaiah · · Reply

    Hi Alan,

    Could you please explain me how to invoke Magento 2 API with examples?

    Thanks.

  3. Magento · · Reply

    Hi Alan,

    From your article I’m able to create Rest API.

    Can you please provide how to create SOAP API with example.

    1. Sorry, been pretty busy to have a go myself, but https://github.com/magento/magento2/issues/2002 might contain useful information to help.

  4. Hi Alan,

    I have created Custom Api which returns associativeArray.

    If I use associativeArray it’s giving error
    If I use string[] it’s working

    But I want to return associativeArray.

    Error: Class “array” does not exist. Please note that namespace must be specified.

    Example Methods:

    /**
    * Create multi Customers
    *
    * @param \Magento\Customer\Api\Data\CustomerInterface $customer
    * @return string[]
    */
    public function save(\Magento\Customer\Api\Data\CustomerInterface $customer);

    save method working fine.

    /**
    * Create multi Customers
    *
    * @param \Magento\Customer\Api\Data\CustomerInterface $customer
    * @return array()|associativeArray()
    */
    public function save1(\Magento\Customer\Api\Data\CustomerInterface $customer);

    save1 method returning error.

    Error: Class “array()” does not exist. Please note that namespace must be specified

    So how can I return associativeArray() or array() ?

    Thanks in advance.

    1. Service contracts do not support all types. You cannot use array() because we need to know what it is an array of, and associative array currently is not supported at all (we only support data interfaces which is serialized as a JSON object with name/value pairs.)

      1. K.Thank You for your information.

  5. Hi Alan,

    I developed a Custom API that require an array as parameter for an Entity: how can i pass the array to the service? If I pass to the service the following json:

    {
    “foo”: {
    “property1″:”one”,
    “property2”:[1,2,3,4,55]
    }
    }

    I get an error:

    {
    “message”: “Class array does not exist”,
    “code”: -1,
    “trace”: “#0 /var/www/magento2/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php(136): ReflectionClass->__construct(‘array’)\n#1….

    Please help me!

    1. What is php declaration? We do support arrays.

    2. Hi,

      Can you post your code? so that we can clarify.

      I think return type array/associateArray is not supporting.

    3. To expand, you cannot say “@return array”. You need to do something like “@return CustomerInterface[]”. You need to be explicit about the type of values in the array. Also we do not support associateArray” directly. You have to declare a data interface (one with setters/getters). Each setter/getter turns into a JSON object field (which is like keys of an associate array).

  6. Hi Alan,

    thank for your reply.

    I declared in /Api/Data/FooInterface

    /**
    * @param array
    * @return $this
    */
    public function setProperty2(array $property2 = null);

    In /Model/Data/Foo.php I have:

    /**
    * @param array
    * @return $this
    */
    public function setProperty2(array $property2 = NULL)
    {
    return $this->setData(self::PROPERTY2, $property2);
    }

    If i use in a controller:
    $foo = $this->_objectManager->create(‘\….\…\Api\Data\FooInterface’);
    $foo->setProperty2(array(1,3,4));

    $this->fooRepository->save($foo);

    I can save the entity without any problem, but if I use the rest API I got the following error:

    “#0 /var/www/magento2/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php(136): ReflectionClass->__construct(‘array’)\n#1 /var/www/magento2/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php(274): Magento\Framework\Webapi\ServiceInputProcessor->_createFromArray(‘array’, Array)\n#2 /var/www/magento2/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php(161): Magento\Framework\Webapi\ServiceInputProcessor->_convertValue(Array, ‘array’)\n#3 /var/www/magento2/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php(274): Magento\Framework\Webapi\ServiceInputProcessor->_createFromArray(‘MyName\MyMod…’, Array)

    1. For rest API bindings we must be told the exact type. You cannot use “array”. You must use something like CustomerInterface[].

      1. Thanks Alan for your patience. So should i create an Interface for a single array? Inspecting Magento\Customer\Api\Data\AddressInterface I found:

        /**
        * Set street
        *
        * @api
        * @param string[] $street
        * @return $this
        */
        public function setStreet(array $street);

      2. If you want a json object, you need a data interface (setters/getters). Array of string does not need it, like what you saw

      3. Thanks Alan. So now I have to declare a \Api\Data\Property2Interface (with a \Model\Data\Property2.php and a new record in the di.xml). Inside \Api\Data\Property2Interface I have to define a setter/getter “property” with type \Api\Data\Property2Interface[] and the in my json I will have to write something like:

        {
        “foo”: {
        “property1”: “one”,
        “property2”: {
        “property”: [
        1,
        2,
        3,
        4,
        55
        ]
        }
        }
        }

        Am I right?

      4. If you want property 2 to be an array of strings, you declare it as string[]. If you want an array of JSON objects with a member “property” set to an array of integers then you need a new data interface. That is

        * JSON object ({…}) needs a new data interface
        * JSON array uses [] after the type
        * JSON string (“…”), integer (0, 1, 3), float (0.1, 43.52), bool (true, false) map to the same PHP types.

        So if you want an array of integers, use int[]. If you want an array of JSON objects, use Property2Interface[] where Property2Interface as a single setProperty(int)/getProperty() pair of methods.

        Oh, and a function call with arguments also maps to a JSON object (function parameter names = member names in JSON object).

  7. Hi Alan,
    I have installed fresh magento2 in my windows system. I have got the following error while accessing the WSDL file by soap method(http://127.0.0.1/magento2/soap/default?wsdl=1&services=catalogProductRepositoryV1)
    Class “array[]” does not exist. Please note that namespace must be specified.
    #0 D:\xampp\htdocs\magento2\app\code\Magento\Webapi\Model\Soap\Config\ClassReflector.php(92): Magento\Framework\Reflection\TypeProcessor->register(‘array[]’)

    Suggestion Please

    Thanks in Advance

    1. I would submit as a bug on GitHub.

  8. Hi Alan,

    Have you got any solution for this?

    1. Sorry, lots of comments, which issue were you referring to? The last issue I recommended submitting a bug on github for the devs to investigate. Or was it something else?

  9. Alan,

    I’m having a hard time getting the first part of the demo to work (up to the 1+2 = 3 part). I copied and pasted all of your examples into my clean Magento2 install and I keep getting this error:

    Request does not match any route.

    I have magento running over https, and I can get to the admin site and the public site. It is running over a self-signed cert, but it’s just my dev box at this point and I installed the cert so my browser would quit complaining. Both in the browser and in curl (with -k to turn off the cert checks) I get the same error as stated above.

    Any advice? I would really appreciate it.

    Best,
    Dave

    1. OK, I figured out my problem above. The article doesn’t mention anything about registration.php (or I missed it) or doing magento module:enable mymodule and magento setup:upgrade. I got the demo working.

      However, now I’m trying to programmatically add a store and I can’t figure it out. I found this article:
      http://stackoverflow.com/questions/8309076/how-to-create-a-site-store-and-view-programatically-in-magento/8313029#8313029

      but the solution is written for Magento1, and doesn’t work in Magento2 apparently. Can you point me to some documentation for Magento2 that would show how to programmatically create a store? I noticed the API doesn’t have store creation functionality (or I haven’t found it at least), so I need to extend the API to allow me to create stores.

      Thanks! 🙂

      Best,
      Dave

      1. OK, I figured it out by looking at the code. I still think better documentation is in order, but I can contribute to it here I guess. Here’s what worked for me:
        objectManager = ObjectManager::getInstance();
        }

        /**
        * Create a new store.
        *
        * @api
        * @param string $storeCode Left hand operand.
        * @param string $storeName Right hand operand.
        * @return string $storeCode The sum of the numbers.
        */
        public function newStore($storeCode, $storeName) {
        $websiteFactory = new WebsiteFactory($this->objectManager);
        $website = $websiteFactory->create();
        $website->setCode($storeCode)
        ->setName($storeName)
        ->save();

        $storeGroupFactory = new GroupFactory($this->objectManager);
        $storeGroup = $storeGroupFactory->create();
        $storeGroup->setWebsiteId($website->getId())
        ->setName($storeName)
        ->setRootCategoryId(‘default_category’)
        ->save();
        $storeFactory = new StoreFactory($this->objectManager);
        $store = $storeFactory->create();
        $store->setCode($storeCode)
        ->setWebsiteId($storeGroup->getWebsiteId())
        ->setGroupId($storeGroup->getId())
        ->setName($storeName)
        ->setIsActive(1)
        ->save();
        return $store->getCode();
        }
        }

        Note that this makes the website, storegroup, and store all with the same code. I haven’t figured out yet if that’s OK, but it shows up in the admin interface as having been created, so it looks like it works. 🙂

      2. The idea is to declare the factories as an argument to the constructor and save them away in properties. Then you call $this->whateverFactory->create(); to create a new instance. Your code ideally should never mention objectFactory directly. If you are using ‘new’ directly, that is also a hint of a possible issue. (Sometimes ‘new’ is OK, but generally for Magento classes you would use a factory to create them.)

      3. Sorry – article was written a while back (before registration.php) – I will try to resurrect it soon

      4. Not all APIs are available by service contracts yet – something we will improve over subsequent releases. In the mean time, you can declare your own APIs if you need to expose something via REST etc.

        And yes, registration.php was a later addition, as part of the support to allow modules to exist under the ‘vendor’ directory. We have a bit more work still to do, in order to allow modules to exist outside the root directory of the Magento installation – this would give developers a bit more flexibility when developing custom modules. But no ETA on when we will get to this – but it is on the backlog. Again, the registration script is how we will work out the location of the module in such cases.

  10. Hello Alan,

    I’m working to integrate Magento into my ZF2 application via the API, as you suggested in a previous response to one of my comments above. I have successfully added custom API calls to create a new website, store, and root category as well as to delete them all. Now I’m working on creating a new simple product (also a custom API call, as I saw somewhere that the SimpleProduct creation is not currently supported by the API…please correct if I’m wrong).

    I’m getting a weird error. I’m trying to pass in an array of data to the API call and it’s choking on the array being passed in. I’m getting this error:
    main.CRITICAL: exception ‘ReflectionException’ with message ‘Report ID: webapi-5671a20727a08; Message: Class array does not exist’ in /usr/local/zend/var/apps/http/magento-dev.mydomain.com/80/_docroot_/vendor/magento/framework/Webapi/ErrorProcessor.php:194

    Here are the relevant configuration bits:
    in webapi.xml:

    in ApiExtensionsInterface.php:
    /**
    * Save a piece to the store.
    *
    * @api
    * @param array $data The json data about the piece to be saved.
    * @return boolean true on success, false on failure.
    */
    public function savePiece(array $data = array());

    in ApiExtensions.php:
    /**
    * Save a piece to the store.
    *
    * @api
    * @param array $data The data about the piece to be saved.
    * @return boolean true on success, false on failure.
    */
    public function savePiece(array $data = array()) {
    return true;
    }

    I have tried @param string[] as well and got different errors, still no luck. I tried switching it to a GET in webapi.xml instead of a POST, and I can get it to work. I think the problem is specifically with the @param array part. It keeps choking on that. Please advise.

  11. OK, I figured this one out, too. The $data array I was passing in was almost all strings, but one of them was an array of strings. It was causing the built-in magento TypeProcessor to crash here:
    Message: Notice: Array to string conversion in /usr/local/zend/var/apps/http/magento-dev.mydomain.com/80/_docroot_/vendor/magento/framework/Reflection/TypeProcessor.php on line 496′

    I worked around it by serializing the data in that element and then unserializing it after it was POSTed to Magento. Is this something that could be fixed so that Magento handles it more gracefully? I would think that Magento should be able to handle complex data types being posted via JSON. Thoughts?

    Best,
    Dave

    1. We do not support all possible json data types. We support array of one type, object (name value pairs where each name has a defined type). We do not support array of mixed types as it does not map well to WSDL and other programming languages. There purposely is a restricted set of types we support. We want to be compatible with multiple languages, multiple encodings, etc. json is only one such encoding. Does that make sense?

      1. Makes sense. Thanks again, Alan!

  12. hi Alan,

    I want response in json format then in that case how to do it , now the response is in xml

    1. Sorry, just found this in my blog spam folder. Try adding the HTTP header “Accept: application/json” to your request in order to request the response in JSON. Yes, the default is XML encoded otherwise.

  13. Hi Alan, I think I should provide the link to my question on stack exchange, so you can have a much better understanding, what I want to achieve. Link: http://magento.stackexchange.com/questions/95314/category-ids-attribute-missing-while-searching-for-products-via-rest-api-rest-v ..If your tutorial is what I need to follow? I mean your tutorial provides a solution for adding two numbers using a custom API but I need to extend the already created API. Thanks

    1. Thanks – my example is simpler than what you are asking. You can extend existing web service APIs to include additional information in the response (for example), but I don’t have an example of this on my blog at this stage. In Magento 2, product types are loaded by different modules (so you can remove “bundled products” from the site for example). Some product types extend the base web API for returning product information. If the desired information is not included in the response, you may be able to extend an existing API using this sort of approach.

    1. alankent · · Reply

      I passed on to the relevant internal engineers to see if there is a bug here.

  14. Hi Alan!,
    With the help of your article, I have created several REST API for our current requirement in Magento 2.0 based project,Now I am stuck with a requirement to manage user and roles for the multiple store/website. I want to create a user for a particular role/resources for particular store/website and want to manage by REST API, Please suggest it is possible in magento2.0 or not?

    1. Have you looked at ? You can define roles in admin and associate them with services. Eg some calls are for admin only, self only, etc. Is there something missing there?

  15. Hi Alan,
    With the help of your REST API example, I have created several modules in magento2.0 now, I want to create a plugin in magento2.0 which helps to create a admin user who should restrict role/store/website wise.Is it possible in magento2.0, because as i found this feature was not present in magento1.9(although it was in magento 1.9 EE). Can you suggest me if it is possible then how should i proceed.

  16. Hi Alan,

    Seen this blog and helping many folks in there REST api’s. I am a new bee and need to work on post api’s for complete ecommerce flow. Can i get your help if you pass me one sample custom module which tells about how to add products or customers through rest api using oauth. I will be very grateful to you.

    1. I will try, but no ETA – trying to get some other projects out first. But great topic for another post in this area

  17. Sunil Prasad · · Reply

    Hi Alankent,

    Could you suggest which Rest Api used for login to magento2. What will be the process of using
    customer session and other expect of customer in outer php script with magento2.

    Your suggestion is highly appreciate.

    1. Have you read http://devdocs.magento.com/guides/v2.0/get-started/bk-get-started-api.html? You can auth using session cars, username/password, or generated keys.

  18. Daniel Navarro · · Reply

    Hi Alan,

    I missed one point.

    How Magento is able to know from the Post data in the last example: {“point1”:{“x”:10,”y”:10},”point2″:{“x”:30,”y”:50}} that parameters in the function midPoint el are of type Point?

    Thanks
    Daniel

    1. I does not work it out by looking at the JSON – it can work it out by looking at the types of function parameters. So the types of the method signature guides how to interpret the JSON.

    2. Thanks Alan!,

      Yes, that was what I expected but I cannot any method signature that tells magento the types of the parameters:

      I mean, the method signature is just:
      public function midPoint($point1, $point2);

      There are no types in the declaration of the function, nor in the definition.

      Also looking at the declaration of the api entrypoint in the file webapi.xml, I cannot see any reference to Point type either.

      So I cannot see how magento do its magic to know that $point1 and $point2 are in fact of type Point

      1. There must be a Doc Block (/** @param … */) so the code can work out the type correctly. Otherwise it cannot work it out correctly.

      2. Can try asking in just ask Alan area of forums – easier to do long replies. Basically you *must* have some thing like

        /** @param $point1 Foo\Point
        @param $point2 Foo\Point
        */
        Function x($point1,$point2)

        It won’t work otherwise

      3. Thanks Alan! I’ll try to investigate it later by myself or ask in the forums…

        It was just that I didn’t imagine magento looking at the Doc Block to find the types of the parameters. I was expecting the type hints in the function declaration. Something like:

        public function midPoint(PointInterface $point1, PointInterface $point2)…

        Or maybe some lines in the webapi.xml about the types of the parameters.

        I’ll try later and will go to the forums if I find some problem.

        Thanks!
        Daniel

  19. Thanks Alan!!

    If possible, Can you please suggest the OPEN SOURCE IDE for Magento development.

    1. I think the main IDE I see people using is PHP Storm. But there are lots of options – Eclipse has PHP support, NetBeans I think also has support, I personally grew up on vi (now vim), there are tools like codeanywhere.com which are web based development environments. PHP Storm is a paid product, but it is the main tool I hear of being used with Magento development.

  20. Thanks for this article Alan, very helpful. I was wondering if you might be able to comment on what the correct way of returning API errors is for rest endpoints? For example when I want to return a 404 not found for a resource, or InvalidInput etc.

    In Magento 1 I believe we would’ve done something along the lines of throwing a Mage_API_Error, but I haven’t quite found the analogous method in Magento 2.

    Thanks!

  21. Manish Kumar · · Reply

    Hello Alan,

    $returnData = [
    ‘banner’ => $bannerSlider,
    ‘categoryBanner’ => $categoryBanner,
    ‘topchoicebanner’ => $topChoiceBanner,
    ‘newproduct’ => $newProductArray,
    ‘popularMenuBanner’ => $popularMenus
    ];

    return returnData;

    this return an array, how can I return as json object instead of array.

    1. Have a look in Api/Data directories. Declare an interface with setters/getters and return that object. We will then serialize as json object

  22. magepsychogh · · Reply

    This is really a good practical example of creating new web-services in Magento.
    I do have some questions though.

    Questions:

    – Why methods are limited for anonymous users? If this is the case how protected methods can be tested with tools like SoapUI ?
    – How customer is authenticated with SOAP web services? Any other workaround? Even we are not getting password_hash in any response.

    1. There is an authentication header in requests for authenticated connections. Try looking in the Web API guide – http://devdocs.magento.com/guides/v2.1/get-started/authentication/gs-authentication.html

      1. magepsychogh · ·

        Not sure if that’s the one I am looking for. But will definitely give a try.
        Main thing what I am trying to do is to login the mobile user using SOAP webservice if they enter the username and password, just like we have in web: /customer/account/login.

  23. Oved Yavine · · Reply

    I created my REST api successfully, but how do i secure it? i want it to follow the same security that Magent2o use for it’s own api. only registered users should call my custom API

    can you guide?

    1. The Magento built in security is exactly the same as what everyone else gets access to. http://devdocs.magento.com/guides/v2.0/get-started/authentication/gs-authentication.html is the docs, but if you find our config files you can copy what is in them to mimic what we do for security.

  24. Hello Alan, I am trying to learn how to use rest API in Magento 2, and found your example easy to follow. However, I did the Example 1, but it’s not working.
    I have Magento 2.1.2 installed over WAMP server. It’s working fine, and cURL it’s also working fine. But when I create the files and folder structure, and then invoke curl, I don’t get the expected result. Indeed, on CMD, I do curl http://127.0.0.1/index.php/rest/V1/calculator/add/1/2, but then I get the index page of wamp, which basically is the wamp’s welcome page; since my Magento URL is 127.0.0.1/magento, I tried to invoke curl http://127.0.0.1/magento/rest/V1/calculator/add/1/2 instead. But then I get the error ‘{“message”:”Request does not match any route.”,”trace”:null}’.

    I saw in another comment that maybe registration.php is missing; maybe composer does too?

    I am new to Magento and I’m trying to learn by myself, and your tutorial for rest API seems the easiest to follow. Could you please give your opinion on why it’s not working for me?

    Thank you

    1. +1, I’m having the same issue (also running on Magento 2.1.2)

      1. sorry,my bad.Found out there is typo somewhere.
        This articles helps me a lot to understand how to create a custom web api in Magento

      2. TeYou Huang, could you please share the typo you found?

    2. I will try to have a look soon. I have a few deadlines – feel free to harass me in a week if I have not got back to you.

  25. Hola Alan al igual que TeYou Huang tengo el mismo problema tome su ejemplo he intente probar este servicio y me arroja el mismo error. Uso Magento 2.1.2 con un SO Ubuntu 16.06.

    1. Translated “Hello Alan as well as TeYou Huang I have the same problem take your example and try to try this service and throw me the same error. Use Magento 2.1.2 with an Ubuntu OS 16.06 OS.”

  26. Hello Alan as well as TeYou Huang I have the same problem take your example and try to try this service and throw me the same error. Use Magento 2.1.2 with an Ubuntu OS 16.06 OS.

    1. There may have been some changes since the version when I published the post. Its a problem with blog posts – they are point in time. Have you tried reading the Magento devdocs? We keep that up to date with current versions.

      Or I need to do a new version of the blog post maybe 😉

  27. Martin Pfister · · Reply

    Hi Alan. Great Post. I was able to follow you examples until the function midPoint. Problem is that compile (magento setup:di:compile) constatly throws Exceptions related to the Factory generation:

    [ReflectionException]
    Class \CaluclatorWebService\Api\Data\PointInterfaceFactory does not exist
    [ReflectionException]
    Class \CaluclatorWebService\Api\Data\PointInterfaceFactory does not exist
    [RuntimeException]
    Source class “\\CaluclatorWebService\Api\Data\PointInterface” for “\CaluclatorWebService\Api\Data\PointInterfaceFactory” generation does not exist.

    I’m expecting that this class will be automatically created by the Magento framework. I also read DevDocs Code Generation http://devdocs.magento.com/guides/v2.0/extension-dev-guide/code-generation.html and DevDocs Factories: http://devdocs.magento.com/guides/v2.0/extension-dev-guide/factories.html.

    Except the Module Name i used excately you code. If there is no typo (checked several times) i have no idea why Factory is not generated.

    Thanks for you help.

    1. I need to go back and review – I think something may have changed for 2.1 etc, I just have not had a chance to get back to this.

  28. Saved my day. Works fine. Thank you so much

  29. vishal bansal · · Reply

    Hi Team , how we can get this response by GET method API:

    {
    “data”: [
    {
    “success”: [
    {
    “code”: 200,
    “response”: {
    “count”: 9,
    “shapes”: [
    {
    “id”: “shape1”,
    “thunbnail_image”: “http://beta.artoreal.com/media/mobileappdata/shapes/Thumb_1.png”,
    “original_image”: “”
    },
    {
    “id”: “shape2”,
    “thunbnail_image”: “http://beta.artoreal.com/media/mobileappdata/shapes/Thumb_2.png”,
    “original_image”: “”
    },
    {
    “id”: “shape3”,
    “thunbnail_image”: “http://beta.artoreal.com/media/mobileappdata/shapes/Thumb_3.png”,
    “original_image”: “”
    },
    {
    “id”: “shape4”,
    “thunbnail_image”: “http://beta.artoreal.com/media/mobileappdata/shapes/Thumb_4.png”,
    “original_image”: “http://beta.artoreal.com/media/mobileappdata/shapes/4.png”
    },
    {
    “id”: “shape5”,
    “thunbnail_image”: “http://beta.artoreal.com/media/mobileappdata/shapes/Thumb_5.png”,
    “original_image”: “http://beta.artoreal.com/media/mobileappdata/shapes/5.png”
    },
    {
    “id”: “shape6”,
    “thunbnail_image”: “http://beta.artoreal.com/media/mobileappdata/shapes/Thumb_6.png”,
    “original_image”: “http://beta.artoreal.com/media/mobileappdata/shapes/6.png”
    },
    {
    “id”: “shape7”,
    “thunbnail_image”: “http://beta.artoreal.com/media/mobileappdata/shapes/Thumb_7.png”,
    “original_image”: “http://beta.artoreal.com/media/mobileappdata/shapes/7.png”
    },
    {
    “id”: “shape8”,
    “thunbnail_image”: “http://beta.artoreal.com/media/mobileappdata/shapes/Thumb_8.png”,
    “original_image”: “http://beta.artoreal.com/media/mobileappdata/shapes/8.png”
    },
    {
    “id”: “shape9”,
    “thunbnail_image”: “http://beta.artoreal.com/media/mobileappdata/shapes/Thumb_9.png”,
    “original_image”: “http://beta.artoreal.com/media/mobileappdata/shapes/9.png”
    }
    ]
    }
    }
    ]
    }
    ]
    }

    1. Hi. I would check with the Magento team now – the product may have changed since I left Magento a while back.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.