api
The HTTP client behind every engine API module: ky, pointed at the Salla Store API, adding store, language, version, token and branch headers.
import { api } from '@salla.sa/twilight-theme-engine/api/client';In plain words
api is like fetch, but it already knows where the Salla Store API lives and who is asking. api.get('categories') calls https://api.salla.dev/store/v1/categories and adds which store you mean, the page language, and the customer's login token when there is one. .json() turns the answer into a plain object.
Use it for an endpoint no engine module wraps yet. Every answer comes wrapped: what you want is under data.
Signature
const api: KyInstance
// ky.extend({ prefix: 'https://api.salla.dev/store/v1', timeout: 8000, hooks: { beforeRequest } })
api.get(path, options?).json<T>(): Promise<T>
api.post(path, { json }), api.put(path), api.delete(path)
// Set before every request:
// accept, accept-language (page locale), s-app-os, s-app-version, s-ray, s-source,
// s-theme-type: react, s-store-api-version, x-requested-with, s-cf-proxy-whitelist-id
// store-identifier unless the request already has one
// s-version-id when a theme version resolves
// Authorization: Bearer <token> when a customer token exists
// s-scope-id, s-scope-type, s-scope-allocation-type, s-scope-allocation-id when a branch is selectedTry it live
GET https://api.salla.dev/store/v1/categories…
import { api } from '@salla.sa/twilight-theme-engine/api/client';
export async function load() {
// GET https://api.salla.dev/store/v1/categories
// store-identifier, language, version, token and scope headers are added for you.
const { data } = await api.get('categories').json<{ data: unknown }>();
return data;
}
Example
import { api } from '@salla.sa/twilight-theme-engine/api/client';
interface Voucher {
code: string;
quantity_remaining: number;
}
/** The customer's vouchers: an endpoint no engine module wraps. */
export async function getVouchers(): Promise<Voucher[]> {
const body = await api
.get('coupons?is_voucher=1&include_quantity_orders=1')
.json<{ data?: Voucher[] | null }>();
return body.data ?? [];
}
How it behaves
It is a ky v2 instance.
prefixjoins with a slash, so'categories'and'/categories'reach the same URL.A non-2xx answer throws
HTTPError: the status iserror.response.status, and ky has already parsed the body ontoerror.data.await error.response.json()does not work, because ky consumed the body.ky retries a GET up to 2 more times on 408, 413, 429, 500, 502, 503 and 504, each attempt with its own 8 s timeout. Inside
useQuery, the engine QueryClient adds 1 retry on top.The token comes from the twilight context, then from the
tokencookie (the request cookie on the server;document.cookie,Salla.storageor the SDK in the browser). See getAuthToken.Customer-only endpoints answer a guest with HTTP 400 and
error.codetoken_not_provided, not 401 (orders,auth/user,notificationsandbalance/pointson the demo store).
Gotchas
The base URL is fixed. The docblock describes a
VITE_API_URLoverride, but that line is commented out inclient.ts: setting the variable changes nothing.src/api/README.md shows
api.get('custom-endpoint', { locale } as any). There is no locale option: the language header always comes from the page being rendered.Nothing unwraps the envelope for you:
api.get(path).json()resolves{ status, success, data, cursor? }. A request can also fail with HTTP 200: given a badcursor, the demo store answered 200 with anerrorand nodata. Readdata, and check it exists.
Related
A bare ky client for Salla's public CDN at cdn.salla.network; it sends no store, language or customer headers.
getAuthTokenReturns the customer's login token the way the API client finds it, or null for a guest; use it to switch customer-only queries on.
getActiveScope, getActiveScopeIdReturn the store branch (scope) the shopper picked, or null; the API client sends it, and scope-sensitive query keys include it.
STORE_IDENTIFIER_HEADERThe name of the store-identifier header, which tells the Salla API which store a request is about.