Select Page

Akeneo – A JavaScript Node Programmer’s Guide to the PIM’s Web API

December 1, 2022

Overview

Akeneo’s Product Information Management (PIM) application: Akeneo PIM, is one of the best open-source and/or enterprise PIM’s on the market. One of its greatest strengths is its REST-based Web API. Akeneo has made a commitment to keep it forward compatible. Therefore, using it ensures any commitment to data processing and integration with Akeneo PIM using its Web API, is a stable, long term, investment.

The Web API allows creates, reads, and updates against almost all the PIM’s entities, with delete operations on products and product models.

Here’s Akeneo’s API documentation: https://api.akeneo.com/api-reference-index.html

 

An ERD of Basic Relationships

 

Akeneo - A JavaScript Node Programmer's Guide to the PIM's Web API JSON

 

The Entity Relationship Diagram (ERD) above is a simplified example of the PIM’s entities. I simplified it here in order to help you understand the basic relationships between the PIM entities that are commonly modified using the Web API.

Reading the ERD, a given product belongs to one and only one family, which, in turn, has one or more attributes assigned to it. Attributes exist to store product data. Attributes themselves belong to a group. Families can have variants, and if a family has variants, the given product will have a parent product model. If you didn’t follow that, don’t worry, I’ll explain it in more detail as you read on.

 

JavaScript Object Notation (JSON)

The Web API’s responses are always in the form of JSON. JSON is a serialized version of a JavaScript object; a string.

In Create, Read, Update, and Delete (CRUD) parlance:

  • To create a new entity instance, let’s say a product, you send a properly formatted JSON string via the API using an HTTP POST request with the appropriate URL and a mime type of application/json.
  • To read an existing entity instance, let’s say a product, you send an HTTP GET request with an appropriate URL and a mime type of application/json.
  • To modify an existing entity instance, let’s say a product, you send a properly formatted JSON string via the API using an HTTP PATCH request with the appropriate URL and a mime type of application/json.
  • To delete an existing entity instance, let’s say a product, you send an HTTP DELETE request with an appropriate URL and a mime type of application/json.

See the consistency of the pattern there?

 

The Vendor Akeneo Collection (VAC) Mime Type aka Newline Delimited JSON

What if you want to do a batch create or update?

To create, or modify existing entity instances, let’s say 1,000 products, you send a properly formatted newline delimited JSON string via the API using an HTTP PATCH request with the appropriate URL and a mime type of vnd.akeneo.collection+json.

 

Internationalization

Akeneo PIM is a fully internationalized (i18n) application. Last count, there are 210 defined locales in the PIM. Every entity instance created in the PIM has both an externally unique identifier, or code, and one or more labels. Label values can be supplied for every locale you choose to work with in the PIM.

 

Channels (Scopes)

Akeneo PIM also allows you to specify one or more channels, aka as scopes in the JSON objects. Channels are a way to identify where the data you enrich into the PIM will be consumed.

 

Attributes

Since products depend on families, and families depend on attributes, the first thing you need to do is define one or more attributes. Think of attributes as product properties (using JavaScript nomenclature). Any field you might fill out on the Attribute detail screen in the PIM, is available as a JavaScript property in the JSON response from the Web API.

 

Akeneo - A JavaScript Node Programmer's Guide to the PIM's Web API JSON

Here’s an example:

{
    "code": "camera_model_name",
    "type": "pim_catalog_text",
    "group": "technical",
    "unique": false,
    "useable_as_grid_filter": true,
    "allowed_extensions": [],
    "metric_family": null,
    "default_metric_unit": null,
    "reference_data_name": null,
    "available_locales": [],
    "max_characters": null,
    "validation_rule": null,
    "validation_regexp": null,
    "wysiwyg_enabled": null,
    "number_min": null,
    "number_max": null,
    "decimals_allowed": null,
    "negative_allowed": null,
    "date_min": null,
    "date_max": null,
    "max_file_size": null,
    "minimum_input_length": null,
    "sort_order": 22,
    "localizable": false,
    "scopable": false,
    "labels": {
      "de_DE": "Modelname",
      "en_US": "Model name",
      "fr_FR": "Modèle"
    },
    "guidelines": {},
    auto_option_sorting": null,
    "is_read_only": false,
    "default_value": null,
    "group_labels": {
      "en_US": "Technical",
      "fr_FR": "Technique",
      "de_DE": "Technische"
    }
}

