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:
| Field | Description |
|---|---|
term | Text search across product fields |
searchFields | Control which fields term searches |
categoryId | Filter by category |
textFilters | Filter by text/enum attribute values |
rangeFilters | Filter by numeric attribute ranges |
price | Filter by price range (requires both from and to) |
sortInputs | Sort by field (NAME, PRICE, SKU, RELEVANCE, etc.) |
manufacturers | Filter by manufacturer name |
skus | Filter by SKU codes |
statuses | Filter by product status (default: active) |
hasBundle | Filter to products with bundle offers (Y or N) |
isBundleLeader | Filter to bundle leader products (Y or N) |
applyOrderlists / orderlistIds | Restrict to orderlist assortment |
class | Filter by PRODUCT or CLUSTER |
hidden | Include or exclude hidden products |
containerSlugs / containerPathSlugs | Filter by path slugs |
page / offset | Pagination (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.