This guide builds the read side of a storefront: product listing pages, product detail pages and content pages. The examples use the TypeScript SDK and work in any server runtime — Next.js, Astro, Remix, a build script. The full endpoint reference is at api.minipim.com/docs.
1. Get a key and a client
In the admin, open System → API keys, create a key, and store it as a server-side environment variable. It’s a secret: never ship it to the browser.
npm install @minipim/sdk
// lib/pim.ts
import { createMinipimClient } from '@minipim/sdk';
export const pim = createMinipimClient({
baseUrl: 'https://api.minipim.com', // or your self-hosted API
apiKey: process.env.MINIPIM_API_KEY!,
});
The key carries its organization, so there’s nothing else to configure.
2. Listing pages
const { data } = await pim.GET('/v1/products', {
params: { query: { status: 'active', categoryId, includeDescendants: true, limit: 48, offset: 0 } },
});
// data = { data: Product[], limit, offset, hasMore }
- Always pass
status: 'active'— lists include drafts and archived products otherwise. - Pages use
limit(up to 200) andoffset;hasMoretells you whether there’s another page. includeDescendants: truemakes a parent category show everything beneath it.- Unknown query parameters are rejected with a 422, which catches typos early.
3. Product detail by slug
Product detail is fetched by id, so resolve the URL slug with the list filter first:
const { data: list } = await pim.GET('/v1/products', {
params: { query: { slug: params.slug, status: 'active', limit: 1 } },
});
const id = list?.data[0]?.id;
if (!id) return notFound();
const { data: product } = await pim.GET('/v1/products/{id}', {
params: { path: { id }, query: { locale: 'en_US', channel: 'web' } },
});
Passing locale and/or channel adds resolvedAttributes: one value per attribute, picked by the closest match — exact locale and channel, then locale only, then channel only, then the unscoped value. Render from resolvedAttributes and you don’t have to handle the stored per-locale shape yourself.
Money comes back as { amount_cents, currency } — format it with the SDK’s money helpers rather than dividing floats.
4. Variants
Variants are a separate call:
const { data: variants } = await pim.GET('/v1/products/{id}/variants', {
params: { path: { id } },
});
Each variant has its SKU and only the values it overrides; anything missing inherits from the product.
5. Content pages
const { data: page } = await pim.GET('/v1/content/by-slug/{slug}', {
params: { path: { slug }, query: { status: 'active' } },
});
Pass status: 'active' here too — the slug lookup returns drafts otherwise. Bodies are Markdown; render them with whatever Markdown component you use.
6. Static builds
For a full static build, the SDK’s paginate helper walks every page for you:
import { paginate } from '@minipim/sdk';
for await (const product of paginate(pim, '/v1/products', { query: { status: 'active' } })) {
// generate a page
}
Next
Make the site update when the catalog changes: keep a storefront in sync with webhooks.