cart
Reads a cart by its id and returns it with the loyalty prize and gift details merged into the cart object.
import { cart, CartApiResponse } from '@salla.sa/twilight-theme-engine/api/cart';In plain words
The cart is kept by Salla, not by your theme. Adding and removing products goes through the Salla SDK (window.Salla.cart); this module only reads a cart: its items, totals and coupon.
It needs the cart's id, and only the browser knows it: the Salla SDK keeps it for the visitor. So a cart is read after the page has loaded, never while the server renders.
Signature
cart.get(cartId: number): Promise<Cart>
cart.getOrThrow(cartId: number): Promise<Cart> // any failure → NotFoundError
cart.queries.detail(cartId: number) // key ['cart', cartId]
interface CartApiResponse { // the raw `data` of GET cart/{id}
cart: Omit<Cart, 'loyalty' | 'gift'>;
loyalty?: Cart['loyalty'];
gift?: Cart['gift'];
}
// cart.get returns { ...data.cart, loyalty: data.loyalty ?? null, gift: data.gift ?? null }Try it live
import { useEffect, useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { cart } from '@salla.sa/twilight-theme-engine/api/cart';
import { getSallaSDK } from '@salla.sa/twilight-theme-engine/utils';
export function MiniCart() {
const [cartId, setCartId] = useState<number | null>(null);
useEffect(() => {
// Browser only: the server has no cart id to ask about.
getSallaSDK()?.cart.api.getCurrentCartId().then(setCartId);
}, []);
const { data } = useQuery({ ...cart.queries.detail(cartId ?? 0), enabled: cartId !== null });
if (!data) return null;
return <span className="mini-cart">{data.count}</span>;
}
Example
import { useEffect, useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { cart } from '@salla.sa/twilight-theme-engine/api/cart';
import { useMoney } from '@salla.sa/twilight-theme-engine/hooks/useMoney';
import { getSallaSDK } from '@salla.sa/twilight-theme-engine/utils';
export function CartTotal() {
const { format } = useMoney();
const [cartId, setCartId] = useState<number | null>(null);
useEffect(() => {
// Browser only: the Salla SDK knows the visitor's cart id.
getSallaSDK()?.cart.api.getCurrentCartId().then(setCartId);
}, []);
const { data } = useQuery({ ...cart.queries.detail(cartId ?? 0), enabled: cartId !== null });
if (!data) return null;
return <span className="cart-total">{format(data.total)}</span>;
}
How it behaves
Endpoint:
GET cart/{id}. It works for guest carts; the customer token is sent when there is one.The engine cart page gets the id with
window.Salla.cart.api.getCurrentCartId()in an effect and runsuseQuery({ ...cart.queries.detail(cartId!), enabled: !!cartId }).In the Salla SDK source,
getCurrentCartId()asks the API for the latest cart when it has no id stored, which creates an empty cart for a new visitor.Adding or removing items through the SDK does not refresh this query. Refetch it after the SDK reports a change.
Gotchas
There is no cart id on the server, so a cart can never be part of the server-rendered HTML. Keep the query disabled until the id arrives;
cart.queries.detail(0)would otherwise requestcart/0.getOrThrowturns every failure, a timeout included, intoNotFoundErrorand the 404 page. For a cart widget usegetor the query and handle the error.
Related
Source and docs
- Engine source:
packages/theme-engine/src/api/cart.ts