@afosto/cms-client
v0.0.1
Published
Framework-agnostic Afosto Storefront API client with TypeScript types and formatter utilities. Used as the foundation for `@afosto/react` and `@afosto/vue`, but can also be used standalone for custom integrations.
Readme
@afosto/cms-client
Framework-agnostic Afosto Storefront API client with TypeScript types and formatter utilities. Used as the foundation for @afosto/react and @afosto/vue, but can also be used standalone for custom integrations.
Installation
npm install @afosto/cms-client
# or
pnpm add @afosto/cms-clientQuick start
import { createClient } from '@afosto/cms-client';
const client = createClient('https://storefront.afosto.net');
// Fetch a collection page
const page = await client.getPage('/heren/');
if (page.type === 'collection') {
console.log(page.content.products); // ProductObject[]
console.log(page.filters); // Record<string, FormattedFilter>
}
// Fetch a product page
const product = await client.getPage('/heren/t-shirt-wit');
if (product.type === 'product') {
console.log(product.content.price); // "€29,95"
console.log(product.content.attributes); // Record<string, Attribute>
}API client
createClient(baseURL)
Creates a fetch client configured for the Afosto Storefront API.
- Sends
Content-Type: application/jsonon every request - Automatically retries once on HTTP 429 (rate limit) with a 5–10 s delay
- Returns a
FormattedPage— allRecord<string, T>fields already converted to arrays
const client = createClient('https://storefront.afosto.net');client.getPage(slug, params?, options?)
Fetches any Afosto page by slug. Active filters are passed as query params.
// Collection with filters applied
const page = await client.getPage('/heren/', { kleur: 'wit', maat: 'M' });
// Second page
const page = await client.getPage('/heren/', { page: '2' });The page.type field identifies the page:
| type | What it contains |
| ----------------------------------- | ------------------------------------------------------------------------------------- |
| 'collection' | content.products, content.filters, content.active_filters, content.pagination |
| 'product' | content.attributes (variants), content.price, content.specifications |
| 'page' | Static content blocks |
| 'cart' / 'checkout' / 'login' | — |
Formatters
The Afosto API returns many fields as Record<string, T> keyed by an internal key rather than arrays. The formatters convert these to arrays so you never have to call Object.values() yourself.
client.getPage() already applies all formatters. They are also exported individually if you are working with the raw API response.
import {
formatPage,
formatFilters,
formatActiveFilters,
formatPagination,
formatSort,
formatMenus,
formatBreadcrumbs,
formatPrice,
} from '@afosto/cms-client';| Formatter | Converts |
| --------------------------------- | --------------------------------------------------------------------------------------------------- |
| formatPage(page) | Orchestrates all formatters on the full Page response |
| formatFilters(filters, locale?) | Record<string, Filter> → Record<string, FormattedFilter> (attributes as array, locale-filtered) |
| formatActiveFilters(filters) | Record<string, ActiveFilter> → Record<string, FormattedActiveFilter> |
| formatPagination(pagination) | Adds .page (current page number) and .itemsPerPage helper |
| formatSort(sort) | sort.options from Record to SortOption[] |
| formatMenus(menus) | Recursively converts menu.items from Record to MenuItem[] |
Logic utilities
Pure helper functions for building filter and variant UIs on top of the API data.
Filters
import {
filterReducer,
extractParamsFromLink,
buildFilterParams,
isFilterActive,
} from '@afosto/cms-client';
// Manage active filter state
const next = filterReducer(activeFilters, {
type: 'TOGGLE',
filterId: 'kleur',
value: 'wit',
});
const reset = filterReducer(activeFilters, { type: 'RESET' });
// Extract query params from a filter attribute's link URL (for re-fetching)
const params = extractParamsFromLink(filterAttribute.link);
// → { kleur: 'wit', maat: 'M' }
// Build query params from local state
const params = buildFilterParams(activeFilters);Quantity
import {
clampQuantity,
validateQuantity,
incrementQuantity,
decrementQuantity,
} from '@afosto/cms-client';
clampQuantity(0, { min: 1, max: 99 }); // → 1
clampQuantity(7, { min: 0, max: 100, step: 5 }); // → 5 (rounds to nearest step)
validateQuantity(5, { min: 1, max: 99, step: 1 }); // → true
incrementQuantity(3, { min: 1, max: 10, step: 2 }); // → 5
decrementQuantity(5, { min: 1, max: 10, step: 2 }); // → 3Variants
import {
formatAttributes,
getAvailableOptions,
findOptionByKeys,
} from '@afosto/cms-client';
// Convert Record<string, Attribute> to FormattedAttribute[]
const attributes = formatAttributes(product.content.attributes);
// Filter to only available options
const available = getAvailableOptions(attributes);
// Find the selected option by key map
const selected = findOptionByKeys(attributes, { kleur: 'wit', maat: 'M' });TypeScript types
import type {
Page,
FormattedPage,
PageType,
ProductObject,
Filter,
FormattedFilter,
FilterAttribute,
ActiveFilter,
FormattedActiveFilter,
Attribute,
FormattedAttribute,
AttributeOption,
Pagination,
FormattedPagination,
Sort,
FormattedSort,
SortOption,
Menu,
FormattedMenu,
MenuItem,
Breadcrumb,
Image,
ShopProperties,
Inventory,
Specification,
TextBlock,
Slider,
} from '@afosto/cms-client';License
MIT