The code value: camera_model_name, is the unique external identifier for this attribute.

The example JSON above has been formatted to look pretty. Here’s what it looks like when you send it as a JSON string via the Web API:

{"code":"camera_model_name","type":"pim_catalog_text","group":"technical","unique":false,"useable_as_grid_filter":true,"allowed_extensions":[],"metric_family":null,"default_metric_unit":null,"reference_data_name":null,"available_locales":[],"max_characters":null,"validation_rule":null,"validation_regexp":null,"wysiwyg_enabled":null,"number_min":null,"number_max":null,"decimals_allowed":null,"negative_allowed":null,"date_min":null,"date_max":null,"max_file_size":null,"minimum_input_length":null,"sort_order":22,"localizable":false,"scopable":false,"labels":{"de_DE":"Modelname","en_US":"Model name","fr_FR":"Modèle"},"guidelines":{},"auto_option_sorting":null,"is_read_only":false,"default_value":null,"group_labels":{"en_US":"Technical","fr_FR":"Technique","de_DE":"Technische"}}

Make sense?

Attribute Options

If the attribute type is pim_catalog_simpleselect or pim_catalog_multiselect, the attribute will have one or more Attribute Options.

Here’s an example:

{
  "code": "canon_brand",
  "attribute": "camera_brand",
  "sort_order": 1,
  "labels": {
      "de_DE": "Canon",
      "en_US": "Canon",
      "fr_FR": "Canon"
  }
}

The example JSON above is for the attribute camera_brand.

Attribute Groups

Attributes are grouped using an attribute group. Assigning an attribute to a group makes them display as a group on the Product detail screen in the PIM.

Here’s an example of attribute group JSON:

{
    "code": "technical",
    "sort_order": 2,
    "attributes": [
      "weight",
      "maximum_scan_size",
      "color_scanning",
      "power_requirements",
      "maximum_print_size",
      "sensor_type",
      "total_megapixels",
      "optical_zoom",
      "image_stabilizer",
      "camera_type",
      "thd",
      "snr",
      "headphone_connectivity",
      "maximum_video_resolution",
      "maximum_frame_rate",
      "multifunctional_functions",
      "display_srgb",
      "display_color",
      "display_diagonal",
      "viewing_area",
      "camera_brand",
      "camera_model_name",
      "short_description",
      "max_image_resolution",
      "image_resolutions",
      "supported_aspect_ratios",
      "supported_image_format",
      "lens_mount_interface",
      "focus",
      "focus_adjustement",
      "auto_focus_modes",
      "auto_focus_points",
      "auto_focus_lock",
      "auto_focus_assist_beam",
      "iso_sensitivity",
      "light_exposure_modes",
      "light_exposure_corrections",
      "light_metering",
      "auto_exposure",
      "iso_sensitivity_max",
      "iso_sensitivity_min"
    ],
    "labels": {
      "en_US": "Technical",
      "fr_FR": "Technique",
      "de_DE": "Technische"
    }
}

 

Now that you have some attributes defined, you can create a family.

Families

A List of attributes

Families associate a set of common attributes to a family of products. If the product is a T-shirt, the attributes may be the color, collar type, material, and size. If it’s a notebook computer, it may be the number of cores, disk size, memory size, and screen size. These two products don’t have much in common beyond a probable consumer: a JavaScript Node programmer. So, it wouldn’t make sense to have both T-shirts and notebook computers in the same family. They have no attribution in common. In this case, there should be a family for T-shirts, and another for notebook computers.

List of required attributes

Families also specify which of their attributes are required. In Akeneo PIM there’s an enrichment concept of completion. Only when all a product’s required attributes have values, as determined by its associated family, is the product complete. That is, ready for publication and syndication. In our first example, the T-shirt: color, collar type, and size may be required, while material may be optional. For our notebook computers, perhaps all four attributes should be required.

