Skip to main content

Querying products

Fetch products from the Propeller catalog using GraphQL. This page covers how to retrieve product listings, handle pagination, work with filters and deal with clusters (product groups) in your query results.

Fetching products

The products query returns a paginated list of catalog items. Each item is either a Product (a single purchasable item) or a Cluster (a group of related products). Your query needs to handle both types.

query GetProducts($language: String) {
products {
items {
... on Product {
productId
sku
names(language: $language) {
value
}
price {
gross
net
}
slugs(language: $language) {
value
}
media {
images(search: { sort: ASC, offset: 1 }) {
items {
alt(language: $language) {
value
}
imageVariants(
input: {
transformations: {
name: "small"
transformation: {
width: 50
height: 50
fit: BOUNDS
format: WEBP
}
}
}
) {
url
}
}
}
}
}
... on Cluster {
names(language: $language) {
value
}
sku
defaultProduct {
productId
price {
gross
net
}
slugs(language: $language) {
value
}
media {
images(search: { sort: ASC, offset: 1 }) {
items {
imageVariants(
input: {
transformations: {
name: "small"
transformation: {
width: 50
height: 50
fit: BOUNDS
format: WEBP
}
}
}
) {
url
}
}
}
}
}
}
}
itemsFound
page
pages
}
}

The response contains both products and clusters in the same items array. For clusters, use defaultProduct to get the price, images and slug to display on a listing page. The cluster itself holds shared data like the name.

You can filter results to only products or only clusters using the class parameter in ProductSearchInput with values PRODUCT or CLUSTER.

Pagination

Control how many products appear per page using offset (items per page) and page (current page number). The response includes itemsFound, pages, start and end so you can build pagination controls.

query GetProducts($page: Int, $offset: Int) {
products(input: { page: $page, offset: $offset }) {
items {
... on Product {
productId
}
}
itemsFound
start
end
page
pages
}
}

Variables:

{
"page": 2,
"offset": 24
}

Categories and other list queries use the same pagination pattern.

Filtering by attributes

Products have attributes (like color, size or material) that you can use as filters. First retrieve the available filters for the current product set, then pass selected values back into the query.

Get available filters

query CatalogQuery($input: ProductSearchInput) {
products(input: $input) {
filters(input: { isPublic: true, isSearchable: true }) {
attributeDescription {
name
descriptions {
language
value
}
}
textFilters {
value
count
isSelected
}
decimalRangeFilter {
min
max
}
type
}
items {
... on Product {
productId
}
}
itemsFound
}
}

The filters field returns two types: textFilters for discrete values (colors, brands) and decimalRangeFilter for numeric ranges (price, weight). Each text filter includes a count of matching products so you can show facet counts in your UI.

Apply selected filters

Pass the user's selections back through ProductSearchInput.textFilters and ProductSearchInput.rangeFilters:

query CatalogQuery {
products(
input: {
term: "valve"
price: { from: 0, to: 500 }
textFilters: [{ name: "COLOR", values: ["red", "blue"] }]
}
) {
items {
... on Product {
productId
}
}
itemsFound
page
pages
}
}

The term field performs a text search across product fields. Use searchFields to control which fields are included in the search. Filters combine with AND logic: the query above returns products matching "valve" that cost under 500 and are red or blue.

ProductSearchInput reference

The ProductSearchInput type supports the following fields:

FieldDescription
termText search across product fields
searchFieldsControl which fields term searches
categoryIdFilter by category
textFiltersFilter by text/enum attribute values
rangeFiltersFilter by numeric attribute ranges
priceFilter by price range (requires both from and to)
sortInputsSort by field (NAME, PRICE, SKU, RELEVANCE, etc.)
manufacturersFilter by manufacturer name
skusFilter by SKU codes
statusesFilter by product status (default: active)
hasBundleFilter to products with bundle offers (Y or N)
isBundleLeaderFilter to bundle leader products (Y or N)
applyOrderlists / orderlistIdsRestrict to orderlist assortment
classFilter by PRODUCT or CLUSTER
hiddenInclude or exclude hidden products
containerSlugs / containerPathSlugsFilter by path slugs
page / offsetPagination (default: page 1, 12 items per page)

For the full schema definition see the ProductSearchInput reference.

Filtering by orderlist

In B2B commerce, different companies see different product assortments. Orderlists control which products are visible and orderable for a specific company. Pass orderlist IDs to restrict results to products the customer is allowed to see.

query GetOrderListProducts {
products(input: { applyOrderlists: true, orderlistIds: [1, 3, 678] }) {
items {
... on Product {
productId
}
}
itemsFound
}
}

When applyOrderlists is true, only products assigned to the specified orderlists are returned.

Sorting results

Use sortInputs in ProductSearchInput to order results. Available sort fields include NAME, PRICE, SKU, RELEVANCE, CREATED_AT, LAST_MODIFIED_AT, PRIORITY and CATEGORY_ORDER.

query GetProducts {
products(input: { sortInputs: [{ field: NAME, order: ASC }] }) {
items {
... on Product {
productId
}
}
}
}

Including inventory in listings

You can include stock data directly in your product listing query to show availability badges or "in stock" indicators without a separate request.

query GetProducts {
products {
items {
... on Product {
productId
inventory {
totalQuantity
}
}
}
}
}

totalQuantity is the combined stock across all warehouses and suppliers. For a detailed breakdown by warehouse, see the recipe Fetch product stock across warehouses.