category
Reads the store's category tree, or a single category, from the Salla API; the tree is cached per branch.
import { category } from '@salla.sa/twilight-theme-engine/api/category';In plain words
Categories are how a store groups its products: Furniture, and Chairs inside it. category.list() returns the whole tree (each category lists its sub_categories), and category.find(id) returns one category.
To show the products of a category, ask product.list with source: 'categories' and the category's numeric id.
Signature
category.list(): Promise<Category[]>
category.find(id: string): Promise<Category>
category.findOrThrow(id: string): Promise<Category> // any failure → NotFoundError
category.queries.list() // key ['categories', 'list', { scope }]
category.queries.detail(id) // key ['categories', 'detail', id]
interface Category {
id: number | string; // hashed, e.g. 'mgjZqj'
id_?: number; // numeric, e.g. 708738657
name: string;
url: string;
description?: string;
icon?: string | null;
image?: string | null;
products_count?: number;
sub_categories?: Category[];
}Try it live
Loading categories…
import { useQuery } from '@tanstack/react-query';
import { category } from '@salla.sa/twilight-theme-engine/api/category';
import { product } from '@salla.sa/twilight-theme-engine/api/product';
export function CategoryShelf() {
const { data: categories = [] } = useQuery(category.queries.list());
const first = categories[0];
const { data: products } = useQuery({
...product.queries.list({ source: 'categories', sourceValue: first?.id_ ? [first.id_] : [], perPage: 4 }),
enabled: first?.id_ != null, // the API answers 422 without a category
});
return (
<section>
<h2>{first?.name}</h2>
<ul>
{products?.items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</section>
);
}
Example
import { useQuery } from '@tanstack/react-query';
import { category } from '@salla.sa/twilight-theme-engine/api/category';
import { Link } from '@salla.sa/twilight-theme-engine/components/common';
export function CategoryPills() {
const { data: categories = [] } = useQuery(category.queries.list());
return (
<nav className="category-pills">
{categories.map((item) => (
<Link key={item.id} to={item.url}>
{item.name}
</Link>
))}
</nav>
);
}
How it behaves
Endpoints:
GET categories(returnsdata ?? []) andGET categories/{id}, both public.findaccepts either id form: the demo store answeredmgjZqjand708738657alike.Only the list key carries the branch scope. The detail key does not, so one cached category is shared across branches.
The engine product-listing loader reads a category with
queryClient.fetchQuery(category.queries.detail(id))before listing its products.urlis an absolute address on the store domain (on a shared Salla host it includes the store username). The engineMainMenuhands such addresses to the engineLinkunchanged.
Gotchas
Products want the numeric
id_, in an array:product.list({ source: 'categories', sourceValue: [item.id_] }). The hashedidanswers HTTP 422 (demo store).id_is optional in the type. Check it before building a products query, or the request goes out without a category and fails.
Related
Lists products from any source (latest, offers, a category, a brand, a search) with page size, sort, filters and cursor pagination.
getActiveScope, getActiveScopeIdReturn the store branch (scope) the shopper picked, or null; the API client sends it, and scope-sensitive query keys include it.
menuFetches the header and footer menus the merchant built in the Salla dashboard, in the language of the current page.
Source and docs
- Engine source:
packages/theme-engine/src/api/category.ts