Family JSON

Now let’s look at example:


{
    "code": "digital_cameras",
    "attributes": [
      "auto_exposure",
      "auto_focus_assist_beam",
      "auto_focus_lock",
      "auto_focus_modes",
      "auto_focus_points",
      "camera_brand",
      "camera_model_name",
      "camera_type",
      "description",
      "focus",
      "focus_adjustement",
      "image_resolutions",
      "iso_sensitivity",
      "iso_sensitivity_max",
      "iso_sensitivity_min",
      "lens_mount_interface",
      "light_exposure_corrections",
      "light_exposure_modes",
      "light_metering",
      "max_image_resolution",
      "name",
      "optical_zoom",
      "picture",
      "power_requirements",
      "price",
      "release_date",
      "sensor_type",
      "short_description",
      "sku",
      "supported_aspect_ratios",
      "supported_image_format",
      "total_megapixels",
      "weight"
    ],
    "attribute_as_label": "name",
    "attribute_as_image": "picture",
    "attribute_requirements": {
      "ecommerce": [
        "auto_exposure",
        "auto_focus_assist_beam",
        "auto_focus_lock",
        "auto_focus_modes",
        "auto_focus_points",
        "camera_brand",
        "camera_model_name",
        "camera_type",
        "description",
        "focus",
        "focus_adjustement",
        "image_resolutions",
        "iso_sensitivity",
        "iso_sensitivity_max",
        "iso_sensitivity_min",
        "lens_mount_interface",
                "light_exposure_corrections",
        "light_exposure_modes",
        "light_metering",
        "max_image_resolution",
        "name",
        "price",
        "sensor_type",
        "short_description",
        "sku",
        "supported_aspect_ratios",
        "supported_image_format",
        "total_megapixels"
    ],
    "mobile": [
      "camera_type",
        "description",
        "name",
        "price",
        "sensor_type",
        "sku",
        "total_megapixels"
    ],
      "print": [
        "camera_type",
        "description",
        "name",
        "sensor_type",
        "sku",
        "total_megapixels"
      ]
    },
    "labels": {
      "en_US": "Digital cameras",
      "fr_FR": "Caméras digitales",
      "de_DE": "Digitale Kameras"
    }
}

 

It starts with its unique identifier, property code: digital_cameras. The next property is: attributes, a list of the attributes assigned to this family. Of these, only the attributes listed in the property: attribute_requirements, are required for completeness. For the requirements, there’s a list for each scope (channel).

Categories

A product may be assigned to one or more categories. Let’s start out conversation about categories by dispelling a myth: no, categories and families are the same thing. At first blush, it may appear that categories and families are the same thing, but they are not. Above, in families, I introduced two mythical products: T-shirts, and notebook computers. While T-shirts may get their own category, appropriately named T-shirts, notebook computers may be lumped into a category named: computers, along with desktop computers, notepads, and Raspberry Pis. Families are for assigning attributes to be enriched, while categories are for grouping one or more families into a taxonomy. The American Heritage Dictionary defines taxonomy as: the classification and naming of organisms in an ordered system that is intended to indicate natural relationships, especially evolutionary relationships. So don’t fall into the families and categories are the same thing trap.

Let’s look at example in the PIM:

 

And here’s the corresponding example JSON:


{
    "code": "master",
    "parent": null,
    "updated": "2022-11-21T21:08:18+00:00",
    "labels": {
      "en_US": "Master catalog",
      "de_DE": "Hauptkatalog",
      "fr_FR": "Catalogue principal"
    }
}
{
    "code": "cameras",
    "parent": "master",
    "updated": "2022-11-21T21:08:18+00:00",
    "labels": {
      "en_US": "Cameras",
      "de_DE": "Cameras",
      "fr_FR": "Caméras"
    }
}
{
    "code": "digital_cameras",
    "parent": "cameras",
    "updated": "2022-11-21T21:08:18+00:00",
    "labels": {
      "en_US": "Digital cameras",
      "de_DE": "Digitale Kameras",
      "fr_FR": "Caméras digitales"
    }
}
{
    "code": "camcorders",
    "parent": "cameras",
    "updated": "2022-11-21T21:08:18+00:00",
    "labels": {
      "en_US": "Camcorders",
      "de_DE": "Digitale Videokameras",
      "fr_FR": "Caméscopes numériques"
    }
}
{
    "code": "webcams",
    "parent": "cameras",
    "updated": "2022-11-21T21:08:18+00:00",
    "labels": {
      "en_US": "Webcams",
      "de_DE": "Webcams",
      "fr_FR": "Webcams"
    }
}

