Skip to main content
Just like blocks, listings are managed in the API Builder section of your dashboard. The main difference between listings and blocks is that listings are designed to work with lists of items - where each item is a block.
Listings overview

Overview of listings in the API Builder

Why use listings?

It’s easier to understand by comparing it to components in a frontend framework like React. A listing component takes a list of items and renders them in a grid or list.
product-listing.jsx
function ProductListing({ products }) {
  return (
    <div>
      {products.map((product) => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  );
}
A product card component takes a single product and renders it in a card.
product-card.jsx
function ProductCard({ product }) {
  return <div>{product.name}</div>;
}
Most of the time, you will fetch the list of products from a listing component and pass the products one by one to the product card component. In that case, you can use a listing to fetch the products. By defining a listing, you can fetch the products in a single request like this:
listing.jsx
import { client } from "@frontstack/client";
const { items } = await client.listing("ProductListing");

return (
  <div>
    {items.map((product) => (
      <ProductCard key={product.id} product={product} />
    ))}
  </div>
);

Input Parameters

Oftentimes, you will want to fetch a list of items based on a certain criteria. For example, you might want to fetch all products in a certain category. Using parameters, you can define the criteria for the list. Parameters can be strings - for example a category ID or arrays - for example a list of product IDs. In the Filter Query card inside the Query tab of the listing, you can map the parameters to query criteria that will be used to filter the results. Parameters are typically controlled by the frontend application based on context (like the current category being viewed), while query options are intended for user-controlled actions such as searching, filtering, and pagination. This separation allows yout to maintain core content relevance while enabling user refinement of results.

Listing Settings

Listing settings are used to control how the listing behaves and how it can be queried.
Listing settings

Listing settings in the query tab of a listing

Filter query

The filter query is a pre-defined query that will always be applied to the listing. For example, you might want to fetch all products in a certain category. You can add mulitple query conditions to refine the results.

Filter options

Press the Settings button in the query tab to see filter, search, sorting and pagination settings.
Filters are used to filter the results of the listing. They provide data to display filters in the application UI - often used to filter by tags, categories or other properties.

Facets

By default, the listing response will include facet information (up to 200 possible values) for your filters. For cases with lots of different options (where you’d usually not even show a filter selection - for example for price ranges), you should consider disabling the facets. This will reduce your response size and increase performance. See an example

Active Filters

We distinguish between active filters and all filters, so your facets and aggregations will be returned both applying to the active set (with the result key) and applying to all filters (with the total key).

Searchable fields

You can mark fields as searchable to enable a text search on them. This will allow your users to search for items by typing in a search query. Often used for product names, aliases or descriptions. By default, Frontstack will run a fuzzy search on your searchable fields to find the best matches and counteract user typos. You can disable fuzzy search to get exact matches only.

Sorting

You can define multiple sorting fields to allow your users to sort the results by different criteria as well as specify the default sorting. For the default sorting, you can also define a default sorting direction (ascending or descending).
Sorting options

Sorting options in the query tab of a listing

Dynamic Sortings

If you have any dynamic (or parameterized) sorting structures like category-specific positioning of products, you can use our built-in “Sorting” data type in your storage to map those structures. If you use such a field in your listing as a sort option, we will ask you to select a parameter of that listing to use as the sorting key. Frontstack will match that parameter’s value to the key inside your sorting structure and use those sorting positions. Don’t worry about any missing keys, we’ll handle those gracefully. Be aware that sorting keys (and the parameter) must be strings.

Pagination

Lets you define how many items per page are fetched.

Fetching a listing

If this is your first time reading, check out how to generate the client first.
To fetch a listing, you can use the clients listing method.
const { items } = await client.listing("ProductListing");

Parameters

If you need to specify parameters, you can do so by passing them as the second argument to the listing method.
const { items } = await client.listing("ProductListing", {
  categoryId: "123",
});

Filtering

For applying filters (as defined in the Filter options), you can pass a config object with a query field as the third argument to the listing method. You can filter by all fields that are configured as filter options. The following filter types are supported:
  • equals
  • range
  • contains
  • and, or and not - allows chaining multiple filters
By default, all filters are applied as and filters.
const { items } = await client.listing("ProductListing", {
  categoryId: "123",
  },
  query: {
    filter: [
      { field: "price.amount", type: "range", from: 3000, to: 10000 },
      { type: "or", filter: [
        { field: "properties.activity", type: "equals", value: ["Running"] },
        { field: "properties.activity", type: "equals", value: ["Cycling"] },
      ]},
      {
        field: "categoryNames",
        contains: ["Sportswear"]
      }
    ],
  },
);
If you pass an array of values to an equals, or contains filter, it will be treated as an or filter group. So in the example below, items matching either “Running” or “Cycling” will be returned.
const { items } = await client.listing("ProductListing", {
  categoryId: "123",
  },
  query: {
    "filter": [
        { "field": "properties.activity",  "type": "equals", "value": ["Running", "Cycling"] },
    ]
  }
);

Pagination

To paginate the results, you can pass a page and a limit field to the query object. Note, that the limit parameter respects the default and maximum limit set in the pagination settings.
const { items } = await client.listing("ProductListing", {
  categoryId: "123",
  },
  query: { page: 6, limit: 24 },
);
To perform a search, you can pass a search field to the query object.
const { items } = await client.listing("ProductListing", {
  categoryId: "123",
  },
  query: { search: "Running shoe" },
);

Response

When you fetch a listing, the response contains both the items and helpful metadata for building your UI, including facets for filters. Typical top-level fields:
{
  "items": [
  ],
  "total": 998,
  "page": {
    "last": 42,
    "limit": 24,
    "current": 1,
    "next": null,
    "prev": null
  },
  "filter": {},
  "aggregation": {},
  "sort": []
}
  • items: The resulting items (if configured in Listing Settings).
  • total: Total number of items matching the query (if configured in Pagination).
  • page: Current page information (if configured in Pagination).
  • filter: Facet data for configured filters (if enabled in Filter options).
  • aggregation: Results for configured aggregations (if enabled).
  • sort: Configured sorting possibilities (up to 3) (as defined in Sorting).

Filter (facets) in the response

Facets describe the available options for each configured filter and how many results each option would yield under the current query. A facet has the following properties per option:
  • option: A stringified value that can be used for UI/control bindings
  • value: The raw value of the option (e.g., “M”, 42, “Running”, false)
  • count: The number of items for this option under the current query
  • selected: Whether this option is currently active in the query
  • disabled: Whether this option is selectable given the current query
By default, the listing response will include facet information (up to 200 possible values) for your filters. For cases with lots of different options (where you’d usually not even show a filter selection - for example for price ranges), you should consider disabling the facets. This will reduce your response size and increase performance.

OR filter group behavior

When a filter is configured or applied as an OR group (multiple alternatives where any can match):
  • For options that are currently selected in that group, we can compute and return the correct count
  • For options in that OR group that are not currently selected, we cannot determine an accurate count without changing the query semantics. In this case we return:
    • count: null
    • disabled: false (meaning users may select it)
This allows the UI to keep these options selectable while making it clear that an exact count is not available.

Example: facet entry

The response portion for a boolean-like facet (or any discrete option) can look like this:
{
  "filter": {
    "activity": [
      {
        "count": 100,
        "disabled": false,
        "option": "running",
        "selected": true,
        "value": "running"
      }
    ]
  }
}

Example: OR group with unknown counts for non-selected options

{
  "filter": {
    "activity": [
      { "option": "Running", "value": "Running", "selected": true,  "count": 42,  "disabled": false },
      { "option": "Cycling", "value": "Cycling", "selected": false, "count": null, "disabled": false },
      { "option": "Hiking",  "value": "Hiking",  "selected": false, "count": null, "disabled": false }
    ]
  }
}

Aggregations

Filters will also receive aggregation information by default, with these fields for your numeric filter fields:
  • min - The minimum value in the facet values
  • max - The maximum value in the facet values
  • avg - The average of the facet values
  • sum - The sum of the facet values
  • total - The count of facet values (will return 1 for price fields)
For string filter fields, you will only get the total aggregation. See the following examples:
{
  # ...
  "aggregation": {
    "stock": { // numeric field
      "result": {
        "max": 50,
        "min": 10,
        "avg": 32.5,
        "sum": 130,
        "total": 4
      },
      "total": {
        "max": 50,
        "min": 5,
        "avg": 27,
        "sum": 135,
        "total": 5
      }
    },
    "sku": { // string field
      "result": {
        "total": 10 // 10 different values in filtered result
      },
      "total": {
        "total": 20 // 20 different values in full listing
      }
    }
  }
}
If you don’t need aggregations for a filter field, you can disable them and reduce your response size.