Zapytania (Queries)
Ten dokument opisuje kiedy używać każdej query w DoSwiftly Storefront API — pogrupowanej po obszarach biznesowych (sklep, produkty, koszyk, klient, lojalność itd.). Pełne sygnatury, zmienne, fragmenty i opisy renderuje komponent <OperationRef /> na podstawie @doswiftly/storefront-operations/operations.json — zero ręcznej duplikacji, zero driftu.
Szczegółowa referencja typów (objects / inputs / enums / scalars / interfaces / unions) jest w sekcji Types Reference — <OperationRef /> linkuje do niej automatycznie z tabeli Variables. Wszystkie operacje są wstępnie zbudowane w pakiecie @doswiftly/storefront-operations i konsumowane przez @doswiftly/storefront-sdk — większości z nich nie musisz pisać ręcznie.
Komponent <OperationsList /> poniżej grupuje operacje per sekcja. Możesz też przefiltrować po jednej sekcji, np. <OperationsList section="Cart" />. Sekcje pochodzą bezpośrednio z SSOT pakietu operacji.
Shop
| Operation | Kind | Description |
|---|---|---|
Shop | query | Returns shop configuration: name, base + supported currencies, supported locales, branding (logo, colors, fonts, social links), contact info, active payment methods, brand metadata, money format template, and the list of countries the shop ships to. Public; no auth required. Call once per session and cache — almost everything else is contextualized by the shop returned here. |
ShopConfig | query | Minimal Shop payload for `<StorefrontProvider shopData={...}>` from `@doswiftly/storefront-sdk/react`. Returns exactly the fields the SDK's `ShopConfig` interface declares — currency setup (with `localeToCurrencyMap` for browser-locale-based currency detection), language setup, and bot protection. Cache for the session; refetch when the merchant updates currency / language settings or you want to pick up new bot-protection rules. |
Products
| Operation | Kind | Description |
|---|---|---|
Product | query | Fetches a single product by `id` or `handle` (URL-friendly identifier). Pass either — whichever is provided wins; if both are missing, returns null. Returns null if the product is not storefront-accessible (must be `ACTIVE` status with `PUBLIC` or `BUNDLE_ONLY` visibility). |
ProductConfigurator | query | Fetches a product together with its configurator fields, optimized for the product-page configurator UI. filledBy CUSTOMER returns only the fields a shopper edits; pass BOTH to also include seller-prefilled fields. Single round-trip. |
Products | query | Paginated product list (Relay Connection, default page size 20, max 100). The `query` argument supports a structured search syntax — `tag:summer`, `vendor:foo`, `product_type:shirts`, `variants.price:>10`, plus `AND`/`OR`/`NOT` — falling back to free-text title/content search. The `filters[]` array uses multi-filter logic: same field name appears multiple times → OR; different fields → AND. Sort: `RELEVANCE`, `TITLE`, `PRICE`, `NEWEST`, `OLDEST`, `BEST_SELLING`. The response includes a `filters` block for faceted navigation (counts per filterable attribute value). |
ProductSearch | query | Full-text product search — `$query` is required. Functionally equivalent to `Products` with `$query` set, minus the `sortKey` argument (search defaults to relevance ranking). Use for the search results page; combine with `filters[]` for guided refinement. |
SearchSuggestions | query | Type-ahead suggestions for the storefront search input. Returns up to `$limit` matching products (hard cap 50) plus up to 5 styled query suggestions with `<mark>` tags around matched spans. Polish-language aware (handles morphology in suggestions). Run on each keystroke (debounce 200-300ms). The `$query` is capped at 100 characters server-side. |
Collections
| Operation | Kind | Description |
|---|---|---|
Collection | query | Fetches a single collection by `id` or `handle`, with paginated products. Collections come in two types: **MANUAL** (curated — products explicitly added by the merchant) and **AUTO** (rule-based — products matched dynamically). Both surfaces use the same field selection. |
Collections | query | Paginated list of all active collections (default 20, max 100). Sort by `TITLE` or `UPDATED_AT` via `sortKey`. Note: the `query` argument is reserved for future text filtering — it is currently accepted but ignored. |
Brands
| Operation | Kind | Description |
|---|---|---|
Brand | query | Fetches a single brand by `id` or `handle` (e.g. "funko" → /brands/funko) together with its landing-page fields (name, logo, description, SEO) and a paginated slice of its products. Use for brand pages. Returns null when the brand does not exist or is archived. Public — no auth required. |
Brands | query | Paginated list of active brands (brand index / navigation), default 20, max 100. Sort by `NAME` (default), `PRODUCT_COUNT`, `CREATED_AT` or `UPDATED_AT`; set `reverse: true` to descend. Optional `query` filters by brand name. Public — no auth required. |
Categories
| Operation | Kind | Description |
|---|---|---|
Category | query | Fetches a single category by `id` or `handle` with its parent, immediate children, and a paginated slice of its products. Use for category landing pages (hero + breadcrumbs + product grid in one round-trip) and sub-navigation. Nested `parent` / `children` are batched server-side — safe to use in lists without N+1 concerns. Public — no auth required. |
Categories | query | Returns active root categories for the shop. Each root exposes its `children` — build the tree client-side by walking those fields (server batches the lookups, no N+1). The hierarchy is not depth-capped server-side. Use for nav mega-menus and category pages. |
Cart
| Operation | Kind | Description |
|---|---|---|
Cart | query | Fetches a cart by `id` (the value persisted by the SDK in the `cart-id` cookie). The cart query is public — no auth needed to read it — but once a customer logs in and gets associated with the cart, mutations enforce ownership. Returns line items (paginated up to 100), totals, applied discount codes, gift cards, buyer identity, note, attributes, and warnings. Refetch after every cart mutation. |
Customer (requires auth)
| Operation | Kind | Description |
|---|---|---|
Customer | query | Full customer profile — basic info plus the first 10 addresses and first 10 orders. Heaviest customer query; for narrow use cases prefer `CustomerProfile` (no orders / addresses) or `CustomerOrder` (single order). Returns null if unauthenticated. |
CustomerProfile | query | Lightweight customer profile (no orders, no addresses list). Use for settings / profile pages that only need basic customer info — much cheaper than `Customer`. Returns null if unauthenticated. |
CustomerAddresses | query | Authenticated customer's saved address book — used on checkout to let the buyer pick a previously-used shipping / billing address instead of typing it. Each entry carries B2B invoicing fields (`taxId`, `vatNumber`) and the `isDefault` flag so the same list serves both as the shipping picker and as the billing/invoice picker. Returns up to 50 addresses (Relay Connection — buyers rarely keep more); for the unauthenticated case the connection is empty (no error). The default address is also surfaced as `Customer.defaultAddress`. |
CustomerOrder | query | Single order by `orderId`. Returns only orders that belong to the authenticated customer (cross-customer access returns null, not an error). Much cheaper than fetching the full `Customer` payload to access one order. Use on the order detail page. |
OrderByToken | query | Fetch a single order using its opaque access token (`Order.accessToken`) — designed for guest order summary pages where the buyer has not signed in. The token is returned in `cartComplete.order.accessToken` immediately after checkout completes; persist it in an HTTP-only cookie (preferred) or `sessionStorage` for the post-checkout page (NEVER `localStorage`). Optional `email` parameter adds defense-in-depth: when provided, it is matched case-insensitively against the order's buyer email; on mismatch the query returns `null` exactly like an invalid token (the response shape is identical, so an attacker cannot distinguish "token valid, wrong email" from "token invalid"). Rate-limited to 5 requests per minute per IP+shop combination to deter token enumeration; clients exceeding the limit receive a GraphQL error with `extensions.code: THROTTLED`. The response is marked `Cache-Control: no-store` so per-customer order data is never served from CDN or browser cache between users. Safe to retry; the token is permanent until the order is deleted. |
Discount Code Validation
| Operation | Kind | Description |
|---|---|---|
CartValidateDiscountCode | query | Read-only validation of a discount code against an existing cart — does NOT modify cart state. Returns `{ isValid, discount, error }` (`DiscountValidationResult`) for previewing the effect of a code (inline UI feedback as the user types, before they commit to applying via `cartDiscountCodesUpdate`). Validates: discount existence + active status + customer eligibility + minimum order amount + minimum quantity met. Errors: `NOT_FOUND`, `INACTIVE`, `NOT_STARTED`, `EXPIRED`, `USAGE_LIMIT_REACHED`, `CUSTOMER_USAGE_LIMIT_REACHED`, `CUSTOMER_NOT_ELIGIBLE`, `MINIMUM_ORDER_NOT_MET`, `MINIMUM_QUANTITY_NOT_MET`. |
Payment Methods
| Operation | Kind | Description |
|---|---|---|
AvailablePaymentMethods | query | Returns the active payment methods for the shop, sorted by the merchant-configured display position. Shop-level — does NOT vary by cart amount or currency. Each method exposes `type` (`CARD`, `BANK_TRANSFER`, `BLIK`, `PAYPAL`, `APPLE_PAY`, `GOOGLE_PAY`, `CASH_ON_DELIVERY`, `OTHER`), provider, icon, description, and supported currencies. Use to render the payment step of checkout. |
CartAvailablePaymentMethods | query | Cart-aware payment methods discovery. Returns the same active payment methods as the shop-level `availablePaymentMethods`, but with surcharge amounts resolved: a payment fee can be a percentage of the order value, so the exact amount (e.g. "+5 zł" or "+2.5% (7.18 zł)") is only computable against a cart. Prefer this once a cart exists (`cartCreate`); use the shop-level query for pre-cart previews (method grid, product page upsell) — there `fee` is always null, because the amount it would carry does not exist without a cart. Read `methods[].fee` for the whole-tile surcharge; when instruments carry different fees (e.g. per-brand card surcharges), read `methods[].instruments[].fee` per instrument instead. This query does NOT change cart state and is safe to retry; returns null when the cart does not exist. |
Shipments / Tracking
| Operation | Kind | Description |
|---|---|---|
Shipment | query | Fetches a shipment by `id` with status, tracking events, recipient address, and shipped/delivered timestamps. **Auth required** — customer access token plus ownership of the parent order. Wrapped response: `{ shipment, userErrors[] }`. Error codes: `INVALID_TOKEN`, `NOT_FOUND` (also returned on ownership mismatch to prevent enumeration), `FETCH_FAILED`. |
ShipmentByTrackingNumber | query | **Public** shipment lookup by carrier tracking number — no auth required. Designed for "Track my order" landing pages reachable without login. Returns the basic shipment fragment including recipient address. Wrapped response: `{ shipment, userErrors[] }`. Error codes: `INVALID_INPUT`, `NOT_FOUND`, `FETCH_FAILED`. |
Returns / RMA
| Operation | Kind | Description |
|---|---|---|
Return | query | Fetches a single return (RMA) by `id` with line items, refund/compensation info, and history. **Auth required** — customer access token plus ownership of the return. Wrapped response: `{ return, userErrors[] }`. Error codes: `INVALID_TOKEN`, `NOT_FOUND` (also returned on ownership mismatch), `FETCH_FAILED`. |
ReturnsByOrder | query | Lists returns for a given order (paginated, default page size 20, cursor-based). **Auth required** — customer access token plus ownership of the order; the connection is empty (no explicit error) on auth failure. Use on the order detail page to show return history. |
ReturnReasons | query | Returns the standard list of return reasons used by the RMA flow: `DEFECTIVE`, `NOT_AS_DESCRIBED`, `WRONG_ITEM`, `CHANGED_MIND`, `BETTER_PRICE`, `DAMAGED_SHIPPING`, `OTHER`. The list is fixed across all shops — not per-shop configurable. Public; no auth required. |
Gift Cards
| Operation | Kind | Description |
|---|---|---|
GiftCard | query | Public gift-card lookup by `code`. Returns balance, currency, expiry, and `maskedCode` (first 4 + last 4 chars only — the full code never leaks back). Returns null if the code is unknown (rather than an explicit error, to limit enumeration). **Rate-limited**: 10 requests per 60 seconds per IP. |
GiftCardValidate | query | Validates whether a gift card is usable (and optionally for a given `$amount`). Checks status (`DISABLED`, `USED`, `EXPIRED`), expiry date, and — when `$amount` is provided — sufficient balance. Returns `{ validation: { isValid, availableBalance, error: { code, message } }, userErrors[] }`. Validation error codes: `NOT_FOUND`, `DISABLED`, `ALREADY_USED`, `EXPIRED`, `INSUFFICIENT_BALANCE`. **Rate-limited**: 10 / 60s. |
Shipping Methods
| Operation | Kind | Description |
|---|---|---|
AvailableShippingMethods | query | Returns shipping methods for a given destination address and cart shape (subtotal, total weight, currency). The query computes everything from the inputs alone — no existing cart is required, so it can be used for "shipping cost preview" UIs (e.g. product detail page shipping calculator) before the customer adds anything to a cart. Each method includes price, free-shipping progress (`{ qualifies, currentAmount, threshold, remaining, progressPercent }`), estimated delivery, and carrier metadata. Sorted by the merchant's `sortOrder`, then by price. For a cart-bound checkout flow (where the cart is already known and the storefront wants the resolver to skip non-physical items and surface a `DIGITAL_ONLY_NO_SHIPPING` user error for all-digital carts), use `CartAvailableShippingMethods` against `cart.availableShippingMethods(address)` instead. |
CartAvailableShippingMethods | query | Cart-aware shipping methods discovery. Returns shipping methods available for the cart's contents at the given destination, with subtotal and physical-item weight pulled from the cart aggregate (no need to compute them client-side). When the cart contains only non-physical items (digital, gift card, service, subscription), the response is `methods: []` plus a `DIGITAL_ONLY_NO_SHIPPING` user error — use this as the signal to skip rendering the shipping picker step. Prefer this query over the standalone `AvailableShippingMethods` once a cart has been created (`cartCreate`). For pre-cart "shipping cost preview" UIs on product detail pages, the standalone query remains the right tool. |
Attribute Filters
| Operation | Kind | Description |
|---|---|---|
ProductFilters | query | Returns the dynamic facet filters available for a listing context — pass `collectionId`, `categoryId`, `searchQuery`, optional `available` (boolean for the availability facet), and optional `currentFilters` (array of attribute filters currently applied by the UI). For each visible & filterable attribute, returns either discrete value counts (for `SELECT` / `CHECKBOX` types) or numeric range bounds (for `SLIDER` types). Plus `priceRange`, `brands`, per-category counts, `activeCount` (length of `currentFilters`), `totalCount` (products in context — Relay-aligned), and `availableCount` (boolean facet count for `availableForSale`). All per-facet counts use exclude-self aggregation: a facet's count IGNORES its own currently-applied filter and APPLIES other filters — so the count reflects "products if this facet were toggled" rather than "products currently visible". Untracked inventory (gift cards, digital, made-to-order) is always counted as available. Use to render filter sidebars on listing/search pages. |
Loyalty Program
| Operation | Kind | Description |
|---|---|---|
LoyaltyMember | query | Returns the logged-in customer's loyalty membership: points (current, pending, redeemed, expired, expiring), current tier, tier progress, annual spend, last activity. Returns null if the customer is not enrolled — there is **no auto-enrollment** here (enrollment happens via signup or a first qualifying order). Auth required. |
LoyaltyTiers | query | Lists the loyalty tiers configured for the shop (`BRONZE`, `SILVER`, `GOLD`, `PLATINUM`, `DIAMOND` etc.) with their `minPoints`, `minAnnualSpend`, `pointsMultiplier`, and custom benefits. Sorted by `minPoints` ASC. Public; no auth required. |
LoyaltyRewards | query | Lists rewards customers can redeem (free shipping, percent off, free product, gift card). Filtered to **active** rewards only (`is_active = true` AND inside their `starts_at`/`ends_at` window). Public; no auth required. |
LoyaltyTransactions | query | Paginated history of loyalty point transactions for the logged-in customer (default 20). Transaction `type` enum: `EARN_PURCHASE`, `EARN_SIGNUP`, `EARN_REFERRAL`, `EARN_REVIEW`, `EARN_BIRTHDAY`, `EARN_BONUS`, `REDEEM`, `EXPIRE`, `ADJUST`, `REFUND_REVERSAL`. Auth required — empty connection if unauthenticated. |
LoyaltySettings | query | Returns the loyalty program configuration: `isEnabled`, `pointsName` (e.g. "stars"), `pointsPerCurrency`, `pointsExpiryMonths`, available earn actions, referral settings. Use this at app boot to decide whether to render any loyalty UI at all. Public; no auth required. |
NewsletterIncentive | query | Returns the shop's newsletter signup incentive — when `isEnabled` is true, render the signup banner with the returned discount shape (e.g. "subscribe & get -10%"); a confirmed signup earns a personal, single-use code delivered by email (it works only for the address it was issued to). When false, hide the incentive messaging — plain newsletter signup still works. Public; no auth required. |
EstimatePoints | query | Estimates how many loyalty points the customer would earn for an order of `$orderTotal` (in major currency units). When the customer is authenticated, the result accounts for their current tier's points multiplier. Use on cart/checkout to show "Earn X points with this order". |
ReferralStats | query | Returns the customer's referral statistics: `referralCode`, `shareUrl`, `totalReferred`, `completedReferrals`, `pendingReferrals`, `totalPointsEarned`. Auth required. Returns null if unauthenticated or if the referral program is disabled for the shop. |
Reviews
| Operation | Kind | Description |
|---|---|---|
ProductReviews | query | Paginated list of customer reviews for a product, **filtered to APPROVED reviews only** (PENDING / REJECTED reviews are not exposed to the storefront). Sort by `CREATED_AT` (default), helpfulness, or rating. Public; no auth required. |
ReviewStats | query | Aggregate review statistics for a product: average rating, total count, distribution per star (1-5). Computed from APPROVED reviews only. Use for product card review summaries. Public; no auth required. |
Wishlists
| Operation | Kind | Description |
|---|---|---|
Wishlists | query | Paginated list of the logged-in customer's wishlists (default 20). Auth required — empty connection if unauthenticated. Customers typically have a small set (<10). |
WishlistById | query | Fetches a single wishlist by `id`. Private wishlists are visible only to the owner; public wishlists are visible to anyone. Note: this query supports lookup by `id` only — there is currently no way to fetch a wishlist by its share token. |
Blog
| Operation | Kind | Description |
|---|---|---|
BlogPosts | query | Paginated list of published blog posts. Filter by `categoryHandle`, `tagHandle`, or `featured` (boolean flag, not enum). Sort: `PUBLISHED_AT` (default), `TITLE`, `VIEW_COUNT`, or `CREATED_AT`. Public; no auth required. |
BlogPost | query | Fetches a single blog post by `id` or `handle`. Visibility-gated: returns null if the post is not yet `PUBLISHED` or if its publish date is in the future (scheduled posts stay hidden until their publish time). Side effect: fetching a post increments its `view_count` asynchronously (does not block the response). |
BlogCategories | query | Lists all blog categories with per-category `postCount` and SEO metadata. Use to render category navigation on blog pages. Public; no auth required. |
BlogTags | query | Lists blog tags with usage counts (`postCount` per tag). Use to render a tag cloud. Public; no auth required. |
BlogCategory | query | Fetches a single blog category by `id` or `handle`, including its image and SEO metadata. Use on a category landing page (the paginated post list for the category comes from `BlogPosts` with `categoryHandle`). Public; no auth required. |
BlogTag | query | Fetches a single blog tag by `id` or `handle`, including its SEO metadata (null when the merchant has not set any). Use on a tag landing page (the paginated post list for the tag comes from `BlogPosts` with `tagHandle`). Public; no auth required. |
BlogPostProducts | query | Products the merchant pinned to a blog post — directly, via product categories, or via collections. Always a connection: empty when nothing is pinned; only products visible on the storefront are included. Use to render a product strip inside or below the post. Public; no auth required. |
BlogCategoryProducts | query | Products the merchant pinned to a blog category — directly, via product categories, or via collections. Always a connection: empty when nothing is pinned; only products visible on the storefront are included. Use to render a product strip on the category landing page. Public; no auth required. |
BlogTagProducts | query | Products the merchant pinned to a blog tag — directly, via product categories, or via collections. Always a connection: empty when nothing is pinned; only products visible on the storefront are included. Use to render a product strip on the tag landing page. Public; no auth required. |
Recommendations
| Operation | Kind | Description |
|---|---|---|
ProductRecommendations | query | Returns up to `$limit` recommended products related to `$productId`. Default `$intent: SIMILAR` — products sharing categories or tags. Use on PDP "You may also like" sections. Public; no auth required. |
Content: Pages
| Operation | Kind | Description |
|---|---|---|
Page | query | Fetches a single CMS page (About, Privacy, Returns Policy, Terms, etc.) by `handle` or `id`. Visibility-gated: returns null if the page is hidden or if its publish date is in the future. Public; no auth required. |
Pages | query | Paginated list of visible, already-published CMS pages. Use for sitemap, footer link list, or page directory. The `query` argument supports text search over the page title/handle. Public; no auth required. |
Content: Navigation Menus
| Operation | Kind | Description |
|---|---|---|
Menu | query | Fetches a navigation menu by `handle` (e.g. `"main-menu"`, `"footer"`, `"mobile"`). Returns the nested item tree (up to 3 levels). Each item is typed as one of: `HTTP`, `FRONTPAGE`, `SEARCH`, `CATALOG`, `BLOG`, `PRODUCT`, `COLLECTION`, `CATEGORY`, `PAGE`, or `BRAND` — switch on the type to render the right link target. Each resource-linked item exposes both a pre-resolved `url` (standard `/categories|/collections|/pages|/products|/brands/<handle>` convention) and a typed `resource` union with the raw handle so storefronts with custom routing can construct their own paths instead. All resource lookups are batched per request — no N+1 even for deep menus. |
Content: URL Redirects
| Operation | Kind | Description |
|---|---|---|
UrlRedirects | query | Returns the shop's URL redirects (legacy `path` → new `target` mappings). Use server-side at the edge or in SSR to issue 301 redirects for migrated routes (preserves SEO equity). Default page size 250 — most shops fit in a single page. |
Store Availability: per-location stock (BOPIS / multi-location)
| Operation | Kind | Description |
|---|---|---|
ProductStoreAvailability | query | Fetches a product (by `handle` or `id`) along with per-variant availability across the merchant's physical locations — for the BOPIS / multi-location flow. The `storeAvailability` connection lives on each `ProductVariant`; its arguments (`first`, `after`, `near`, `locationType`) are set inside the `VariantStoreAvailability` fragment. The connection returns null for single-location shops (in which case the storefront can skip the store picker entirely). `availableStock` is null for anonymous users and an integer for authenticated customers. Apply `@inContext(preferredLocationId: ...)` on the operation to pin the customer's preferred location to the top of the result. |
Locations (store picker UI)
| Operation | Kind | Description |
|---|---|---|
Locations | query | Paginated list of active store locations (default 20, max 100). Filters: `near` (`{ latitude, longitude }`) for proximity search — sorts ascending by distance; `hasPickupEnabled` for pickup-only filtering; `locationType` (`RETAIL`, `WAREHOUSE`, `PICKUP_POINT`). When `near` is omitted, results are sorted by the merchant's `priority`, then name. Use for the BOPIS store picker UI. Public; no auth required. |
Location | query | Fetches a single store location by `id` — full address, coordinates, business hours, pickup config (lead time, hours, timezone), and services. Returns null if the location is not found, not active, or owned by another shop. Use on the location detail page. Public; no auth required. |
Treści cyfrowe
| Operation | Kind | Description |
|---|---|---|
ProductAttachments | query | Publiczne pliki dołączone do produktu — instrukcje, certyfikaty, karty gwarancyjne. Osobne zapytanie, a nie pole w podstawowym fragmencie produktu: załączniki wymagają dodatkowego odczytu po stronie serwera, więc lista produktów nie powinna za nie płacić. Pobierz je dopiero na karcie produktu. |
OrderDigitalDownloads | query | Treści cyfrowe kupione w zamówieniu, po opaque tokenie zamówienia (strona po-zakupowa gościa). Osobne zapytanie z tego samego powodu co wyżej — lista zamówień nie musi rozwiązywać pobrań dla każdej pozycji. |
Forms engine (store-defined forms)
| Operation | Kind | Description |
|---|---|---|
RegistrationForm | query | Registration form configuration of the store. Tells you whether new accounts need manual approval (`requireApproval` — `customerSignup` then returns accountStatus PENDING_APPROVAL and no token), which built-in sections to render (company / address / phone, each OFF / OPTIONAL / REQUIRED) and the store-defined custom fields. Collect custom-field values and pass them to `customerSignup.input.customFields`. All labels are resolved to the request language. |
Form | query | A store-defined contact form by slug. Render `fields` and send the collected values via `formSubmit` with the same slug. Returns null when no active form exists under the slug — treat it as a 404 page state. |
VariantPrices | query | Batch variant prices for the signed-in customer — the authenticated price channel. Use when the store restricts catalog prices to signed-in customers: `price` / `priceRange` / conversion fields come back null in the public catalog then, and price filtering / price sorting return the PRICE_FILTER_RESTRICTED error. Requires authentication on a price-restricted store (error code PRICES_REQUIRE_LOGIN otherwise); on an unrestricted store the query works for everyone. Max 100 ids per call; the response is never shared-cached. |
ConfiguratorOptionPrices | query | Configurator surcharges for the signed-in customer — the authenticated price channel for product configurator choices. Use when the store restricts catalog prices to signed-in customers: `ConfiguratorOption.surchargeAmount` / `surchargeType` come back null in the public catalog then. Options mapped to a stocked variant are priced via `VariantPrices` (they are real variants). Requires authentication on a price-restricted store (error code PRICES_REQUIRE_LOGIN otherwise); on an unrestricted store the query works for everyone. The response is never shared-cached. |
Sklep
Konfiguracja sklepu (waluta, języki, branding, godziny otwarcia, dane kontaktowe, metody płatności, kraje wysyłki). Pobieraj raz na sesję i cache'uj — niemal wszystkie pozostałe zapytania są kontekstualizowane przez sklep zwrócony tutaj. SDK <StorefrontProvider> używa wewnętrznie ShopConfig do bootstrapu store'ów (currency, language, bot protection).
ShopqueryShopquery Shop
Returns shop configuration: name, base + supported currencies, supported locales, branding (logo, colors, fonts, social links), contact info, active payment methods, brand metadata, money format template, and the list of countries the shop ships to. Public; no auth required. Call once per session and cache — almost everything else is contextualized by the shop returned here.
GraphQL operation
query Shop {
shop {
...Shop
}
}
ShopshipsToCountries to kraje ze stref wysyłki sklepu — lista, z której budujesz wybór kraju dostawy. Kraje objęte sankcjami są z niej zawsze pominięte; szczegóły w Kody krajów.
Dla minimalnego payload pod provider — patrz ShopConfig. Refetchuj gdy merchant zmienia ustawienia waluty / języka albo gdy chcesz podchwycić nowe reguły bot-protection.
ShopConfigqueryShopquery ShopConfig
Minimal Shop payload for `<StorefrontProvider shopData={...}>` from `@doswiftly/storefront-sdk/react`. Returns exactly the fields the SDK's `ShopConfig` interface declares — currency setup (with `localeToCurrencyMap` for browser-locale-based currency detection), language setup, and bot protection. Cache for the session; refetch when the merchant updates currency / language settings or you want to pick up new bot-protection rules.
GraphQL operation
query ShopConfig {
shop {
...ShopConfigFields
}
}
ShopConfigFieldsProdukty
Pojedynczy produkt, paginowana lista, wyszukiwarka, autocomplete, konfigurator i filtry to fundament każdej witryny katalogowej. DoSwiftly zwraca produkt tylko jeśli jest storefront-accessible (status ACTIVE z visibility PUBLIC lub BUNDLE_ONLY) — produkty ukryte / draft nigdy nie wyciekają.
Pojedynczy produkt
Pobierz produkt po id LUB handle (URL-friendly slug). Wystarczy jeden z parametrów — jeśli oba podane, wygrywa ten przekazany.
ProductqueryProductsquery Product($id: ID, $handle: String)
Fetches a single product by `id` or `handle` (URL-friendly identifier). Pass either — whichever is provided wins; if both are missing, returns null. Returns null if the product is not storefront-accessible (must be `ACTIVE` status with `PUBLIC` or `BUNDLE_ONLY` visibility).
GraphQL operation
query Product($id: ID, $handle: String) {
product(id: $id, handle: $handle) {
...ProductFull
}
}
ProductFullKonfigurator produktu
Wariant Product zoptymalizowany pod UI konfiguratora — w jednym round-tripie pobiera produkt i jego pola konfiguratora przez configuratorFields (filledBy: CUSTOMER zwraca tylko pola edytowalne przez kupującego). Dzięki temu nie trzeba osobnego zapytania o konfigurator. Kompletny ekran krok po kroku: przepis Konfigurator produktu.
ProductConfiguratorqueryProductsquery ProductConfigurator($handle: String!, $filledBy: AttributeFillingMode = CUSTOMER)
Fetches a product together with its configurator fields, optimized for the product-page configurator UI. filledBy CUSTOMER returns only the fields a shopper edits; pass BOTH to also include seller-prefilled fields. Single round-trip.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$handle | String! | — | Yes |
$filledBy | AttributeFillingMode | CUSTOMER | No |
GraphQL operation
query ProductConfigurator($handle: String!, $filledBy: AttributeFillingMode = CUSTOMER) {
product(handle: $handle) {
...ProductFull
configuratorFields(filter: {filledBy: $filledBy}) {
...ConfiguratorField
}
}
}
Gdy sklep ogranicza widoczność cen (ceny po zalogowaniu), surchargeAmount w polach konfiguratora przychodzi jako null — tym zapytaniem dociągniesz dopłaty opcji po uwierzytelnieniu, bez ponownego pobierania całego konfiguratora (lustro VariantPrices dla wariantów; opcje z komponentem magazynowym wyceniasz właśnie przez VariantPrices — to realne warianty):
ConfiguratorOptionPricesqueryForms engine (store-defined forms)query ConfiguratorOptionPrices($productId: ID!)
Configurator surcharges for the signed-in customer — the authenticated price channel for product configurator choices. Use when the store restricts catalog prices to signed-in customers: `ConfiguratorOption.surchargeAmount` / `surchargeType` come back null in the public catalog then. Options mapped to a stocked variant are priced via `VariantPrices` (they are real variants). Requires authentication on a price-restricted store (error code PRICES_REQUIRE_LOGIN otherwise); on an unrestricted store the query works for everyone. The response is never shared-cached.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$productId | ID! | — | Yes |
GraphQL operation
query ConfiguratorOptionPrices($productId: ID!) {
configuratorOptionPrices(productId: $productId) {
optionId
surchargeAmount
surchargeType
}
}
Paginowana lista produktów
Relay Connection (domyślnie 20, max 100). Obsługuje:
- Strukturalne zapytanie
query: "tag:summer AND variants.price:>10"(parser + free-text fallback potitle/content) - Multi-filter logic: ten sam field name w
filters[]wiele razy → OR; różne fieldy → AND - Sort keys:
RELEVANCE(domyślny),TITLE,PRICE,CREATED_AT,UPDATED_AT;reverse: trueodwraca kolejność.RELEVANCEporządkuje po trafności względemquery— bezquerynie ma czego rankować, więc lista wraca do najnowszych. Pozostałe klucze działają razem zquery: wyszukiwanie zawęża zbiór, a wybrany klucz go porządkuje.BEST_SELLING,ID,PRODUCT_TYPEiVENDORsą zarezerwowane na przyszłość i dziś zachowują się jakCREATED_AT. - Faceted navigation: response zawiera blok
filtersz licznikami per filterable attribute value
ProductsqueryProductsquery Products($first: Int = 20, $after: String, $query: String, $sortKey: ProductSortKeys = RELEVANCE, $reverse: Boolean = false, $filters: [ProductFilter!])
Paginated product list (Relay Connection, default page size 20, max 100). The `query` argument supports a structured search syntax — `tag:summer`, `vendor:foo`, `product_type:shirts`, `variants.price:>10`, plus `AND`/`OR`/`NOT` — falling back to free-text title/content search. The `filters[]` array uses multi-filter logic: same field name appears multiple times → OR; different fields → AND. Sort: `RELEVANCE`, `TITLE`, `PRICE`, `NEWEST`, `OLDEST`, `BEST_SELLING`. The response includes a `filters` block for faceted navigation (counts per filterable attribute value).
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$first | Int | 20 | No |
$after | String | — | No |
$query | String | — | No |
$sortKey | ProductSortKeys | RELEVANCE | No |
$reverse | Boolean | false | No |
$filters | [ProductFilter!] | — | No |
GraphQL operation
query Products($first: Int = 20, $after: String, $query: String, $sortKey: ProductSortKeys = RELEVANCE, $reverse: Boolean = false, $filters: [ProductFilter!]) {
products(
first: $first
after: $after
query: $query
sortKey: $sortKey
reverse: $reverse
filters: $filters
) {
edges {
node {
...ProductCard
}
cursor
}
nodes {
...ProductCard
}
pageInfo {
...PageInfo
}
totalCount
}
}
Wyszukiwanie pełnotekstowe
Wariant Products zoptymalizowany pod search UI — wymaga $query jako pierwszej zmiennej, reszta args zachowuje semantykę.
ProductSearchqueryProductsquery ProductSearch($query: String!, $first: Int = 20, $after: String, $filters: [ProductFilter!])
Full-text product search — `$query` is required. Functionally equivalent to `Products` with `$query` set, minus the `sortKey` argument (search defaults to relevance ranking). Use for the search results page; combine with `filters[]` for guided refinement.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$query | String! | — | Yes |
$first | Int | 20 | No |
$after | String | — | No |
$filters | [ProductFilter!] | — | No |
GraphQL operation
query ProductSearch($query: String!, $first: Int = 20, $after: String, $filters: [ProductFilter!]) {
products(query: $query, first: $first, after: $after, filters: $filters) {
edges {
node {
...ProductCard
}
cursor
}
nodes {
...ProductCard
}
pageInfo {
...PageInfo
}
totalCount
}
}
Sugestie wyszukiwania (autocomplete)
Lekkie zapytanie pod search-as-you-type. Zwraca { products, queries, collections, categories } z dopasowaniami top-N — idealne do live autocomplete pod inputem szukajki.
SearchSuggestionsqueryProductsquery SearchSuggestions($query: String!, $limit: Int = 10)
Type-ahead suggestions for the storefront search input. Returns up to `$limit` matching products (hard cap 50) plus up to 5 styled query suggestions with `<mark>` tags around matched spans. Polish-language aware (handles morphology in suggestions). Run on each keystroke (debounce 200-300ms). The `$query` is capped at 100 characters server-side.
GraphQL operation
query SearchSuggestions($query: String!, $limit: Int = 10) {
searchSuggestions(query: $query, limit: $limit) {
products {
...ProductCard
}
queries {
text
styledText
}
}
}
ProductCardFiltry produktowe
Zwraca dostępne filtry dla bieżącego kontekstu (kolekcja, kategoria, search query): atrybuty filtrowalne z licznikami, zakres cenowy, kategorie, marki. Renderuje faceted sidebar — typowo wywoływane razem z Products (ten sam input).
ProductFiltersqueryAttribute Filtersquery ProductFilters($input: AvailableFiltersInput)
Returns the dynamic facet filters available for a listing context — pass `collectionId`, `categoryId`, `searchQuery`, optional `available` (boolean for the availability facet), and optional `currentFilters` (array of attribute filters currently applied by the UI). For each visible & filterable attribute, returns either discrete value counts (for `SELECT` / `CHECKBOX` types) or numeric range bounds (for `SLIDER` types). Plus `priceRange`, `brands`, per-category counts, `activeCount` (length of `currentFilters`), `totalCount` (products in context — Relay-aligned), and `availableCount` (boolean facet count for `availableForSale`). All per-facet counts use exclude-self aggregation: a facet's count IGNORES its own currently-applied filter and APPLIES other filters — so the count reflects "products if this facet were toggled" rather than "products currently visible". Untracked inventory (gift cards, digital, made-to-order) is always counted as available. Use to render filter sidebars on listing/search pages.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$input | AvailableFiltersInput | — | No |
GraphQL operation
query ProductFilters($input: AvailableFiltersInput) {
productFilters(input: $input) {
...AvailableFilters
}
}
AvailableFiltersfilterValues jest dostępne także dla typów TEXT i TEXTAREA (nie tylko SELECT). Licznik productCount per unikalna wartość tekstowa używa indeksu GIN pg_trgm — możesz budować facet dla pola Producent / Materiał / Licencja bez konieczności konwersji atrybutu na SELECT.
Drill-into pojedynczego filtra
Drugi profil obok productFilters — productFilters zwraca top-N facet'ów do quick browse w sidebar, attributeOptionsSearch służy do drill-into konkretnego filtra z autocomplete (np. „Pokaż wszystkie 832 marki" + search input). Honoruje kontekst kategorii / kolekcji / search query / currentFilters z exclude-self semantyką (gdy currentFilters zawiera ten sam atrybut, jest pomijany przy obliczaniu productCount).
attributeOptionsSearch istnieje w schema, ale nie jest pre-built operation w @doswiftly/storefront-operations — odpalaj jako ad-hoc query po stronie storefrontu:
query AttributeOptionsSearch($input: AttributeOptionsSearchInput!) {
attributeOptionsSearch(input: $input) {
edges {
cursor
node { id value label productCount swatch { colorHex image { url altText } } }
}
pageInfo { hasNextPage hasPreviousPage startCursor endCursor }
totalCount
}
}
Pełny zestaw typów filtrów i ich argumentów znajdziesz w referencji typów.
Ceny wariantów dla zalogowanych
Batchowe pobranie cen wariantów dla zalogowanego klienta — kanał cen po uwierzytelnieniu. Używaj, gdy sklep ogranicza ceny katalogu do zalogowanych klientów: pola price / priceRange i przeliczenia walutowe wracają wtedy w publicznym katalogu jako null, a filtrowanie i sortowanie po cenie kończy się błędem PRICE_FILTER_RESTRICTED. Na sklepie z ograniczeniem wymaga sesji klienta (bez niej kod błędu PRICES_REQUIRE_LOGIN); na sklepie bez ograniczenia działa dla każdego. Maksymalnie 100 identyfikatorów na wywołanie; odpowiedź nigdy nie trafia do współdzielonego cache.
VariantPricesqueryForms engine (store-defined forms)query VariantPrices($variantIds: [ID!]!)
Batch variant prices for the signed-in customer — the authenticated price channel. Use when the store restricts catalog prices to signed-in customers: `price` / `priceRange` / conversion fields come back null in the public catalog then, and price filtering / price sorting return the PRICE_FILTER_RESTRICTED error. Requires authentication on a price-restricted store (error code PRICES_REQUIRE_LOGIN otherwise); on an unrestricted store the query works for everyone. Max 100 ids per call; the response is never shared-cached.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$variantIds | [ID!]! | — | Yes |
GraphQL operation
query VariantPrices($variantIds: [ID!]!) {
variantPrices(variantIds: $variantIds) {
variantId
price {
amount
currencyCode
}
compareAtPrice {
amount
currencyCode
}
}
}
Treść rich-text — argument format
Pola tekstowe redagowane w panelu (opisy produktów, kategorii, kolekcji, marek oraz treść wpisów blogowych i stron CMS) mają kanoniczne, strukturalne źródło. Storefront API renderuje je na żądanie do wybranej reprezentacji przez opcjonalny argument format: RichTextFormat:
| Wartość | Co zwraca | Kiedy używać |
|---|---|---|
HTML (domyślna) | Gotowy do renderu markup (bezpieczny podzbiór tagów) | Wstawienie do strony przez dangerouslySetInnerHTML (owiń w sanitizeHtml z SDK) |
TEXT | Czysty tekst bez tagów | <meta name="description">, podglądy, snippety, indeks wyszukiwarki |
JSON | Strukturalny dokument (string do sparsowania) | Własny renderer (headless, aplikacja mobilna) |
Argument jest opcjonalny — pominięcie zwraca HTML, więc istniejące zapytania działają bez zmian. Każda reprezentacja jest bezpieczna do renderu (sanityzacja XSS po stronie API).
Pola z argumentem format: Product.description, Category.description, Collection.description, Brand.description, BlogPost.content, ShopPage.body.
query ProductCopy($handle: String!) {
product(handle: $handle) {
description # HTML (domyślnie)
plain: description(format: TEXT) # czysty tekst — podglądy/snippety (do SEO użyj pól seo)
doc: description(format: JSON) # strukturalny dokument dla własnego renderera
}
}
Możesz zażądać kilku reprezentacji jednego pola w jednym zapytaniu, nadając im aliasy (plain, doc powyżej).
Do meta tagów ta konwersja nie jest potrzebna. seo jest gotowe do renderu: seo.title zwraca meta tytuł merchanta, a gdy pole zostało puste — nazwę zasobu; seo.description analogicznie meta opis, a w jego braku opis zasobu jako czysty tekst, przycięty na granicy słowa do długości pokazywanej w wynikach (strona CMS i wpis blogowy biorą najpierw zajawkę, dopiero potem treść). Renderujesz oba wprost, bez własnego fallbacku i bez format: TEXT.
Zwracamy ELEMENT, nie kompozycję: seo.title to sama nazwa zasobu, więc wzorzec ${seo.title} — ${shop.name}, separatory i numer strony zostają po Twojej stronie. Tag blogowy nie ma opisu, więc dostaje sam tytuł.
Zastąp starsze pola równoważnym format:
| Wycofane pole | Zamiennik |
|---|---|
Product.descriptionHtml | description(format: HTML) |
Collection.descriptionHtml | description(format: HTML) |
BlogPost.contentFormat | format wybierany per-request przez content(format: ...) |
Pola nadal działają w okresie karencji, ale są oznaczone @deprecated — migruj przed datą usunięcia.
Kolekcje i kategorie
Kolekcje to kuratorskie grupy produktów (np. „Nowości", „Promocje", „Bestsellery"). Kategorie to hierarchiczne drzewo katalogu (np. „Buty → Sportowe → Trail running"). Oba mają handle (URL slug), a zarówno kolekcja, jak i kategoria dodatkowo wystawiają paginowaną listę swoich produktów inline.
Pojedyncza kolekcja z produktami
CollectionqueryCollectionsquery Collection($id: ID, $handle: String, $productsFirst: Int = 20, $productsAfter: String, $productsFilters: [ProductFilter!])
Fetches a single collection by `id` or `handle`, with paginated products. Collections come in two types: **MANUAL** (curated — products explicitly added by the merchant) and **AUTO** (rule-based — products matched dynamically). Both surfaces use the same field selection.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$id | ID | — | No |
$handle | String | — | No |
$productsFirst | Int | 20 | No |
$productsAfter | String | — | No |
$productsFilters | [ProductFilter!] | — | No |
GraphQL operation
query Collection($id: ID, $handle: String, $productsFirst: Int = 20, $productsAfter: String, $productsFilters: [ProductFilter!]) {
collection(id: $id, handle: $handle) {
...Collection
products(
first: $productsFirst
after: $productsAfter
filters: $productsFilters
) {
edges {
node {
...ProductCard
}
cursor
}
nodes {
...ProductCard
}
pageInfo {
...PageInfo
}
totalCount
}
}
}
Lista kolekcji
CollectionsqueryCollectionsquery Collections($first: Int = 20, $after: String, $query: String, $sortKey: CollectionSortKeys = TITLE, $reverse: Boolean = false)
Paginated list of all active collections (default 20, max 100). Sort by `TITLE` or `UPDATED_AT` via `sortKey`. Note: the `query` argument is reserved for future text filtering — it is currently accepted but ignored.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$first | Int | 20 | No |
$after | String | — | No |
$query | String | — | No |
$sortKey | CollectionSortKeys | TITLE | No |
$reverse | Boolean | false | No |
GraphQL operation
query Collections($first: Int = 20, $after: String, $query: String, $sortKey: CollectionSortKeys = TITLE, $reverse: Boolean = false) {
collections(
first: $first
after: $after
query: $query
sortKey: $sortKey
reverse: $reverse
) {
edges {
node {
...Collection
}
cursor
}
pageInfo {
...PageInfo
}
totalCount
}
}
Pojedyncza kategoria
Zwraca kategorię z hierarchią — parent oraz lista children (rekurencyjny fragment buduje drzewo client-side). Pole products to paginowana lista produktów kategorii (Relay Connection) — sterujesz nią przez $productsSortKey / $productsFilters w jednym round-tripie, bez osobnego zapytania o produkty (analogicznie do kolekcji i marki). Jeśli zawężasz istniejącą listę produktów (np. ekran kategorii z facetami), użyj filtra products(filters: [{ category: { handle } }]) — handle jest stabilny w adresie URL, więc nie musisz wcześniej rozwiązywać go na id.
CategoryqueryCategoriesquery Category($id: ID, $handle: String, $productsFirst: Int = 20, $productsAfter: String, $productsSortKey: ProductSortKeys = BEST_SELLING, $productsFilters: [ProductFilter!])
Fetches a single category by `id` or `handle` with its parent, immediate children, and a paginated slice of its products. Use for category landing pages (hero + breadcrumbs + product grid in one round-trip) and sub-navigation. Nested `parent` / `children` are batched server-side — safe to use in lists without N+1 concerns. Public — no auth required.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$id | ID | — | No |
$handle | String | — | No |
$productsFirst | Int | 20 | No |
$productsAfter | String | — | No |
$productsSortKey | ProductSortKeys | BEST_SELLING | No |
$productsFilters | [ProductFilter!] | — | No |
GraphQL operation
query Category($id: ID, $handle: String, $productsFirst: Int = 20, $productsAfter: String, $productsSortKey: ProductSortKeys = BEST_SELLING, $productsFilters: [ProductFilter!]) {
category(id: $id, handle: $handle) {
...Category
parent {
...Category
}
children {
...Category
}
products(
first: $productsFirst
after: $productsAfter
sortKey: $productsSortKey
filters: $productsFilters
) {
edges {
node {
...ProductCard
}
cursor
}
nodes {
...ProductCard
}
pageInfo {
...PageInfo
}
totalCount
}
}
}
Drzewo kategorii
Zwraca kategorie główne sklepu w jednym round-tripie z 2-3 poziomami zagnieżdżenia. Serwer batchuje odczyty (DataLoader) — brak N+1 niezależnie od głębokości drzewa.
CategoriesqueryCategoriesquery Categories
Returns active root categories for the shop. Each root exposes its `children` — build the tree client-side by walking those fields (server batches the lookups, no N+1). The hierarchy is not depth-capped server-side. Use for nav mega-menus and category pages.
GraphQL operation
query Categories {
categories(rootsOnly: true) {
nodes {
...Category
children {
...Category
children {
...Category
}
}
}
totalCount
}
}
CategoryMarki
Marka (producent / etykieta) to encja katalogu z własną stroną docelową — nazwą, logo, opisem (HTML) i metadanymi SEO. Marka ma handle (URL slug stabilny per sklep, np. ruta /brands/[handle]), a zapytanie brand dodatkowo wystawia paginowaną listę jej produktów inline — analogicznie do kolekcji.
Nie myl trzech różnych powierzchni „marki" w API — każda służy do czego innego:
| Powierzchnia | Co to jest | Kiedy używać |
|---|---|---|
Encja Brand (ta sekcja) | Strona marki pobierana po handle — nazwa, logo, opis, SEO + produkty marki | Ruta /brands/[handle], indeks marek, kafelek marki |
Filtr ProductFilter.brand | Zawężenie listy produktów do marki (po handle lub id) | Zawężanie wyników w products / Brand.products |
Facet productFilters.brands | Lista marek z licznikami produktów (BrandFilterValue) w bieżącym kontekście | Sidebar nawigacji facetowej (checkboxy marek) |
Krótko: encja = strona marki, filtr = zawężenie listy, facet = lista marek do wyboru w sidebarze. Facet i filtr opisuje SDK — Produkty.
Pojedyncza marka z produktami
Pobierz markę po id LUB handle (wystarczy jeden — jeśli oba podane, wygrywa id). Zwraca null, gdy marka nie istnieje lub została zarchiwizowana. Pole products to paginowana lista produktów marki (Relay Connection) — sterujesz nią przez $productsSortKey / $productsFilters w jednym round-tripie, bez osobnego zapytania o produkty.
BrandqueryBrandsquery Brand($id: ID, $handle: String, $productsFirst: Int = 20, $productsAfter: String, $productsSortKey: ProductSortKeys = BEST_SELLING, $productsFilters: [ProductFilter!])
Fetches a single brand by `id` or `handle` (e.g. "funko" → /brands/funko) together with its landing-page fields (name, logo, description, SEO) and a paginated slice of its products. Use for brand pages. Returns null when the brand does not exist or is archived. Public — no auth required.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$id | ID | — | No |
$handle | String | — | No |
$productsFirst | Int | 20 | No |
$productsAfter | String | — | No |
$productsSortKey | ProductSortKeys | BEST_SELLING | No |
$productsFilters | [ProductFilter!] | — | No |
GraphQL operation
query Brand($id: ID, $handle: String, $productsFirst: Int = 20, $productsAfter: String, $productsSortKey: ProductSortKeys = BEST_SELLING, $productsFilters: [ProductFilter!]) {
brand(id: $id, handle: $handle) {
...Brand
products(
first: $productsFirst
after: $productsAfter
sortKey: $productsSortKey
filters: $productsFilters
) {
edges {
node {
...ProductCard
}
cursor
}
nodes {
...ProductCard
}
pageInfo {
...PageInfo
}
totalCount
}
}
}
Lista marek
Paginowana lista aktywnych marek (indeks marek / nawigacja). Sortowanie przez sortKey: NAME (domyślny), PRODUCT_COUNT, CREATED_AT, UPDATED_AT; reverse: true odwraca kolejność. Opcjonalny query filtruje po nazwie marki.
BrandsqueryBrandsquery Brands($first: Int = 20, $after: String, $query: String, $sortKey: BrandSortKeys = NAME, $reverse: Boolean = false)
Paginated list of active brands (brand index / navigation), default 20, max 100. Sort by `NAME` (default), `PRODUCT_COUNT`, `CREATED_AT` or `UPDATED_AT`; set `reverse: true` to descend. Optional `query` filters by brand name. Public — no auth required.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$first | Int | 20 | No |
$after | String | — | No |
$query | String | — | No |
$sortKey | BrandSortKeys | NAME | No |
$reverse | Boolean | false | No |
GraphQL operation
query Brands($first: Int = 20, $after: String, $query: String, $sortKey: BrandSortKeys = NAME, $reverse: Boolean = false) {
brands(
first: $first
after: $after
query: $query
sortKey: $sortKey
reverse: $reverse
) {
edges {
node {
...Brand
}
cursor
}
pageInfo {
...PageInfo
}
totalCount
}
}
Koszyk
Koszyk to jedyny aggregate realizacji zamówienia — adres dostawy, metoda wysyłki, płatność, kody rabatowe, karty podarunkowe i finalizacja są wystawione bezpośrednio na obiekcie Cart. Nie ma osobnego obiektu Checkout. Mutacje koszyka opisane są na stronie Mutacje.
Pobranie koszyka
Cart jest re-fetchowany przy każdej zmianie (po mutacji koszyka SDK refetchuje automatycznie). Cart ID żyje w cookie cart-id (30 dni, SSR-visible) — patrz SDK — Koszyk.
CartqueryCartquery Cart($id: ID!)
Fetches a cart by `id` (the value persisted by the SDK in the `cart-id` cookie). The cart query is public — no auth needed to read it — but once a customer logs in and gets associated with the cart, mutations enforce ownership. Returns line items (paginated up to 100), totals, applied discount codes, gift cards, buyer identity, note, attributes, and warnings. Refetch after every cart mutation.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$id | ID! | — | Yes |
GraphQL operation
query Cart($id: ID!) {
cart(id: $id) {
...Cart
}
}
CartWalidacja kodu rabatowego (preview)
Walidacja kodu przed jego naliczeniem na koszyku — używaj na expand discount input żeby pokazać "Kod ważny: 10% rabatu" zanim user kliknie Apply. Mutacja cartDiscountCodesUpdate aplikuje kod realnie i zwraca user errors per cart-level rules.
Preview jest parytetowy z apply — nie kłamie. Kod scope-mismatch (ważny, ale żadna pozycja koszyka nie wchodzi w jego zasięg produktowy) zwróci isValid:false z error.code: NOT_APPLICABLE_TO_CART, dokładnie tak jak cartDiscountCodesUpdate ustawi dla niego isApplicable:false. Dzięki temu preview nie pokaże „Kod ważny", jeśli realne naliczenie dałoby 0 zł. Wynik zależy od cart.subtotal (reguły minimum-order) — nie cache'uj agresywnie: użyj fetchPolicy: "network-only" albo dołącz cart.subtotal do klucza cache.
CartValidateDiscountCodequeryDiscount Code Validationquery CartValidateDiscountCode($cartId: ID!, $discountCode: String!)
Read-only validation of a discount code against an existing cart — does NOT modify cart state. Returns `{ isValid, discount, error }` (`DiscountValidationResult`) for previewing the effect of a code (inline UI feedback as the user types, before they commit to applying via `cartDiscountCodesUpdate`). Validates: discount existence + active status + customer eligibility + minimum order amount + minimum quantity met. Errors: `NOT_FOUND`, `INACTIVE`, `NOT_STARTED`, `EXPIRED`, `USAGE_LIMIT_REACHED`, `CUSTOMER_USAGE_LIMIT_REACHED`, `CUSTOMER_NOT_ELIGIBLE`, `MINIMUM_ORDER_NOT_MET`, `MINIMUM_QUANTITY_NOT_MET`.
GraphQL operation
query CartValidateDiscountCode($cartId: ID!, $discountCode: String!) {
cartValidateDiscountCode(cartId: $cartId, discountCode: $discountCode) {
isValid
discount {
code
title
type
value
discountAmount {
amount
currencyCode
}
}
error {
code
message
}
}
}
Dostępne metody płatności
Lista metod płatności dostępnych dla sklepu (PayU, Przelewy24, BLIK, Stripe, Apple Pay, COD itd.) — używana w checkout payment step.
AvailablePaymentMethodsqueryPayment Methodsquery AvailablePaymentMethods
Returns the active payment methods for the shop, sorted by the merchant-configured display position. Shop-level — does NOT vary by cart amount or currency. Each method exposes `type` (`CARD`, `BANK_TRANSFER`, `BLIK`, `PAYPAL`, `APPLE_PAY`, `GOOGLE_PAY`, `CASH_ON_DELIVERY`, `OTHER`), provider, icon, description, and supported currencies. Use to render the payment step of checkout.
GraphQL operation
query AvailablePaymentMethods {
availablePaymentMethods {
...AvailablePaymentMethods
}
}
AvailablePaymentMethodsDostępne metody płatności — cart-aware (checkout flow)
Ta sama lista metod, ale w kontekście konkretnego koszyka: opłaty za formy płatności (np. za pobranie) mają tu gotowe kwoty policzone od wartości tego koszyka — dokładnie te same, które trafią do podsumowania po wyborze. Kafelek metody i podsumowanie czytają to samo pole, więc kwoty nigdy się nie rozjadą.
Używaj w kroku płatności kasy, od momentu utworzenia koszyka. Dla podglądów poza koszykiem (np. siatka form płatności na stronie produktu) służy shop-level operacja powyżej — tam fee jest zawsze null, bo kwota dopłaty nie istnieje bez koszyka. Pełny scenariusz: przepis Koszt formy płatności na kafelku.
CartAvailablePaymentMethodsqueryPayment Methodsquery CartAvailablePaymentMethods($cartId: ID!)
Cart-aware payment methods discovery. Returns the same active payment methods as the shop-level `availablePaymentMethods`, but with surcharge amounts resolved: a payment fee can be a percentage of the order value, so the exact amount (e.g. "+5 zł" or "+2.5% (7.18 zł)") is only computable against a cart. Prefer this once a cart exists (`cartCreate`); use the shop-level query for pre-cart previews (method grid, product page upsell) — there `fee` is always null, because the amount it would carry does not exist without a cart. Read `methods[].fee` for the whole-tile surcharge; when instruments carry different fees (e.g. per-brand card surcharges), read `methods[].instruments[].fee` per instrument instead. This query does NOT change cart state and is safe to retry; returns null when the cart does not exist.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$cartId | ID! | — | Yes |
GraphQL operation
query CartAvailablePaymentMethods($cartId: ID!) {
cart(id: $cartId) {
id
availablePaymentMethods {
...PaymentMethod
}
}
}
PaymentMethodDostępne metody wysyłki — preview (pre-cart)
Używaj tylko gdy klient jeszcze nie utworzył koszyka — np. shipping calculator na product detail page („ile zapłacę za dostawę?"). Po utworzeniu cart preferuj cart-aware wariant poniżej (eliminuje konieczność client-side computation subtotal/totalWeight i automatycznie respektuje digital-only constraint).
AvailableShippingMethodsqueryShipping Methodsquery AvailableShippingMethods($address: ShippingAddressInput!, $cart: CartShippingInput)
Returns shipping methods for a given destination address and cart shape (subtotal, total weight, currency). The query computes everything from the inputs alone — no existing cart is required, so it can be used for "shipping cost preview" UIs (e.g. product detail page shipping calculator) before the customer adds anything to a cart. Each method includes price, free-shipping progress (`{ qualifies, currentAmount, threshold, remaining, progressPercent }`), estimated delivery, and carrier metadata. Sorted by the merchant's `sortOrder`, then by price. For a cart-bound checkout flow (where the cart is already known and the storefront wants the resolver to skip non-physical items and surface a `DIGITAL_ONLY_NO_SHIPPING` user error for all-digital carts), use `CartAvailableShippingMethods` against `cart.availableShippingMethods(address)` instead.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$address | ShippingAddressInput! | — | Yes |
$cart | CartShippingInput | — | No |
GraphQL operation
query AvailableShippingMethods($address: ShippingAddressInput!, $cart: CartShippingInput) {
availableShippingMethods(address: $address, cart: $cart) {
methods {
...AvailableShippingMethod
}
freeShippingProgress {
...FreeShippingProgress
}
userErrors {
...UserError
}
}
}
Dostępne metody wysyłki — cart-aware (checkout flow)
Field na Cart aggregate. Backend pobiera dane koszyka z DB (subtotal, totalWeight, productType per linia), więc nie musisz ich obliczać po stronie klienta. Dla koszyka 100% cyfrowego (typy DIGITAL / GIFT_CARD / SERVICE / SUBSCRIPTION) zwraca empty methods + userErrors[{ code: 'DIGITAL_ONLY_NO_SHIPPING' }] — możesz użyć tego sygnału do pominięcia całego kroku wyboru wysyłki w checkout.
Razem z polem cart.requiresShipping: Boolean! (single bool) daje storefrontowi spójny mechanizm decydowania kiedy renderować shipping picker.
CartAvailableShippingMethodsqueryShipping Methodsquery CartAvailableShippingMethods($cartId: ID!, $address: ShippingAddressInput!)
Cart-aware shipping methods discovery. Returns shipping methods available for the cart's contents at the given destination, with subtotal and physical-item weight pulled from the cart aggregate (no need to compute them client-side). When the cart contains only non-physical items (digital, gift card, service, subscription), the response is `methods: []` plus a `DIGITAL_ONLY_NO_SHIPPING` user error — use this as the signal to skip rendering the shipping picker step. Prefer this query over the standalone `AvailableShippingMethods` once a cart has been created (`cartCreate`). For pre-cart "shipping cost preview" UIs on product detail pages, the standalone query remains the right tool.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$cartId | ID! | — | Yes |
$address | ShippingAddressInput! | — | Yes |
GraphQL operation
query CartAvailableShippingMethods($cartId: ID!, $address: ShippingAddressInput!) {
cart(id: $cartId) {
id
requiresShipping
availableShippingMethods(address: $address) {
methods {
...AvailableShippingMethod
}
freeShippingProgress {
...FreeShippingProgress
}
userErrors {
...UserError
}
}
}
}
Live carrier rates (opcjonalne)
Pobiera stawki wysyłki w czasie rzeczywistym od skonfigurowanych dostawców (InPost, DPD, Furgonetka itp.). Wyniki cachowane w Redis przez 15 minut na kombinację adresu i wagi. Dostawcy nieodpowiadający w czasie / zwracający błąd są pomijani. Query istnieje w schema, ale nie jest pre-built operation — odpalaj ad-hoc:
query AvailableShippingRates(
$address: ShippingAddressInput!
$packages: [PackageDimensionsInput!]
$totalWeight: Float
) {
availableShippingRates(address: $address, packages: $packages, totalWeight: $totalWeight) {
rates { providerId serviceId serviceName price { amount currencyCode } estimatedDeliveryDays isPickupAvailable isCashOnDeliveryAvailable }
errors { provider message code }
cached
}
}
Klient
Profil klienta z adresami i historią zamówień. SDK <StorefrontProvider> automatycznie dołącza nagłówek Authorization: Bearer <customerAccessToken> po customerLogin, więc te zapytania działają bez ręcznego przekazywania tokenu.
Formularz rejestracji (konfiguracja pól)
Publiczna konfiguracja formularza rejestracji sklepu — renderuj formularz zapisu na jej podstawie zamiast hardkodować pola. Mówi, czy nowe konta wymagają ręcznej akceptacji (requireApproval — customerSignup zwraca wtedy accountStatus: PENDING_APPROVAL i nie wydaje tokenu), które wbudowane sekcje pokazać (firma / adres / telefon, każda w trybie OFF / OPTIONAL / REQUIRED) oraz pola własne zdefiniowane przez sklep. Wartości pól własnych przekaż do customerSignup.input.customFields. Etykiety przychodzą w języku żądania.
RegistrationFormqueryForms engine (store-defined forms)query RegistrationForm
Registration form configuration of the store. Tells you whether new accounts need manual approval (`requireApproval` — `customerSignup` then returns accountStatus PENDING_APPROVAL and no token), which built-in sections to render (company / address / phone, each OFF / OPTIONAL / REQUIRED) and the store-defined custom fields. Collect custom-field values and pass them to `customerSignup.input.customFields`. All labels are resolved to the request language.
GraphQL operation
query RegistrationForm {
registrationForm {
requireApproval
companySection
addressSection
phoneSection
fields {
...FormField
}
}
}
FormFieldPełny profil (z adresami i historią zamówień)
Zwraca profil klienta z paginowanymi adresami (Relay Connection) i historią zamówień. Field numberOfOrders jest typu UnsignedInt64 (zwracany jako String) — bezpieczny dla dużych wartości.
CustomerqueryCustomer (requires auth)query Customer
Full customer profile — basic info plus the first 10 addresses and first 10 orders. Heaviest customer query; for narrow use cases prefer `CustomerProfile` (no orders / addresses) or `CustomerOrder` (single order). Returns null if unauthenticated.
GraphQL operation
query Customer {
customer {
...Customer
addresses(first: 10) {
edges {
cursor
node {
...MailingAddress
}
}
nodes {
...MailingAddress
}
pageInfo {
...PageInfo
}
totalCount
}
orders(first: 10) {
edges {
node {
...Order
}
cursor
}
pageInfo {
...PageInfo
}
totalCount
}
}
}
Lekki profil (bez adresów / zamówień)
Wariant pod stronę ustawień / profilu — szybsze niż Customer bo pomija historię zamówień i listę adresów. Używaj na /account jeśli nie potrzebujesz orders.
CustomerProfilequeryCustomer (requires auth)query CustomerProfile
Lightweight customer profile (no orders, no addresses list). Use for settings / profile pages that only need basic customer info — much cheaper than `Customer`. Returns null if unauthenticated.
GraphQL operation
query CustomerProfile {
customer {
...Customer
}
}
CustomerAdresy klienta (paginowane)
Dedykowane zapytanie tylko o adresy z paginacją — używaj na stronie zarządzania adresami dostawy.
CustomerAddressesqueryCustomer (requires auth)query CustomerAddresses
Authenticated customer's saved address book — used on checkout to let the buyer pick a previously-used shipping / billing address instead of typing it. Each entry carries B2B invoicing fields (`taxId`, `vatNumber`) and the `isDefault` flag so the same list serves both as the shipping picker and as the billing/invoice picker. Returns up to 50 addresses (Relay Connection — buyers rarely keep more); for the unauthenticated case the connection is empty (no error). The default address is also surfaced as `Customer.defaultAddress`.
GraphQL operation
query CustomerAddresses {
customer {
addresses(first: 50) {
nodes {
...MailingAddress
}
pageInfo {
...PageInfo
}
totalCount
}
}
}
Pojedyncze zamówienie klienta
Szczegóły jednego zamówienia. Bardziej wydajne niż pobieranie pełnego profilu z Customer — używaj na /account/orders/:id.
CustomerOrderqueryCustomer (requires auth)query CustomerOrder($orderId: ID!)
Single order by `orderId`. Returns only orders that belong to the authenticated customer (cross-customer access returns null, not an error). Much cheaper than fetching the full `Customer` payload to access one order. Use on the order detail page.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$orderId | ID! | — | Yes |
GraphQL operation
query CustomerOrder($orderId: ID!) {
customerOrder(orderId: $orderId) {
...Order
}
}
OrderZamówienia (dostęp gościa)
Klient gość — bez konta — nie ma customerAccessToken, ale otrzymuje opaque Order.accessToken w payload'zie cartComplete. To umożliwia mu wejście na /orders/confirmation?token=... z pełnym widokiem zamówienia.
Order by opaque token
Dostęp gościa do podsumowania zamówienia bez sesji. Przeznaczone do strony potwierdzenia tuż po cartComplete. Opcjonalny email to mechanizm defense-in-depth (przy niezgodności query zwraca null).
OrderByTokenqueryCustomer (requires auth)query OrderByToken($token: String!, $email: String)
Fetch a single order using its opaque access token (`Order.accessToken`) — designed for guest order summary pages where the buyer has not signed in. The token is returned in `cartComplete.order.accessToken` immediately after checkout completes; persist it in an HTTP-only cookie (preferred) or `sessionStorage` for the post-checkout page (NEVER `localStorage`). Optional `email` parameter adds defense-in-depth: when provided, it is matched case-insensitively against the order's buyer email; on mismatch the query returns `null` exactly like an invalid token (the response shape is identical, so an attacker cannot distinguish "token valid, wrong email" from "token invalid"). Rate-limited to 5 requests per minute per IP+shop combination to deter token enumeration; clients exceeding the limit receive a GraphQL error with `extensions.code: THROTTLED`. The response is marked `Cache-Control: no-store` so per-customer order data is never served from CDN or browser cache between users. Safe to retry; the token is permanent until the order is deleted.
GraphQL operation
query OrderByToken($token: String!, $email: String) {
orderByToken(token: $token, email: $email) {
...Order
}
}
Order- Zapytanie jest publiczne (nie wymaga sesji ani tokenu klienta)
- Rate limit: 5 żądań / minutę / IP + sklep
- Odpowiedź nosi nagłówek
Cache-Control: no-store— nie cache'uj jej na CDN Order.accessToken(UUID v4, trwały per zamówienie) zwracany wcartComplete.ordernatychmiast po finalizacji- Storefront powinien przechowywać token w HTTP-only cookie lub
sessionStorage— nigdy wlocalStorage
Pełny przewodnik: SDK — Zamówienia.
Przesyłki i zwroty
Po cartComplete zamówienie ma jeden lub więcej Shipment (paczek u kuriera) i opcjonalnie Return (RMA — zwrot zainicjowany przez klienta).
Pojedyncza przesyłka
ShipmentqueryShipments / Trackingquery Shipment($id: ID!)
Fetches a shipment by `id` with status, tracking events, recipient address, and shipped/delivered timestamps. **Auth required** — customer access token plus ownership of the parent order. Wrapped response: `{ shipment, userErrors[] }`. Error codes: `INVALID_TOKEN`, `NOT_FOUND` (also returned on ownership mismatch to prevent enumeration), `FETCH_FAILED`.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$id | ID! | — | Yes |
GraphQL operation
query Shipment($id: ID!) {
shipment(id: $id) {
shipment {
...Shipment
}
userErrors {
...UserError
}
}
}
Tracking przesyłki po numerze przewozowym
Lekkie publiczne zapytanie dla strony „Track your shipment" — pozwala znaleźć status bez logowania.
ShipmentByTrackingNumberqueryShipments / Trackingquery ShipmentByTrackingNumber($trackingNumber: String!)
**Public** shipment lookup by carrier tracking number — no auth required. Designed for "Track my order" landing pages reachable without login. Returns the basic shipment fragment including recipient address. Wrapped response: `{ shipment, userErrors[] }`. Error codes: `INVALID_INPUT`, `NOT_FOUND`, `FETCH_FAILED`.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$trackingNumber | String! | — | Yes |
GraphQL operation
query ShipmentByTrackingNumber($trackingNumber: String!) {
shipmentByTrackingNumber(trackingNumber: $trackingNumber) {
shipment {
...ShipmentBasic
}
userErrors {
...UserError
}
}
}
Pojedynczy zwrot (RMA)
ReturnqueryReturns / RMAquery Return($id: ID!)
Fetches a single return (RMA) by `id` with line items, refund/compensation info, and history. **Auth required** — customer access token plus ownership of the return. Wrapped response: `{ return, userErrors[] }`. Error codes: `INVALID_TOKEN`, `NOT_FOUND` (also returned on ownership mismatch), `FETCH_FAILED`.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$id | ID! | — | Yes |
GraphQL operation
query Return($id: ID!) {
return(id: $id) {
return {
...Return
}
userErrors {
...UserError
}
}
}
Zwroty per zamówienie
ReturnsByOrderqueryReturns / RMAquery ReturnsByOrder($orderId: ID!)
Lists returns for a given order (paginated, default page size 20, cursor-based). **Auth required** — customer access token plus ownership of the order; the connection is empty (no explicit error) on auth failure. Use on the order detail page to show return history.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$orderId | ID! | — | Yes |
GraphQL operation
query ReturnsByOrder($orderId: ID!) {
returnsByOrder(orderId: $orderId) {
edges {
node {
...Return
}
cursor
}
pageInfo {
...PageInfo
}
totalCount
}
}
Powody zwrotu (do formularza RMA)
Statyczna lista enum'ów wystawiana w formularzu „Zwracam, bo..." — pre-fetchnij raz na sesję.
ReturnReasonsqueryReturns / RMAquery ReturnReasons
Returns the standard list of return reasons used by the RMA flow: `DEFECTIVE`, `NOT_AS_DESCRIBED`, `WRONG_ITEM`, `CHANGED_MIND`, `BETTER_PRICE`, `DAMAGED_SHIPPING`, `OTHER`. The list is fixed across all shops — not per-shop configurable. Public; no auth required.
GraphQL operation
query ReturnReasons {
returnReasons {
...ReturnReasonOption
}
}
ReturnReasonOptionKarty podarunkowe (Gift cards)
Gift card to balance-bearing token — klient kupuje kartę za X PLN, otrzymuje kod, ktoś inny realizuje w checkout. DoSwiftly wspiera digital gift cards (kod email) i fizyczne (kod w pudełku).
Sprawdzenie karty (full payload)
Zwraca pełne dane karty po kodzie — balance, expiry, status (active / depleted / expired / cancelled).
GiftCardqueryGift Cardsquery GiftCard($code: String!)
Public gift-card lookup by `code`. Returns balance, currency, expiry, and `maskedCode` (first 4 + last 4 chars only — the full code never leaks back). Returns null if the code is unknown (rather than an explicit error, to limit enumeration). **Rate-limited**: 10 requests per 60 seconds per IP.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$code | String! | — | Yes |
GraphQL operation
query GiftCard($code: String!) {
giftCard(code: $code) {
...GiftCard
}
}
GiftCardWalidacja przed użyciem
Lekka walidacja w checkout — sprawdza czy kod istnieje, czy nie wygasł i czy ma wystarczający balance dla $amount. Mutacja cartApplyGiftCard aplikuje kartę realnie.
GiftCardValidatequeryGift Cardsquery GiftCardValidate($code: String!, $amount: Float)
Validates whether a gift card is usable (and optionally for a given `$amount`). Checks status (`DISABLED`, `USED`, `EXPIRED`), expiry date, and — when `$amount` is provided — sufficient balance. Returns `{ validation: { isValid, availableBalance, error: { code, message } }, userErrors[] }`. Validation error codes: `NOT_FOUND`, `DISABLED`, `ALREADY_USED`, `EXPIRED`, `INSUFFICIENT_BALANCE`. **Rate-limited**: 10 / 60s.
GraphQL operation
query GiftCardValidate($code: String!, $amount: Float) {
giftCardValidate(code: $code, amount: $amount) {
validation {
...GiftCardValidation
}
userErrors {
...UserError
}
}
}
Program lojalnościowy
Loyalty program oparty o punkty + tiery (Bronze / Silver / Gold / Platinum). Klient zarabia punkty za zamówienia (× tier multiplier) i wymienia je na nagrody (rewards). Większość zapytań wymaga autoryzacji klienta (Authorization: Bearer <token>).
Status członkostwa klienta
Punkty, tier, postęp do następnego, suma rocznych wydatków, ostatnia aktywność.
LoyaltyMemberqueryLoyalty Programquery LoyaltyMember
Returns the logged-in customer's loyalty membership: points (current, pending, redeemed, expired, expiring), current tier, tier progress, annual spend, last activity. Returns null if the customer is not enrolled — there is **no auto-enrollment** here (enrollment happens via signup or a first qualifying order). Auth required.
GraphQL operation
query LoyaltyMember {
loyaltyMember {
...LoyaltyMember
}
}
LoyaltyMemberDefinicje tierów
Publiczna lista tierów — możesz pokazać na stronie programu „Jak działa nasz program" bez logowania.
LoyaltyTiersqueryLoyalty Programquery LoyaltyTiers
Lists the loyalty tiers configured for the shop (`BRONZE`, `SILVER`, `GOLD`, `PLATINUM`, `DIAMOND` etc.) with their `minPoints`, `minAnnualSpend`, `pointsMultiplier`, and custom benefits. Sorted by `minPoints` ASC. Public; no auth required.
GraphQL operation
query LoyaltyTiers {
loyaltyTiers {
...LoyaltyTier
}
}
LoyaltyTierKatalog nagród
Lista dostępnych rewards do wymiany za punkty (rabaty %, kwoty, produkty, freebies).
LoyaltyRewardsqueryLoyalty Programquery LoyaltyRewards
Lists rewards customers can redeem (free shipping, percent off, free product, gift card). Filtered to **active** rewards only (`is_active = true` AND inside their `starts_at`/`ends_at` window). Public; no auth required.
GraphQL operation
query LoyaltyRewards {
loyaltyRewards {
...LoyaltyReward
}
}
LoyaltyRewardHistoria transakcji punktowych
Paginowana lista zdarzeń EARNED / REDEEMED / EXPIRED / ADJUSTED z opisem i orderId.
LoyaltyTransactionsqueryLoyalty Programquery LoyaltyTransactions($first: Int = 20, $after: String)
Paginated history of loyalty point transactions for the logged-in customer (default 20). Transaction `type` enum: `EARN_PURCHASE`, `EARN_SIGNUP`, `EARN_REFERRAL`, `EARN_REVIEW`, `EARN_BIRTHDAY`, `EARN_BONUS`, `REDEEM`, `EXPIRE`, `ADJUST`, `REFUND_REVERSAL`. Auth required — empty connection if unauthenticated.
GraphQL operation
query LoyaltyTransactions($first: Int = 20, $after: String) {
loyaltyTransactions(first: $first, after: $after) {
edges {
node {
...LoyaltyTransaction
}
cursor
}
pageInfo {
...LoyaltyPageInfo
}
totalCount
}
}
Publiczna konfiguracja programu
Włączony / wyłączony, nazwa punktów, multiplier per PLN, expiry policy, dostępne actions, referral settings. Pre-fetchnij na bootstrap żeby zdecydować czy renderować loyalty UI w ogóle.
LoyaltySettingsqueryLoyalty Programquery LoyaltySettings
Returns the loyalty program configuration: `isEnabled`, `pointsName` (e.g. "stars"), `pointsPerCurrency`, `pointsExpiryMonths`, available earn actions, referral settings. Use this at app boot to decide whether to render any loyalty UI at all. Public; no auth required.
GraphQL operation
query LoyaltySettings {
loyaltySettings {
...LoyaltySettings
}
}
LoyaltySettingsEstymacja punktów dla zamówienia
Pre-checkout preview „zarobisz X punktów za to zamówienie" — uwzględnia mnożnik tieru klienta.
EstimatePointsqueryLoyalty Programquery EstimatePoints($orderTotal: Float!)
Estimates how many loyalty points the customer would earn for an order of `$orderTotal` (in major currency units). When the customer is authenticated, the result accounts for their current tier's points multiplier. Use on cart/checkout to show "Earn X points with this order".
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$orderTotal | Float! | — | Yes |
GraphQL operation
query EstimatePoints($orderTotal: Float!) {
estimatePoints(orderTotal: $orderTotal) {
...PointsEstimate
}
}
PointsEstimateStatystyki poleceń (referrals)
Kod polecający klienta + URL do udostępniania + liczniki polecień (total / completed / pending) + zarobione punkty.
ReferralStatsqueryLoyalty Programquery ReferralStats
Returns the customer's referral statistics: `referralCode`, `shareUrl`, `totalReferred`, `completedReferrals`, `pendingReferrals`, `totalPointsEarned`. Auth required. Returns null if unauthenticated or if the referral program is disabled for the shop.
GraphQL operation
query ReferralStats {
referralStats {
...ReferralStats
}
}
ReferralStatsNewsletter
Zachęta za zapis (baner rabatowy)
Publiczna konfiguracja zachęty „zapisz się do newslettera i odbierz kod rabatowy" — isEnabled plus kształt nagrody (typ, procent lub kwota, minimalna wartość zamówienia, dni ważności kodu). Sklep bez włączonej kampanii zwraca bezpieczny payload z isEnabled: false (nie null) — renderuj baner warunkowo. Wartości pochodzą wprost z rabatu skonfigurowanego przez merchanta, więc treść banera zawsze zgadza się z kodem, który klient dostanie mailem po potwierdzeniu zapisu (mutacje zapisu: Newsletter).
NewsletterIncentivequeryLoyalty Programquery NewsletterIncentive
Returns the shop's newsletter signup incentive — when `isEnabled` is true, render the signup banner with the returned discount shape (e.g. "subscribe & get -10%"); a confirmed signup earns a personal, single-use code delivered by email (it works only for the address it was issued to). When false, hide the incentive messaging — plain newsletter signup still works. Public; no auth required.
GraphQL operation
query NewsletterIncentive {
newsletterIncentive {
...NewsletterIncentive
}
}
NewsletterIncentiveFormularze sklepu
Formularz zdefiniowany przez sklep (po slugu)
Formularz (np. kontaktowy) zdefiniowany przez merchanta w panelu. Renderuj fields (klucz, typ, etykieta, opcje, wymagalność, kolejność), a zebrane wartości wyślij mutacją formSubmit z tym samym slugiem. null oznacza, że pod slugiem nie ma aktywnego formularza — potraktuj to jak stan strony 404.
FormqueryForms engine (store-defined forms)query Form($slug: String!)
A store-defined contact form by slug. Render `fields` and send the collected values via `formSubmit` with the same slug. Returns null when no active form exists under the slug — treat it as a 404 page state.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$slug | String! | — | Yes |
GraphQL operation
query Form($slug: String!) {
form(slug: $slug) {
id
slug
name
fields {
...FormField
}
}
}
FormFieldRecenzje
Recenzje produktów (rating 1-5 gwiazdek + komentarz + helpful votes). Moderowane po stronie merchanta — productReviews zwraca tylko zatwierdzone.
Recenzje produktu (paginowane)
ProductReviewsqueryReviewsquery ProductReviews($productId: ID!, $first: Int = 10, $after: String, $sortKey: ReviewSortKey = CREATED_AT, $reverse: Boolean = true)
Paginated list of customer reviews for a product, **filtered to APPROVED reviews only** (PENDING / REJECTED reviews are not exposed to the storefront). Sort by `CREATED_AT` (default), helpfulness, or rating. Public; no auth required.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$productId | ID! | — | Yes |
$first | Int | 10 | No |
$after | String | — | No |
$sortKey | ReviewSortKey | CREATED_AT | No |
$reverse | Boolean | true | No |
GraphQL operation
query ProductReviews($productId: ID!, $first: Int = 10, $after: String, $sortKey: ReviewSortKey = CREATED_AT, $reverse: Boolean = true) {
productReviews(
productId: $productId
first: $first
after: $after
sortKey: $sortKey
reverse: $reverse
) {
edges {
node {
...ProductReview
}
cursor
}
pageInfo {
...PageInfo
}
totalCount
}
}
Agregaty (rating distribution)
Średnia ocen, count per gwiazdkę, total reviews — pod widget „4.8 ★ (132 reviews)" na product detail.
ReviewStatsqueryReviewsquery ReviewStats($productId: ID!)
Aggregate review statistics for a product: average rating, total count, distribution per star (1-5). Computed from APPROVED reviews only. Use for product card review summaries. Public; no auth required.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$productId | ID! | — | Yes |
GraphQL operation
query ReviewStats($productId: ID!) {
reviewStats(productId: $productId) {
...ReviewStats
}
}
ReviewStatsLista życzeń (Wishlist)
Klient może mieć wiele list życzeń (jedna domyślna „Moje ulubione" + custom np. „Prezenty na święta"). Items to powiązania z productVariantId + opcjonalnym note.
Listy klienta (paginowane)
WishlistsqueryWishlistsquery Wishlists($first: Int = 20, $after: String)
Paginated list of the logged-in customer's wishlists (default 20). Auth required — empty connection if unauthenticated. Customers typically have a small set (<10).
GraphQL operation
query Wishlists($first: Int = 20, $after: String) {
wishlists(first: $first, after: $after) {
edges {
cursor
node {
...Wishlist
}
}
nodes {
...Wishlist
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
totalCount
}
}
WishlistPojedyncza lista z itemami
WishlistByIdqueryWishlistsquery WishlistById($id: ID!)
Fetches a single wishlist by `id`. Private wishlists are visible only to the owner; public wishlists are visible to anyone. Note: this query supports lookup by `id` only — there is currently no way to fetch a wishlist by its share token.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$id | ID! | — | Yes |
GraphQL operation
query WishlistById($id: ID!) {
wishlist(id: $id) {
...Wishlist
}
}
WishlistBlog (content marketing)
Blog DoSwiftly to standardowy CMS pod treści marketingowe — posty, kategorie, tagi, featured flag. Do wpisu, kategorii i tagu merchant może dodatkowo przypiąć produkty (merchandising), które renderujesz jako pasek przy treści.
Lista postów (z filtrowaniem i sortowaniem)
BlogPostsqueryBlogquery BlogPosts($first: Int = 20, $after: String, $categoryHandle: String, $tagHandle: String, $featured: Boolean, $sortKey: BlogPostSortKey = PUBLISHED_AT, $reverse: Boolean = false)
Paginated list of published blog posts. Filter by `categoryHandle`, `tagHandle`, or `featured` (boolean flag, not enum). Sort: `PUBLISHED_AT` (default), `TITLE`, `VIEW_COUNT`, or `CREATED_AT`. Public; no auth required.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$first | Int | 20 | No |
$after | String | — | No |
$categoryHandle | String | — | No |
$tagHandle | String | — | No |
$featured | Boolean | — | No |
$sortKey | BlogPostSortKey | PUBLISHED_AT | No |
$reverse | Boolean | false | No |
GraphQL operation
query BlogPosts($first: Int = 20, $after: String, $categoryHandle: String, $tagHandle: String, $featured: Boolean, $sortKey: BlogPostSortKey = PUBLISHED_AT, $reverse: Boolean = false) {
blogPosts(
first: $first
after: $after
categoryHandle: $categoryHandle
tagHandle: $tagHandle
featured: $featured
sortKey: $sortKey
reverse: $reverse
) {
edges {
node {
...BlogPost
}
cursor
}
pageInfo {
...PageInfo
}
totalCount
}
}
Pojedynczy post
Po id lub handle.
BlogPostqueryBlogquery BlogPost($id: ID, $handle: String)
Fetches a single blog post by `id` or `handle`. Visibility-gated: returns null if the post is not yet `PUBLISHED` or if its publish date is in the future (scheduled posts stay hidden until their publish time). Side effect: fetching a post increments its `view_count` asynchronously (does not block the response).
GraphQL operation
query BlogPost($id: ID, $handle: String) {
blogPost(id: $id, handle: $handle) {
...BlogPost
}
}
BlogPostKategorie blogowe
Lista wszystkich kategorii (nawigacja bloga):
BlogCategoriesqueryBlogquery BlogCategories
Lists all blog categories with per-category `postCount` and SEO metadata. Use to render category navigation on blog pages. Public; no auth required.
GraphQL operation
query BlogCategories {
blogCategories {
...BlogCategory
}
}
BlogCategoryPojedyncza kategoria po id lub handle — na stronę kategorii (obraz + seo). Listę wpisów w kategorii pobierasz osobno przez BlogPosts z argumentem categoryHandle.
BlogCategoryqueryBlogquery BlogCategory($id: ID, $handle: String)
Fetches a single blog category by `id` or `handle`, including its image and SEO metadata. Use on a category landing page (the paginated post list for the category comes from `BlogPosts` with `categoryHandle`). Public; no auth required.
GraphQL operation
query BlogCategory($id: ID, $handle: String) {
blogCategory(id: $id, handle: $handle) {
...BlogCategory
image {
...ImageCard
}
seo {
title
description
}
}
}
Tagi blogowe
Lista tagów (chmura tagów, z licznikiem postCount):
BlogTagsqueryBlogquery BlogTags
Lists blog tags with usage counts (`postCount` per tag). Use to render a tag cloud. Public; no auth required.
GraphQL operation
query BlogTags {
blogTags {
...BlogTag
}
}
BlogTagPojedynczy tag po id lub handle — na stronę tagu. Pole seo niesie meta ustawione przez merchanta, a gdy ich nie ustawił — tytuł złożony z nazwy tagu (opisu tag nie ma). Listę wpisów z tagiem pobierasz osobno przez BlogPosts z argumentem tagHandle.
BlogTagqueryBlogquery BlogTag($id: ID, $handle: String)
Fetches a single blog tag by `id` or `handle`, including its SEO metadata (null when the merchant has not set any). Use on a tag landing page (the paginated post list for the tag comes from `BlogPosts` with `tagHandle`). Public; no auth required.
GraphQL operation
query BlogTag($id: ID, $handle: String) {
blogTag(id: $id, handle: $handle) {
...BlogTag
seo {
title
description
}
}
}
BlogTagPrzypięte produkty (merchandising)
Wpis, kategoria i tag mają pole products — connection produktów przypiętych przez merchanta (bezpośrednio, przez kategorie produktów lub przez kolekcje). Connection jest zwracany zawsze (pusty, gdy nic nie przypięto) i zawiera wyłącznie produkty widoczne w sklepie — ukryte i nieaktywne są automatycznie odsiewane. Paginacja first/after i sortowanie sortKey/reverse działają jak w katalogu.
Produkty przypięte do wpisu:
BlogPostProductsqueryBlogquery BlogPostProducts($id: ID, $handle: String, $first: Int = 12, $after: String, $sortKey: ProductSortKeys = CREATED_AT, $reverse: Boolean = false)
Products the merchant pinned to a blog post — directly, via product categories, or via collections. Always a connection: empty when nothing is pinned; only products visible on the storefront are included. Use to render a product strip inside or below the post. Public; no auth required.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$id | ID | — | No |
$handle | String | — | No |
$first | Int | 12 | No |
$after | String | — | No |
$sortKey | ProductSortKeys | CREATED_AT | No |
$reverse | Boolean | false | No |
GraphQL operation
query BlogPostProducts($id: ID, $handle: String, $first: Int = 12, $after: String, $sortKey: ProductSortKeys = CREATED_AT, $reverse: Boolean = false) {
blogPost(id: $id, handle: $handle) {
id
products(first: $first, after: $after, sortKey: $sortKey, reverse: $reverse) {
edges {
node {
...ProductCard
}
cursor
}
pageInfo {
...PageInfo
}
totalCount
}
}
}
Produkty przypięte do kategorii bloga:
BlogCategoryProductsqueryBlogquery BlogCategoryProducts($id: ID, $handle: String, $first: Int = 12, $after: String, $sortKey: ProductSortKeys = CREATED_AT, $reverse: Boolean = false)
Products the merchant pinned to a blog category — directly, via product categories, or via collections. Always a connection: empty when nothing is pinned; only products visible on the storefront are included. Use to render a product strip on the category landing page. Public; no auth required.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$id | ID | — | No |
$handle | String | — | No |
$first | Int | 12 | No |
$after | String | — | No |
$sortKey | ProductSortKeys | CREATED_AT | No |
$reverse | Boolean | false | No |
GraphQL operation
query BlogCategoryProducts($id: ID, $handle: String, $first: Int = 12, $after: String, $sortKey: ProductSortKeys = CREATED_AT, $reverse: Boolean = false) {
blogCategory(id: $id, handle: $handle) {
id
products(first: $first, after: $after, sortKey: $sortKey, reverse: $reverse) {
edges {
node {
...ProductCard
}
cursor
}
pageInfo {
...PageInfo
}
totalCount
}
}
}
Produkty przypięte do tagu bloga:
BlogTagProductsqueryBlogquery BlogTagProducts($id: ID, $handle: String, $first: Int = 12, $after: String, $sortKey: ProductSortKeys = CREATED_AT, $reverse: Boolean = false)
Products the merchant pinned to a blog tag — directly, via product categories, or via collections. Always a connection: empty when nothing is pinned; only products visible on the storefront are included. Use to render a product strip on the tag landing page. Public; no auth required.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$id | ID | — | No |
$handle | String | — | No |
$first | Int | 12 | No |
$after | String | — | No |
$sortKey | ProductSortKeys | CREATED_AT | No |
$reverse | Boolean | false | No |
GraphQL operation
query BlogTagProducts($id: ID, $handle: String, $first: Int = 12, $after: String, $sortKey: ProductSortKeys = CREATED_AT, $reverse: Boolean = false) {
blogTag(id: $id, handle: $handle) {
id
products(first: $first, after: $after, sortKey: $sortKey, reverse: $reverse) {
edges {
node {
...ProductCard
}
cursor
}
pageInfo {
...PageInfo
}
totalCount
}
}
}
Rekomendacje
Algorithm-driven recommendation engine. Intencja: SIMILAR (alternatywy do tego samego produktu), COMPLEMENTARY (cross-sell), BUNDLE (kup razem).
ProductRecommendationsqueryRecommendationsquery ProductRecommendations($productId: ID!, $limit: Int = 8, $intent: RecommendationIntent = SIMILAR)
Returns up to `$limit` recommended products related to `$productId`. Default `$intent: SIMILAR` — products sharing categories or tags. Use on PDP "You may also like" sections. Public; no auth required.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$productId | ID! | — | Yes |
$limit | Int | 8 | No |
$intent | RecommendationIntent | SIMILAR | No |
GraphQL operation
query ProductRecommendations($productId: ID!, $limit: Int = 8, $intent: RecommendationIntent = SIMILAR) {
productRecommendations(productId: $productId, limit: $limit, intent: $intent) {
...ProductCard
}
}
ProductCardStrony i nawigacja (CMS)
Statyczne strony (Polityka prywatności, Regulamin, About) + nawigacja (drzewo menu z urlami).
Pojedyncza strona
Po id lub handle. Używaj na rutach typu /pages/regulamin.
PagequeryContent: Pagesquery Page($handle: String, $id: ID)
Fetches a single CMS page (About, Privacy, Returns Policy, Terms, etc.) by `handle` or `id`. Visibility-gated: returns null if the page is hidden or if its publish date is in the future. Public; no auth required.
GraphQL operation
query Page($handle: String, $id: ID) {
page(handle: $handle, id: $id) {
...ShopPage
}
}
ShopPageLista stron (paginowana)
PagesqueryContent: Pagesquery Pages($first: Int = 20, $after: String, $sortKey: PageSortKeys = TITLE, $reverse: Boolean = false, $query: String)
Paginated list of visible, already-published CMS pages. Use for sitemap, footer link list, or page directory. The `query` argument supports text search over the page title/handle. Public; no auth required.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$first | Int | 20 | No |
$after | String | — | No |
$sortKey | PageSortKeys | TITLE | No |
$reverse | Boolean | false | No |
$query | String | — | No |
GraphQL operation
query Pages($first: Int = 20, $after: String, $sortKey: PageSortKeys = TITLE, $reverse: Boolean = false, $query: String) {
pages(
first: $first
after: $after
sortKey: $sortKey
reverse: $reverse
query: $query
) {
edges {
node {
...ShopPage
}
cursor
}
pageInfo {
...PageInfo
}
totalCount
}
}
Menu nawigacyjne
Drzewo menu po handle (main-menu, footer itd.). Pole url jest budowane server-side z slug/handle zasobu (bez extra round-trip), pole resource rozwiązywane przez DataLoader (max 4 zapytania per call: Category / Collection / Page / Product).
MenuqueryContent: Navigation Menusquery Menu($handle: String!)
Fetches a navigation menu by `handle` (e.g. `"main-menu"`, `"footer"`, `"mobile"`). Returns the nested item tree (up to 3 levels). Each item is typed as one of: `HTTP`, `FRONTPAGE`, `SEARCH`, `CATALOG`, `BLOG`, `PRODUCT`, `COLLECTION`, `CATEGORY`, `PAGE`, or `BRAND` — switch on the type to render the right link target. Each resource-linked item exposes both a pre-resolved `url` (standard `/categories|/collections|/pages|/products|/brands/<handle>` convention) and a typed `resource` union with the raw handle so storefronts with custom routing can construct their own paths instead. All resource lookups are batched per request — no N+1 even for deep menus.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$handle | String! | — | Yes |
GraphQL operation
query Menu($handle: String!) {
menu(handle: $handle) {
...Menu
}
}
MenuKonwencja url per typ elementu (industry-standard ścieżki sklepowe):
| Typ | URL |
|---|---|
FRONTPAGE | / |
SEARCH | /search |
CATALOG | /products |
BLOG | /blog |
HTTP | Ręcznie wpisany URL |
CATEGORY | /categories/{handle} |
COLLECTION | /collections/{handle} |
PAGE | /pages/{handle} |
PRODUCT | /products/{handle} |
Storefront może przepisywać te ścieżki na własne trasy przez Next.js rewrites lub middleware. Pole url zwraca null tylko gdy linkowany zasób został usunięty.
URL Redirects (301 / 302)
Lista przekierowań skonfigurowanych przez merchanta (po migracji ze starego sklepu, kampanie marketingowe itp.). Storefront eksponuje je w middleware.ts lub next.config.js → redirects().
UrlRedirectsqueryContent: URL Redirectsquery UrlRedirects($first: Int = 250, $after: String)
Returns the shop's URL redirects (legacy `path` → new `target` mappings). Use server-side at the edge or in SSR to issue 301 redirects for migrated routes (preserves SEO equity). Default page size 250 — most shops fit in a single page.
GraphQL operation
query UrlRedirects($first: Int = 250, $after: String) {
urlRedirects(first: $first, after: $after) {
nodes {
...UrlRedirect
}
pageInfo {
...PageInfo
}
}
}
Lokalizacja per sklep (BOPIS / multi-location)
Dla sklepów z wieloma fizycznymi lokalizacjami (magazyny, sklepy stacjonarne, dropshipperzy). Umożliwia BOPIS (Buy Online, Pick up In Store) — klient wybiera lokalizację odbioru, widzi dostępność per sklep, „Click & Collect" jako alternatywa dla wysyłki.
Dostępność per lokalizacja (na wariancie produktu)
ProductStoreAvailability to query do pobrania produktu wraz z storeAvailability per wariant — Relay Connection z edges / pageInfo / totalCount. Dla sklepów z jedną lokalizacją field zwraca null (kompatybilność wsteczna — storefront może pominąć UI picker).
ProductStoreAvailabilityqueryStore Availability: per-location stock (BOPIS / multi-location)query ProductStoreAvailability($handle: String, $id: ID)
Fetches a product (by `handle` or `id`) along with per-variant availability across the merchant's physical locations — for the BOPIS / multi-location flow. The `storeAvailability` connection lives on each `ProductVariant`; its arguments (`first`, `after`, `near`, `locationType`) are set inside the `VariantStoreAvailability` fragment. The connection returns null for single-location shops (in which case the storefront can skip the store picker entirely). `availableStock` is null for anonymous users and an integer for authenticated customers. Apply `@inContext(preferredLocationId: ...)` on the operation to pin the customer's preferred location to the top of the result.
GraphQL operation
query ProductStoreAvailability($handle: String, $id: ID) {
product(handle: $handle, id: $id) {
id
handle
title
variants {
nodes {
...VariantStoreAvailability
}
}
}
}
VariantStoreAvailabilityZachowanie pól:
availableStockjest token-gated —nulldla anonimowych zapytań,Intdla zalogowanego klienta (industry-standard parity)pickupTimeto zlokalizowany string PL/EN obliczany zpickupLeadTimeHours(nulljeśli pickup wyłączony)- Nigdy nie eksponuje pól wewnętrznych:
committed,reserved,damaged,safetyStock
Priorytet sortowania storeAvailability:
@inContext(preferredLocationId: $id)— preferowana lokalizacja na górzenear: { latitude, longitude }— rosnąca odległość Haversine- Bez parametrów —
priority ASC, potemname ASC
Dyrektywa @inContext(preferredLocationId)
Dyrektywa operacji — pinuje wybraną lokalizację na pierwsze miejsce w storeAvailability. Niepoprawne / nieaktywne ID są cicho ignorowane.
query ProductAvailability($handle: String, $loc: ID!) @inContext(preferredLocationId: $loc) {
product(handle: $handle) {
variants {
nodes {
storeAvailability(first: 10) {
edges { node { isAvailable location { id name } } }
}
}
}
}
}
Ta sama dyrektywa przyjmuje też country, language i currency (typ String — wartość w cudzysłowie, np. @inContext(country: "DE")). Ustalają kontekst żądania z najwyższym priorytetem; wartość nieobsługiwana lub niebędąca kodem jest pomijana i rozstrzyga następne źródło — patrz Przegląd API.
Paginowana lista lokalizacji (store picker UI)
Pod store picker w nagłówku („Wybierz sklep") lub samodzielną mapę lokalizacji.
LocationsqueryLocations (store picker UI)query Locations($first: Int = 20, $after: String, $near: GeoCoordinateInput, $hasPickupEnabled: Boolean, $locationType: LocationType)
Paginated list of active store locations (default 20, max 100). Filters: `near` (`{ latitude, longitude }`) for proximity search — sorts ascending by distance; `hasPickupEnabled` for pickup-only filtering; `locationType` (`RETAIL`, `WAREHOUSE`, `PICKUP_POINT`). When `near` is omitted, results are sorted by the merchant's `priority`, then name. Use for the BOPIS store picker UI. Public; no auth required.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$first | Int | 20 | No |
$after | String | — | No |
$near | GeoCoordinateInput | — | No |
$hasPickupEnabled | Boolean | — | No |
$locationType | LocationType | — | No |
GraphQL operation
query Locations($first: Int = 20, $after: String, $near: GeoCoordinateInput, $hasPickupEnabled: Boolean, $locationType: LocationType) {
locations(
first: $first
after: $after
near: $near
hasPickupEnabled: $hasPickupEnabled
locationType: $locationType
) {
totalCount
pageInfo {
...PageInfo
}
edges {
cursor
node {
...Location
}
}
}
}
Pojedyncza aktywna lokalizacja
Po ID. Zwraca null dla nieistniejącej lub nieaktywnej lokalizacji.
LocationqueryLocations (store picker UI)query Location($id: ID!)
Fetches a single store location by `id` — full address, coordinates, business hours, pickup config (lead time, hours, timezone), and services. Returns null if the location is not found, not active, or owned by another shop. Use on the location detail page. Public; no auth required.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$id | ID! | — | Yes |
GraphQL operation
query Location($id: ID!) {
location(id: $id) {
...Location
}
}
LocationEnum LocationType: WAREHOUSE (magazyn, default), STORE (sklep stacjonarny, BOPIS), DROPSHIPPER, FULFILLMENT_CENTER.
storeAvailability używa DataLoader. Przy 30 wariantach na stronie dane są ładowane w 2 zapytaniach (1× liczba aktywnych lokalizacji per shop + 1× batch po variant_id = ANY(...)). Sortowanie i filtrowanie dzieje się w resolverze — nie poison'uje klucza cache.
Ad-hoc queries (schema only)
Następujące queries istnieją w schema, ale nie są pre-built operations w @doswiftly/storefront-operations — odpalaj je jako ad-hoc query (np. przez client.request<T>(gql\...`)`).
Waluty
Multi-currency display. Storefront może pokazywać ceny w wybranej walucie + przeliczać.
query Currencies {
currencies { code name symbol decimalPlaces symbolPosition }
}
query ExchangeRate($from: String!, $to: String!) {
exchangeRate(from: $from, to: $to) # zwraca Float
}
query ConvertCurrency($amount: Float!, $from: String!, $to: String!) {
convertCurrency(amount: $amount, from: $from, to: $to) {
originalAmount originalCurrency
convertedAmount convertedCurrency
rate rateWithMargin marginApplied rateSource rateTimestamp
}
}
query ShopCurrencyConfig {
shopCurrencyConfig { primaryCurrency supportedCurrencies autoConvertPrices }
}
query AllSupportedCurrencies {
allSupportedCurrencies { code name symbol decimalPlaces symbolPosition }
}
query CurrencyInfo($code: String!) {
currencyInfo(code: $code) { code name symbol decimalPlaces symbolPosition }
}
convertCurrency uwzględnia marżę sklepu (admin może ustawić %). allSupportedCurrencies zwraca wszystkie waluty wspierane systemowo (kursy ECB), niezależnie od konfiguracji sklepu.
Język i tłumaczenia
query Languages {
languages { code nativeName englishName direction isDefault }
}
query Translations($input: TranslationsInput!) {
translations(input: $input) {
# zlokalizowane stringi per requested keys
}
}
Unified localization
localization to ujednolicony kontekst lokalizacyjny — dostępne kraje, języki, aktualny kraj i język klienta. Pod picker krajów/języków w storefront.
query Localization {
localization {
availableCountries { id isoCode name currency { isoCode } unitSystem availableLanguages { code nativeName direction isDefault } }
availableLanguages { code nativeName englishName direction isDefault }
country { id isoCode name currency { isoCode } unitSystem }
language { code nativeName englishName direction isDefault }
}
}
availableCountries— kraje ze stref wysyłki sklepu (ta sama lista coshop.shipsToCountries), bez krajów objętych sankcjami.country— bieżący kraj z kontekstu żądania (@inContext(country:)→ geolokalizacja → kraj domyślny sklepu). Nie jest zawężany doavailableCountries: odwiedzający może być w kraju, do którego sklep nie wysyła. Gdy kraju nie da się ustalić,isoCodema wartośćZZ, acurrencyto waluta sklepu.namekraju jest po angielsku; w interfejsie w innym języku zbuduj nazwę zisoCode, np.new Intl.DisplayNames([locale], { type: 'region' }).of(isoCode)(dlaZZpokaż własny tekst, np. „Wybierz kraj").currencyiunitSystemkraju to domyślna waluta i system miar tego kraju — informacja prezentacyjna, nie waluta, w której sklep sprzedaje.
Ceny B2B
Grupy klientów i pricing per wariant produktu — dla zalogowanych B2B klientów zwraca najlepszą dostępną cenę (grupa + rabaty wolumenowe), dla gości cenę detaliczną z zachętą do logowania.
query CustomerGroups {
customerGroups { id name code description discountPercent taxExempt }
}
query B2BPricing($variantId: ID!, $retailPrice: Float!, $quantity: Float) {
b2bPricing(variantId: $variantId, retailPrice: $retailPrice, quantity: $quantity) {
pricing {
retailPrice { amount currencyCode }
yourPrice { amount currencyCode }
savings { amount currencyCode }
savingsPercent
hasGroupDiscount
appliedGroup { id name discountPercent }
tierName
}
volumeTiers { minQuantity maxQuantity unitPrice { amount currencyCode } discountPercent }
isAuthenticated
guestMessage
}
}
b2bPricing nie wymaga autoryzacji, ale zalogowani klienci otrzymują ceny grupowe. Dla gości backend zwraca cenę detaliczną z guestMessage (i18n string „Zaloguj się aby zobaczyć cenę B2B").
Treści cyfrowe i załączniki produktu
Sklep może dołączyć do produktu pliki dostępne dla każdego (instrukcje, certyfikaty, karty gwarancyjne) oraz sprzedawać treści wydawane dopiero po opłaceniu zamówienia. To dwa różne mechanizmy i różnią się jedną istotną rzeczą: plik publiczny ma stały adres, kupiony nie ma.
Załączniki na karcie produktu
Adres załącznika jest stały, więc renderuj go jak zwykły odnośnik i spokojnie trzymaj w pamięci podręcznej. Pole rozwiązywane jest leniwie — lista produktów, która o nie nie pyta, nie płaci za dodatkowe zapytanie po stronie serwera, więc pobieraj je dopiero na karcie produktu.
ProductAttachmentsqueryTreści cyfrowequery ProductAttachments($handle: String!)
Publiczne pliki dołączone do produktu — instrukcje, certyfikaty, karty gwarancyjne. Osobne zapytanie, a nie pole w podstawowym fragmencie produktu: załączniki wymagają dodatkowego odczytu po stronie serwera, więc lista produktów nie powinna za nie płacić. Pobierz je dopiero na karcie produktu.
Variables
| Name | Type | Default | Required |
|---|---|---|---|
$handle | String! | — | Yes |
GraphQL operation
query ProductAttachments($handle: String!) {
product(handle: $handle) {
id
attachments {
...ProductAttachment
}
}
}
ProductAttachmentMateriały wydawane po zakupie nigdy nie pojawią się w tym polu. Nie próbuj ich stamtąd czytać — wymagają uprawnienia wynikającego z opłaconego zamówienia.
Co klient kupił
digitalDownloads na pozycji zamówienia mówi, do czego kupujący ma prawo: nazwa pliku, rozmiar,
ile pobrań mu zostało (brak wartości oznacza brak ograniczenia, nie zero) i kiedy dostęp wygasa
(brak oznacza bezterminowo).
Wpis z ready: false znaczy, że plik jest jeszcze sprawdzany po stronie platformy — pokaż stan
oczekiwania, a nie błąd. Klient, który przed chwilą zapłacił, ma zobaczyć „przygotowujemy plik",
a nie pustą listę sugerującą, że niczego nie kupił.
OrderDigitalDownloadsqueryTreści cyfrowequery OrderDigitalDownloads($token: String!, $email: String)
Treści cyfrowe kupione w zamówieniu, po opaque tokenie zamówienia (strona po-zakupowa gościa). Osobne zapytanie z tego samego powodu co wyżej — lista zamówień nie musi rozwiązywać pobrań dla każdej pozycji.
GraphQL operation
query OrderDigitalDownloads($token: String!, $email: String) {
orderByToken(token: $token, email: $email) {
id
lineItems(first: 100) {
edges {
node {
id
title
digitalDownloads {
...DigitalDownload
}
}
}
}
}
}
DigitalDownloadSamego pliku nie da się stąd pobrać — odnośnik wydaje osobna mutacja, opisana w Mutacjach.
Powiązane sekcje
- Mutacje — zapis stanu (cart, checkout, customer auth, returns, reviews, wishlist)
- Types Reference — pełna referencja typów (objects / inputs / enums / scalars)
- Konwencje nazewnicze —
node/edge/cursorw Relay Connection, namingxxxByXxxdla pojedynczych encji - SDK — Storefront SDK — typed hooks i providery konsumujące te operacje