At the top of the tree is master. You can tell because it doesn’t have a parent property value. The second level is cameras. You can tell because its parent value is: master. The third level has three categories: digital_cameras, camcorders, and webcams.
Now that we have all the prerequisite catalog entities populated, we can work on products.

Products

It’s obvious, but that may be elusive, so let me state the obvious here: a product is where product data is stored. A product has a unique external identifier, is assigned a family, may be assigned to one or more categories, and then has a values object that contains all its attribute values. Akeneo PIM is a data-driven application, so you can’t really work with products unless you also have access to the Attribute and Family entitles.

Need Attributes for Labels and Simple or Multi Select Values

 

  • When you publish product data, you are going to need to get the attribute labels for a given attribute from the Attributes entity.
  • When you create or update product data, the actual data property, which I’ll introduce shortly, is always the actual string value, except when an attribute is a simple or multi select.
  • When an attribute is type: pim_catalog_simpleselect, the data property’s value is a code value for one of the attribute’s option values.
  • When an attribute is type: pim_catalog_multiselect, the data property’s value is an array of code values for the attribute’s option values.

 

The Product Values Object

A product’s property: values, is a JavaScript object where a property name in the object is one of the Attribute’s code values. And the value assigned to the property, is an array of one or more value objects, that has three properties: locale, scope, and data.

Let’s look at a product’s truncated example:


{
  "identifier": "10620502",
  "enabled": true,
  "family": "digital_cameras",
  "categories": [
    "cameras_sales",
    "canon",
    "digital_cameras"
  ],
  "groups": [],
  "parent": null,
  "values": {
    "camera_type": [
      {
        "locale": null,
        "scope": null,
        "data": "compact"
      }
    ],
    ...
    "name": [
      {
        "locale": null,
        "scope": null,
        "data": "Canon EOS 600D + EF 18-55mm IS + EF 55-250mm IS"
      }
    ],
    ...
    "description": [
      {
        "locale": "de_DE",
        "scope": "print",
        "data": "Modernste Technologie mit LED-Backlights, eine starke Performance sowie ein zeitlos klassisches, schwarzes Design kennzeichnen dieses Modell. Aufgrund hoher Kontrast- und Helligkeitswerte sowie einer schnellen Reaktionszeit präsentiert es selbst rasante Bewegtbildsequenzen flüssig und gestochen scharf. Das elegante Display mit haarfeinen Texturen an der Rückseite und an dem runden Sockel ist an seiner „schlanksten“ Stelle gerade einmal 1,6 cm tief. Dank seiner zahlreichen Features empfiehlt es sich sowohl für Büroarbeitsplätze als auch für Zuhause."
      },
      {
        "locale": "en_US",
        "scope": "print",
        "data": "This model is characterised by the latest display technology with LED backlights, a top performance and a timelessly classic, black design. Due to high contrast and brightness values as well as a quick response time, the monitor renders even the fastest movie sequences fluently and razor-sharp. At its slimmest region, this elegant display, which sports hairline textures on the rear side as well as the base, is just 1.6 cm deep. Thanks to numerous features it is recommended for both office and home."
      },
      {
        "locale": "fr_FR",
        "scope": "print",
        "data": "Principales caractéristiques de ce modèle : une technologie d’affichage de pointe à rétro-éclairage LED*, d’excellentes performances et un design noir, à la fois classique et indémodable. Avec ses valeurs élevées de contraste et de luminosité et un temps de réponse rapide, le moniteur confère une netteté une fluidité exceptionnelle aux images animées les plus rapides. Cet écran, à texture brossée sur la face arrière et la base, ne mesure qu’1,6 cm à l’endroit le plus mince. Sa grande polyvalence en fait un outil aussi à l’aise au bureau qu’à la maison."
      }
    ],
  }
  ...
}

