Product detail queries
Fetch detailed data for a single product including attributes, cross-sell relations and bundles. For listing queries (pagination, filtering, sorting), see Querying products.
Fetching a single product
Use the product query to retrieve a single product by productId, slug or sku:
query GetProduct($slug: String, $language: String) {
product(slug: $slug) {
productId
sku
names(language: $language) {
value
}
descriptions(language: $language) {
value
}
shortDescriptions(language: $language) {
value
}
slugs(language: $language) {
value
}
manufacturer
manufacturerCode
status
price {
gross
net
}
inventory {
totalQuantity
}
cluster {
clusterId
names(language: $language) {
value
}
}
categoryPath {
categoryId
name(language: $language) {
value
}
}
}
}
Variables:
{
"slug": "led-paneel-backlit-60x60-3000k",
"language": "NL"
}
Response:
{
"data": {
"product": {
"productId": 2151,
"sku": "LED-PNL-6060-3K",
"names": [{ "value": "LED Paneel Backlit 60x60 3000K" }],
"descriptions": [{ "value": "<p>Compact backlit LED paneel van 60x60 cm...</p>" }],
"shortDescriptions": [{ "value": "<p>Backlit LED paneel 60x60 cm, warm wit 3000K, voor systeemplafonds</p>" }],
"slugs": [{ "value": "led-paneel-backlit-60x60-3000k" }],
"manufacturer": "ProLight Solutions",
"manufacturerCode": "PL-6060-BL-30",
"status": "A",
"price": { "gross": 39.95, "net": 48.34 },
"inventory": { "totalQuantity": 250 },
"cluster": {
"clusterId": 214,
"names": [{ "value": "LED Paneel Backlit" }]
},
"categoryPath": []
}
}
}
You can also look up by productId or sku:
product(productId: 2151) { ... }
product(sku: "LED-PNL-6060-3K") { ... }
If the product belongs to a cluster, the cluster field returns the parent cluster. Use this to link back to the cluster page or show variant selectors. The categoryPath field returns the full category hierarchy for building breadcrumbs.
Product attributes
Attributes are key-value pairs attached to products. Use the attributes field to fetch them for display as product specifications.
query GetProductAttributes($productId: Int!, $language: String) {
product(productId: $productId) {
productId
names(language: $language) {
value
}
attributes(
input: {
offset: 20
attributeDescription: { isPublic: true, isSearchable: true }
}
) {
items {
attributeDescription {
name
descriptions {
language
value
}
type
group
}
value {
... on AttributeColorValue {
colorValue
}
... on AttributeTextValue {
textValues {
language
values
}
}
... on AttributeEnumValue {
enumValues
}
... on AttributeIntValue {
intValue
}
... on AttributeDecimalValue {
decimalValue
}
... on AttributeDateTimeValue {
dateTimeValue
}
}
}
itemsFound
}
}
}
Variables:
{
"productId": 2151,
"language": "NL"
}
Response:
{
"data": {
"product": {
"productId": 2151,
"names": [{ "value": "LED Paneel Backlit 60x60 3000K" }],
"attributes": {
"items": [
{
"attributeDescription": {
"name": "AFMETING_MM",
"descriptions": [{ "language": "NL", "value": "Afmeting mm" }],
"type": "TEXT",
"group": null
},
"value": {
"textValues": [{ "language": "NL", "values": ["60 x 60 cm"] }]
}
},
{
"attributeDescription": {
"name": "KLEURTEMPERATUUR",
"descriptions": [{ "language": "NL", "value": "Kleurtemperatuur in Kelvin" }],
"type": "TEXT",
"group": "LED verlichting"
},
"value": {
"textValues": [{ "language": "NL", "values": ["3000K"] }]
}
}
],
"itemsFound": 2
}
}
}
}
Attribute values use different types depending on the attribute's type. Each type has its own fragment:
| Attribute type | Fragment | Value field |
|---|---|---|
TEXT | AttributeTextValue | textValues (localized array) |
ENUM | AttributeEnumValue | enumValues (string array) |
COLOR | AttributeColorValue | colorValue (hex string) |
INT | AttributeIntValue | intValue |
DECIMAL | AttributeDecimalValue | decimalValue |
DATETIME | AttributeDateTimeValue | dateTimeValue |
Include all six fragments in your query to handle any attribute type. The attributeDescription provides the human-readable label via descriptions and an optional group for organizing attributes into sections.
Filtering attributes
The attributeDescription input controls which attributes are returned:
| Filter | Description |
|---|---|
isPublic | Attributes intended for end-user display |
isSearchable | Attributes used for catalog filtering |
names | Filter by specific attribute names |
groups | Filter by attribute group |
types | Filter by attribute type (TEXT, ENUM, etc.) |
Set includeDefaultValues to false to only return attributes that have a value set on the product, excluding inherited defaults.
Cross-sell and upsell
Products and clusters can have cross-sell and upsell relations. Use crossupsellsFrom to fetch them:
query GetCrossSellProducts($productId: Int!, $language: String) {
product(productId: $productId) {
productId
names(language: $language) {
value
}
crossupsellsFrom(input: { offset: 12 }) {
items {
type
subType
productTo {
... on Product {
productId
names(language: $language) {
value
}
price {
gross
net
}
}
}
clusterTo {
... on Cluster {
clusterId
names(language: $language) {
value
}
}
}
}
itemsFound
}
}
}
Variables:
{
"productId": 25,
"language": "NL"
}
Response:
{
"data": {
"product": {
"productId": 25,
"names": [{ "value": "Industriële Kachel HT-1001" }],
"crossupsellsFrom": {
"items": [
{
"type": "RELATED",
"subType": null,
"productTo": {
"productId": 26,
"names": [{ "value": "Industriële Kachel HT-350" }],
"price": { "gross": 2275.21, "net": 2753.0 }
},
"clusterTo": null
},
{
"type": "PARTS",
"subType": null,
"productTo": {
"productId": 28,
"names": [{ "value": "Vuurvaste Bodemplaat HT-425" }],
"price": { "gross": 172.44, "net": 208.65 }
},
"clusterTo": null
},
{
"type": "ACCESSORIES",
"subType": null,
"productTo": {
"productId": 31,
"names": [{ "value": "Hitteschild Links HT-101" }],
"price": { "gross": 70.0, "net": 84.7 }
},
"clusterTo": null
}
],
"itemsFound": 22
}
}
}
}
Each relation has a type that indicates the relationship. For the available types and their meaning, see Understanding products and categories.
Each relation points to either a productTo (a standalone product) or a clusterTo (a cluster), never both. Both fields return the IBaseProduct interface, so use inline fragments (... on Product and ... on Cluster) to access type-specific fields like productId, price or clusterId.
Bundle details
Products can be part of bundles. The bundles field returns all bundles where this product participates:
query GetProductBundleDetails($productId: Int!, $language: String) {
product(productId: $productId) {
productId
names(language: $language) {
value
}
bundles {
id
name
discount
condition
price {
gross
net
originalGross
originalNet
}
items {
productId
isLeader
product {
names(language: $language) {
value
}
sku
price {
gross
net
}
}
}
}
}
}
Variables:
{
"productId": 2017,
"language": "NL"
}
Response:
{
"data": {
"product": {
"productId": 2017,
"names": [{ "value": "Ergonomische Bureaustoel Pro 300 Mesh Zwart" }],
"bundles": [
{
"id": "019c9425-5eaf-72b5-9315-d7182932db45",
"name": "Bureaustoel Pro + Vloermat",
"discount": 10,
"condition": "EP",
"price": {
"gross": 270.89,
"net": 327.77,
"originalGross": 272.65,
"originalNet": 329.91
},
"items": [
{
"productId": 2017,
"isLeader": "Y",
"product": {
"names": [{ "value": "Ergonomische Bureaustoel Pro 300 Mesh Zwart" }],
"sku": "BST-PRO-300",
"price": { "gross": 255.0, "net": 308.55 }
}
},
{
"productId": 2044,
"isLeader": "N",
"product": {
"names": [{ "value": "Vloermat PVC Transparant 120 x 90 cm" }],
"sku": "0123AA",
"price": { "gross": 17.65, "net": 21.36 }
}
}
]
}
]
}
}
}
The isLeader field (Y or N) identifies the main product in the bundle. The leader is the product the bundle is displayed on. Compare originalGross/originalNet (sum of individual prices) with gross/net (discounted bundle price) to show the savings.
To filter product listings to only products with bundle offers, use hasBundle: Y in ProductSearchInput. For adding bundles to a cart, see Cart management.
Next steps
- Understanding products and categories — how clusters, products, attributes and bundles work
- Querying products — listing queries with filtering, pagination and search
- Media and assets — product images, videos and documents
- Understanding pricing layers — price types, bulk prices and surcharges