Vue Storefront is now Alokai! Learn More
Working with Facets

Working with Facets

Learn how to configure your middleware to to customize facet types and filter facets

Facets are a part of every storefront, allowing customers to deeply filter through products using certain attributes.

The Unified Data Layer provides an SfFacet interface that each unified integration's normalizer will return.

export interface SfFacet {
  label: string;
  name: string;
  values: SfFacetItem[];
  // set by getFacetType method
  type: Maybe<SfFacetType | (string & Record<never, never>)>;
}

We can customize how this SfFacet object is created by using the middleware configuration.

Middleware Configuration

Using the middleware configuration, you can customize how the type of each facet is normalized and also filter out unwanted facets from your API responses.

Customizing SfFacet type

The type property in the SfFacet object can be set using the getFacetType method.

This method is used by your integration when normalizing an object to match the SfFacet data structure. The implementation of this method will change depending on the platform you're using and the facets you want to customize.

getFacetType accepts the raw facet object from your e-commerce backend and returns either an existing SfFacetType or a custom string.

// middleware.config.ts
import { SfFacetTypes } from "@vsf-enterprise/unified-api-[Integration]";

export const unifiedApiExtension = createUnifiedExtension<Context>()({
  normalizers,
  apiMethods: {
    ...methods<typeof normalizers>(),
  },
  config: {
    getFacetType: (facet) => {
      if (facet.facet === "color") {
        return "COLOR";
      }
      if (facet.facet === "size") {
        return "SIZE";
      }

      return SfFacetTypes.MULTI_SELECT;
    },
  },
});

If you don't provide a custom getFacetType method, the default value will depend on the Unified Integration that you're using. You can find the default function in your integration's normalizeFacet method.

Filtering facets

Sometimes, your backend may return certain facets that you don't want included. You can filter out unwanted facets using the filterFacets configuration and provide filtering condition based on raw facet input.

For example, when using the searchProducts Unified Method, all of the facets coming from the search results are also returned. But you may want to filter some of them out, such as facets that are also categories. For cases like this, you can use the filterFacets configuration.

filterFacets runs before normalization, so the input is the raw facet object from your e-commerce backend and the output is a boolean value that determines whether the facet should be included in the response.

// middleware.config.ts
export const unifiedApiExtension = createUnifiedExtension<Context>()({
  normalizers,
  apiMethods: {
    ...methods<typeof normalizers>(),
  },
  config: {
    filterFacets: (facet) => facet.category === false,
  },
});

As a simplified example of how filterFacets is used, the below code shows part of a standard integration's searchProducts implementation.

return {
  // ...
  facets: rawFacets // raw facet array following your backends data structures
      .filter((facet) => filterFacets(facet))
      .map((facet) => normalizeFacet(facet, normalizerContext))
  // ...
}

If you don't provide a custom filterFacets method, all facets will be included in the response.

Example Usage

After configuring the middleware, you can use the Unified Data Model's SfFacet data structure in your storefront application.

Knowing the possible values of SfFacet.type can be helpful for iterating over facets to display filters, queries, tags, and more.

Next.js
// components/CategoryFilters.tsx

export function CategoryFilters() {
  // ...

  return (
    <div>
      {facets.map((facet) => {
        switch (facet.type) {
          case "SIZE": {
            return <FilterSize facet={facet} key={facet.name} />;
          }
          case "COLOR": {
            return <FilterColor facet={facet} key={facet.name} />;
          }
          case "SINGLE_SELECT": {
            return <FilterRadio facet={facet} key={facet.name} />;
          }
          default: {
            return <FilterCheckbox facet={facet} key={facet.name} />;
          }
        }
      })}
    </div>
  );
}