As I stated earlier, it has an identifier. It’s assigned family: digital_cameras. So only attributes in that family should be added to the values object. This product is assigned to three categories.

Since I truncated this example, we only see three properties in the values object:
camera_type, name, and description. Let’s take a close look at the camera_type property values:

"camera_type": [
  {
    "locale": null,
    "scope": null,
    "data": "compact"
  }
],

The property’s value is an array containing one element, an object:

{
  "locale": null,
  "scope": null,
  "data": "compact"
}

The value (singular) object has three properties: locale, scope, and data.

  • data is where the actual data value is specified.
  • locale specifies the locale for the data. Since it’s null, it means all locales.
  • scope specifies the channel for the data. Since it’s null, it means all scopes.

In contrast, let’s look at the description property values:


"description": [
  {
    "locale": "de_DE",
    "scope": "print",
    "data": "Modernste Technologie mit LED-Backlights, eine starke Performance sowie ein zeitlos klassisches, schwarzes Design kennzeichnen dieses Modell. Aufgrund hoher Kontrast- und Helligkeitswerte sowie einer schnellen Reaktionszeit präsentiert es selbst rasante Bewegtbildsequenzen flüssig und gestochen scharf. Das elegante Display mit haarfeinen Texturen an der Rückseite und an dem runden Sockel ist an seiner „schlanksten“ Stelle gerade einmal 1,6 cm tief. Dank seiner zahlreichen Features empfiehlt es sich sowohl für Büroarbeitsplätze als auch für Zuhause."
  },
  {
    "locale": "en_US",
    "scope": "print",
    "data": "This model is characterised by the latest display technology with LED backlights, a top performance and a timelessly classic, black design. Due to high contrast and brightness values as well as a quick response time, the monitor renders even the fastest movie sequences fluently and razor-sharp. At its slimmest region, this elegant display, which sports hairline textures on the rear side as well as the base, is just 1.6 cm deep. Thanks to numerous features it is recommended for both office and home."
  },
  {
    "locale": "fr_FR",
    "scope": "print",
    "data": "Principales caractéristiques de ce modèle : une technologie d’affichage de pointe à rétro-éclairage LED*, d’excellentes performances et un design noir, à la fois classique et indémodable. Avec ses valeurs élevées de contraste et de luminosité et un temps de réponse rapide, le moniteur confère une netteté une fluidité exceptionnelle aux images animées les plus rapides. Cet écran, à texture brossée sur la face arrière et la base, ne mesure qu’1,6 cm à l’endroit le plus mince. Sa grande polyvalence en fait un outil aussi à l’aise au bureau qu’à la maison."
  }
],

This time, the property’s value is an array containing three elements, three value objects:


{
  "locale": "de_DE",
  "scope": "print",
  "data": "Modernste Technologie mit LED-Backlights, eine starke Performance sowie ein zeitlos klassisches, schwarzes Design kennzeichnen dieses Modell. Aufgrund hoher Kontrast- und Helligkeitswerte sowie einer schnellen Reaktionszeit präsentiert es selbst rasante Bewegtbildsequenzen flüssig und gestochen scharf. Das elegante Display mit haarfeinen Texturen an der Rückseite und an dem runden Sockel ist an seiner „schlanksten“ Stelle gerade einmal 1,6 cm tief. Dank seiner zahlreichen Features empfiehlt es sich sowohl für Büroarbeitsplätze als auch für Zuhause."
},
{
  "locale": "en_US",
  "scope": "print",
  "data": "This model is characterised by the latest display technology with LED backlights, a top performance and a timelessly classic, black design. Due to high contrast and brightness values as well as a quick response time, the monitor renders even the fastest movie sequences fluently and razor-sharp. At its slimmest region, this elegant display, which sports hairline textures on the rear side as well as the base, is just 1.6 cm deep. Thanks to numerous features it is recommended for both office and home."
},
{
  "locale": "fr_FR",
  "scope": "print",
  "data": "Principales caractéristiques de ce modèle : une technologie d’affichage de pointe à rétro-éclairage LED*, d’excellentes performances et un design noir, à la fois classique et indémodable. Avec ses valeurs élevées de contraste et de luminosité et un temps de réponse rapide, le moniteur confère une netteté une fluidité exceptionnelle aux images animées les plus rapides. Cet écran, à texture brossée sur la face arrière et la base, ne mesure qu’1,6 cm à l’endroit le plus mince. Sa grande polyvalence en fait un outil aussi à l’aise au bureau qu’à la maison."
}

