Magento 2 API Response Field Filtering

I recently got started with some work integrating some older systems with Magento 2 using their new REST API. Right at the top of the Getting Started with Magento 2 APIs Introduction there's a list of features, including this one:

  • The framework supports field filtering of web api responses to conserve mobile bandwidth.

That sounds nice. Let's get some of those responses down to just the fields we need, and speed up the data transfer just a bit!

So I read through all of the documentation. I couldn't find any details on how to filter the fields of the API responses. Google searches also turned up nothing.

I went digging through the code in /app/code and found nothing. A bit more poking around and I found a docblock in: /lib/internal/Magento/Framework/Webapi/Rest/Response/FieldsFilter.php for the parse() method.

   /**
     * Parse filter string into associative array. Field names are returned as keys with values for scalar fields as 1.
     *
     * @param string $filterString
     * <pre>
     *  ex. customer[id,email],addresses[city,postcode,region[region_code,region]]
     * </pre>
     * @return array|null
     * <pre>
     *  ex.
     * array(
     *      'customer' =>
     *           array(
     *               'id' => 1,
     *               'email' => 1,
     *               ),
     *      'addresses' =>
     *           array(
     *               'city' => 1,
     *               'postcode' => 1,
     *                   'region' =>
     *                       array(
     *                           'region_code' => 1,
     *                           'region' => 1,
     *                         ),
     *               ),
     *      )
     * </pre>
     *
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
     */

Also, at the top of that class definition, there’s a constant:

    const FILTER_PARAMETER = 'fields';

Another key clue.

It's actually really simple: Just add a query string to your REST URL with the variable name of ‘fields’ and with a value of the list of fields you want separated by commas, with optional array notation for nested data elements.

Here's a simple code snippet to test this functionality, running against rgranado’s mage2_vagrant box. It doesn't use any nice object oriented libraries—just core PHP functions that should work on any machine for purposes of illustration, and to give you something you can quickly hack on.

<?php

// First log in
$url = 'http://mage2.dev/rest/V1/integration/admin/token';
$body = '{"username":"admin", "password":"password123"}';
$header = 'Content-type: application/json';
$options = [    'method' => 'POST',
                'content' => $body,
                'header' => $header
           ];
$context = stream_context_create(['http' => $options]);
$token = json_decode(file_get_contents($url, false, $context));

// Now dump a full product
$url = 'http://mage2.dev/rest/V1/products/24-mb01';
$header = 'Authorization: Bearer ' . $token . "\r\n"
. 'Content-type: application/json';
$options = [    'method' => 'GET',
                'header' => $header
           ];
$context = stream_context_create(['http' => $options]);
print file_get_contents($url, false, $context) . "\n";

// Now get the same product with fewer fields
$url = 'http://mage2.dev/rest/V1/products/24-mb01'
       . '?fields=id,name,sku,extension_attributes[stock_item[is_in_stock]]';
$header = 'Authorization: Bearer ' . $token . "\r\n"
          . 'Content-type: application/json';
$options = [    'method' => 'GET',
                'header' => $header
           ];
$context = stream_context_create(['http' => $options]);
print file_get_contents($url, false, $context) . "\n";

The first line it outputs is a full product record:

$ php filter-test.php |head -1 |python -mjson.tool

 {
    "attribute_set_id": 15,
    "created_at": "2016-02-08 20:35:35",
    "custom_attributes": 
        {
            "attribute_code": "description",
            "value": "<p>The sporty Joust Duffle Bag can't be beat - not in the gym, not on the luggage carousel, not anywhere. Big enough to haul a basketball or soccer ball and some sneakers with plenty of room to spare, it's ideal for athletes with places to go.<p>\n<ul>\n<li>Dual top handles.</li>\n<li>Adjustable shoulder strap.</li>\n<li>Full-length zipper.</li>\n<li>L 29\" x W 13\" x H 11\".</li>\n</ul>"
        },
        {
            "attribute_code": "image",
            "value": "/m/b/mb01-blue-0.jpg"
        },
        {
            "attribute_code": "small_image",
            "value": "/m/b/mb01-blue-0.jpg"
        },
        {
            "attribute_code": "thumbnail",
            "value": "/m/b/mb01-blue-0.jpg"
        },
        {
            "attribute_code": "category_ids",
            "value": [
                "3",
                "4"
            ]
        },
        {
            "attribute_code": "options_container",
            "value": "container2"
        },
        {
            "attribute_code": "required_options",
            "value": "0"
        },
        {
            "attribute_code": "has_options",
            "value": "0"
        },
        {
            "attribute_code": "url_key",
            "value": "joust-duffle-bag"
        },
        {
            "attribute_code": "tax_class_id",
            "value": "2"
        },
        {
            "attribute_code": "activity",
            "value": "11,22,21,19"
        },
        {
            "attribute_code": "style_bags",
            "value": "25,26,29"
        },
        {
            "attribute_code": "material",
            "value": "37,38"
        },
        {
            "attribute_code": "strap_bags",
            "value": "61,62,63,64,65,66"
        },
        {
            "attribute_code": "features_bags",
            "value": "74,76,79"
        }
    ],
    "extension_attributes": {
        "stock_item": {
            "backorders": 0,
            "enable_qty_increments": false,
            "is_decimal_divided": false,
            "is_in_stock": true,
            "is_qty_decimal": false,
            "item_id": 1,
            "low_stock_date": null,
            "manage_stock": true,
            "max_sale_qty": 10000,
            "min_qty": 0,
            "min_sale_qty": 1,
            "notify_stock_qty": 1,
            "product_id": 1,
            "qty": 100,
            "qty_increments": 0,
            "show_default_notification_message": false,
            "stock_id": 1,
            "stock_status_changed_auto": 0,
            "use_config_backorders": true,
            "use_config_enable_qty_inc": true,
            "use_config_manage_stock": true,
            "use_config_max_sale_qty": true,
            "use_config_min_qty": true,
            "use_config_min_sale_qty": 1,
            "use_config_notify_stock_qty": true,
            "use_config_qty_increments": true
        }
    },
    "id": 1,
    "media_gallery_entries": [
        {
            "disabled": false,
            "file": "/m/b/mb01-blue-0.jpg",
            "id": 1,
            "label": "Image",
            "media_type": "image",
            "position": 1,
            "types": [
                "image",
                "small_image",
                "thumbnail"
            ]
        }
    ],
    "name": "Joust Duffle Bag",
    "options": [],
    "price": 34,
    "product_links": [
        {
            "extension_attributes": [],
            "link_type": "upsell",
            "linked_product_sku": "24-MB03",
            "linked_product_type": "simple",
            "position": null,
            "sku": "24-MB01"
        },
        {
            "extension_attributes": [],
            "link_type": "upsell",
            "linked_product_sku": "24-MB05",
            "linked_product_type": "simple",
            "position": null,
            "sku": "24-MB01"
        },
        {
            "extension_attributes": [],
            "link_type": "upsell",
            "linked_product_sku": "24-MB06",
            "linked_product_type": "simple",
            "position": null,
            "sku": "24-MB01"
        },
        {
            "extension_attributes": [],
            "link_type": "upsell",
            "linked_product_sku": "24-MB02",
            "linked_product_type": "simple",
            "position": null,
            "sku": "24-MB01"
        },
        {
            "extension_attributes": [],
            "link_type": "upsell",
            "linked_product_sku": "24-UB02",
            "linked_product_type": "simple",
            "position": null,
            "sku": "24-MB01"
        },
        {
            "extension_attributes": [],
            "link_type": "upsell",
            "linked_product_sku": "24-WB03",
            "linked_product_type": "simple",
            "position": null,
            "sku": "24-MB01"
        },
        {
            "extension_attributes": [],
            "link_type": "upsell",
            "linked_product_sku": "24-WB07",
            "linked_product_type": "simple",
            "position": null,
            "sku": "24-MB01"
        },
        {
            "extension_attributes": [],
            "link_type": "upsell",
            "linked_product_sku": "24-WB04",
            "linked_product_type": "simple",
            "position": null,
            "sku": "24-MB01"
        }
    ],
    "sku": "24-MB01",
    "status": 1,
    "tier_prices": [],
    "type_id": "simple",
    "updated_at": "2016-02-08 20:35:35",
    "visibility": 4
}

And the second line it outputs is a smaller set of fields specified in a filter:

$ php filter-test.php |tail -1 |python -mjson.tool

{
    "extension_attributes": {
        "stock_item": {
            "is_in_stock": true
        }
    },
    "id": 1,
    "name": "Joust Duffle Bag",
    "sku": "24-MB01"
}

Bottom line, filtering fields in the Magento 2 API's responses is as simple as appending a query string on the end of the request URL, just like this:

http://mage2.dev/rest/V1/products/24-mb01?fields=id,extension_attributes[stock_item[is_in_stock]]

Previous Post Next Post