Skip to content

Catalog Product — Detail (GraphQL)

GraphQL item query that returns a single catalog product by its IRI. Every nested block is a field-selectable Relay connection — sub-select exactly the fields you need with edges { node { … } }.

Operation

OperationType
adminCatalogProductQuery (item)

For product types, the two-step create flow, and the per-product sub-resources, see the Products overview.

Arguments

ArgumentTypeRequiredDescription
idID!YesAPI Platform IRI of the product (e.g. "/api/admin/catalog/products/42")

Response Shape

Top-level scalars carry the same values as the listing, so the traps are the same:

  • Booleans arrive as "1" or the empty string "", never true/false. status, visibleIndividually, featured, new, and inStock are all typed String, so status === "0" never matches and featured === false never matches. Compare against "1", or coerce.
  • Numbers arrive as strings. price, quantity, imagesCount, weight, and categoryId are String; only _id and attributeFamilyId are Int.
  • taxCategoryId and manageStock are frequently null even on the detail query — they resolve only when the product actually carries that attribute value.
  • Timestamps are ISO 8601 with offset, 2026-01-12T08:15:00+05:30.

Every nested block is a connection you sub-select with { edges { node { … } } }:

ConnectionNode fieldsPresent for
images_id, type, path, url, positionall
videos_id, type, path, url, positionall
categories_id, name, slugall
inventories_id, sourceId, sourceCode, qtyall
customerGroupPrices_id, customerGroupId, qty, valueType, value, uniqueIdall
translations_id, locale, name, description, shortDescription, urlKey, metaTitle, metaDescription, metaKeywordsall
channels_id, code, nameall (the product's assigned channels)
attributeValues_id, attributeId, code, adminName, type, isRequired, groupCode, valueall (the stored EAV values)
superAttributes_id, code, type, adminName, options { edges { node { _id adminName swatchValue sortOrder } } }configurable
variants_id, sku, name, price, formattedPrice, quantity, inStock, attributeValues { edges { node { code adminName value } } }configurable
bundleOptions_id, label, type, position, isRequired, products { edges { node { _id productId sku name qty isDefault sortOrder } } }bundle
linkedProducts_id, associatedProductId, sku, name, qty, sortOrdergrouped
downloadableLinks_id, sortOrder, downloads, price, formattedPrice, type, file, fileUrl, sampleFile, sampleFileUrl, sampleType, translations { edges { node { _id locale title } } }downloadable
downloadableSamples_id, sortOrder, type, file, fileUrl, translations { edges { node { _id locale title } } }downloadable
customizableOptions_id, type, isRequired, sortOrder, maxCharacters, supportedFileExtensions, translations { edges { node { _id locale label } } }, prices { edges { node { _id label price sortOrder } } }all
relatedProducts / upSells / crossSells_id, sku, type, nameall

Type-specific connections (superAttributes / variants, bundleOptions, linkedProducts, downloadableLinks / downloadableSamples) return empty edges on non-matching types — switch on type to know which to read. Selecting all of them regardless is safe.

Two connection details worth knowing:

  • An image node's type is the string "images", plural, not "image" — it names the storage folder, not the media kind. Video nodes carry "videos" for the same reason.
  • channels lists only the product's assigned channels, not every channel in the store. The REST detail's channels block instead lists every channel with an assigned flag, so the two are not interchangeable.

Configurable Option Lists

A configurable exposes its option data twice, and the two answer different questions:

  • superAttributes { edges { node { options { edges { node } } } } } — every option the attribute defines, whether or not a variant uses it. This is the set to render in a picker.
  • variants { edges { node { attributeValues { edges { node { code adminName value } } } } } } — the option value actually chosen for each existing variant.

Fields That Are REST-Only

Three fields exist in the schema but always resolve null over GraphQL — attributes, bookingProduct, and warnings. They are returned only by GET /api/admin/catalog/products/{id}.

  • attributes is the admin edit-screen field set, including family fields the product has not filled in. The GraphQL equivalent is the attributeValues connection, which carries one node per stored value — an unset field simply has no node, so you cannot tell "empty" from "not in this family" without the REST block.
  • bookingProduct carries the booking sub-type, slots, and tickets. There is no GraphQL equivalent; read booking configuration over REST.
  • warnings is populated only on the update response, never on a read.

Errors

An unknown or deleted id returns HTTP 200 with "Product not found." in the GraphQL errors[] array and null in data.adminCatalogProduct.

Working With This Query

  • The id argument is the IRI, not the numeric id. Build it as /api/admin/catalog/products/{_id} from a listing row's _id, or pass a listing edge's id straight through.
  • REST is the flat counterpart. GET /api/admin/catalog/products/{id} returns the same data with every nested block as an inline array, and adds attributes, bookingProduct, and the full superAttributes[].options.

Released under the MIT License.