The first value object is specifically for the locale: German in Germany, and specifically for the scope: print, that is, when the product’s information is printed for German consumers.

{
  "locale": "de_DE",
  "scope": "print",
  "data": "Modernste Technologie mit LED-Backlights, eine starke Performance sowie ein zeitlos klassisches, schwarzes Design kennzeichnen dieses Modell. Aufgrund hoher Kontrast- und Helligkeitswerte sowie einer schnellen Reaktionszeit präsentiert es selbst rasante Bewegtbildsequenzen flüssig und gestochen scharf. Das elegante Display mit haarfeinen Texturen an der Rückseite und an dem runden Sockel ist an seiner „schlanksten“ Stelle gerade einmal 1,6 cm tief. Dank seiner zahlreichen Features empfiehlt es sich sowohl für Büroarbeitsplätze als auch für Zuhause."
}

The value (singular) object has three properties: locale, scope, and data.

  • data is where the actual data value is specified.
  • locale specifies the locale for the data. Since it’s de_DE, it means for locale de_DE.
  • scope specifies the channel for the data. Since it’s print, it means for channel print.

The second value object is specifically for the locale: English in the United States, and specifically for the scope print, that is, when the product’s information is printed for American consumers.

{
  "locale": "en_US",
  "scope": "print",
  "data": "This model is characterised by the latest display technology with LED backlights, a top performance and a timelessly classic, black design. Due to high contrast and brightness values as well as a quick response time, the monitor renders even the fastest movie sequences fluently and razor-sharp. At its slimmest region, this elegant display, which sports hairline textures on the rear side as well as the base, is just 1.6 cm deep. Thanks to numerous features it is recommended for both office and home."
},

The value (singular) object has three properties: locale, scope, and data.

  • data is where the actual data value is specified.
  • locale specifies the locale for the data. Since it’s en_US, it means for locale en_US.
  • scope specifies the channel for the data. Since it’s print, it means for channel print.

You should see the pattern now. So how do you know what locales and scopes are supported for a given attribute? You look at the data in the Attributes entity for the given attribute. The datatype of the data property is

  • Boolean: a JavaScript boolean
  • Date: a JavaScript string with the date formatted to ISO-8601
  • Integer: a JavaScript number
  • Decimal: a JavaScript string
  • String: a JavaScript string
  • Multi Select: a JavaScript array, for example: [“copy”,”fax”,”print”,”scan”]
  • Metric: a JavaScript object, for example: {“amount”:19,”unit”:”INCH”}
  • Price Collection: a JavaScript array, for example: [{“amount”:”2.00″,”currency”:”EUR”},{“amount”:”3.00″,”currency”:”USD”}]

Of course, there’s more to the product object, but I’ll let you discover that through experimentation.

values is a Sparse Collection

The values object in a product is a sparse collection. Only when an attribute for a given product has one or more values does the attribute show up in values. In other words:

  • A JavaScript object: {} is a map. It’s a map because it contains key-value pairs.
  • A map is a collection.
  • A product’s values object only contains attributes for which values exist
  • Therefore, the values object is a sparse collection.

Sparse Updates

Why is this so important? Because you can, and should do, sparse updates. A sparse update is where you create a product with its identifier, family, and values containing only those values you wish to create, update, or delete.
For example, if I want to modify camera_type to slr:

{
  "identifier": "10620502",
  "family": "digital_cameras",
  "values": {
    "camera_type": [
      {
        "locale": null,
        "scope": null,
        "data": "slr"
      }
    ],
  }
}

The example above shows you that you can do a sparse update, but why should you?

Back in the mainframe days, when we only did file processing, we made sure we only scheduled one job, that would modify a particular file, at a time. We did this to maintain data integrity.

Then we started using database management systems that employed locking or versioning, or both, so we could detect when more than one program was trying to modify the same data. We did this to maintain data integrity.

Later we started using relational database management systems that employed the concept of transactions with commit or rollback. A commit ensured that all the data in a transaction, even when it was to multiple data sources, remained consistent. We did this to maintain data integrity.

More recently, we started using document databases. No commit or rollback anymore. No guarantee of data integrity. It’s the wild wild west.

So, with Akeneo PIM, when you send a sparse update, you are maximizing data integrity.

If you retrieve a product document, update an attribute value, then patch it back through the API, with all its attributes, it is more likely to wipe out someone else’s update that took place between the time you retrieved the product document and when you patched the updated.

On the other hand, if you retrieve a product document, update an attribute value using sparse update, then patch it back through the API, since it has only one attribute, it is less likely to wipe out someone else’s update that took place between the time you retrieved the product document and when you patched the updated.

Delete an Attribute Value

To delete an attribute value, set data to null. For example, to remove the camera_type:

{
  "identifier": "10620502",
  "family": "digital_cameras",
  "values": {
    "camera_type": [
      {
        "locale": null,
        "scope": null,
        "data": null
      }
    ],
  }
}

What if a given product varies by one of the attribute values? And, we want to make this obvious in the presentation layer of the application? The Akeneo way is to use product models.

Product Models (Variants)

A variant is something that differs from others of the same kind, in our case, products. Remember those T-shirts I was talking about earlier, they are the classic example of a product that varies by color and size. In Akeneo speak, they vary by two axes (attributes): color, and size. Is this situation you would get the family variants and find the one where the family code in the variant matches the code in its parent family.

Need Families, and Family Variants Entities to Determine Where Attributes Belong

Working with variant families and product models doubles or triples the complexity of your code. The most important thing to remember is where the attributes belong.

  • A family lists all the attributes possible for a product.
  • A family variant lists which attribute(s), from the corresponding family, are an axis, and the attributes assigned to that axis level.
  • In a nutshell, you need to update the attribute values in the product, or product model they belong to, not just the product.

One of the nice features, that is also deceiving, is the fact that when you get products from the API you get all the attributes, whether they belong to an underlying product model or the product. It may make you think you can just update a product model’s attribute in the product, but that doesn’t work. You need to examine the family and family variants to determine where to assign the attribute value.

Here’s an example of product model’s truncated JSON:

{
  "code": "samtv",
  "family": "led_tvs",
  "family_variant": "tv",
  "parent": null,
  "categories": [
    "led_tvs"
  ],
  "values": {
    "name": [
      {
        "locale": null,
        "scope": null,
        "data": "TV SAMSUNG"
      }
    ],
    "description": [
      {
        "locale": "en_US",
        "scope": "ecommerce",
        "data": "TV SAMSUNG"
      }
    ]
  },
  ...
}

Notice, it has the same values object structure.

Node Akeneo API

I’ve worked on Akeneo PIM implementation for years now. To make my life easier, I wrote a module named akeneo that was part of an integration framework named: node-akeneo. Recently, I made the akeneo module into its own npm, because not everyone may want to use my framework, but they may benefit from the node-akeneo-api npm: https://www.npmjs.com/package/node-akeneo-api. The README.md file explains how to use the npm fairly well.

Node Akeneo Framework

If you’re migrating data from a legacy data source into Akeneo PIM, you may find my Node Akeneo framework helpful: https://github.com/donaldbales/node-akeneo.

Just fork the framework and code away. I hope this short guide to using the Akeneo PIM Web API, written from a JavaScript Node programmer’s frame of reference is helpful.

You May Also Like…