Dokumentacja API
DoSwiftly Storefront API to GraphQL API oparte na sprawdzonej, szeroko stosowanej konwencji Storefront API. Umożliwia budowanie niestandardowych witryn sklepowych, aplikacji mobilnych i integracji z zewnętrznymi systemami.
Endpoint
POST https://<api-url>/storefront/graphql
Wszystkie zapytania i mutacje wysyłane są jako zapytania POST z ciałem JSON zawierającym pole query i opcjonalnie variables.
Wymagane nagłówki
| Nagłówek | Wymagany | Opis |
|---|---|---|
X-Shop-Slug | Tak | Identyfikator sklepu (slug), np. moj-sklep. Alternatywa: custom subdomena (moj-sklep.doswiftly.pl). |
X-Preferred-Currency | Nie | Kod waluty (ISO 4217), np. EUR, USD. Jeśli nie podany, używana jest waluta bazowa sklepu. |
X-Language | Nie | Kod języka (ISO 639-1), np. pl, en. Wsparcie w przygotowaniu. |
Authorization | Nie* | Bearer <token> — token dostępu klienta (JWT). Dla storefrontów przeglądarkowych alternatywą jest httpOnly cookie customerAccessToken (ustawiane przez BFF helpers SDK). |
Content-Type | Tak | application/json |
*Autoryzacja jest wymagana dla zapytań dotyczących profilu klienta, zamówień, przesyłek, zwrotów oraz programu lojalnościowego.
extensions.context — echo kontekstu
Każda odpowiedź GraphQL zawiera extensions.context z zastosowanym kontekstem. Klient powinien weryfikować tę sekcję po round-tripie, aby wykryć niezgodności (np. CDN/cache staleness):
{
"data": { "shop": { "name": "Moj Sklep" } },
"extensions": {
"context": {
"country": "PL",
"language": "pl",
"currency": "EUR",
"baseCurrency": "PLN",
"source": {
"currency": "header",
"language": "default",
"country": "default"
}
}
}
}
Pole source pokazuje skąd każdy wymiar został rozwiązany: directive / header / cookie / auto / default. Hierarchia priorytetów:
@inContext directive > request header > cookie > auto-detect (Accept-Language) > shop default
Kody krajów (CountryCode)
Enum CountryCode obejmuje pełną listę kodów ISO 3166-1 alpha-2 (np. PL, DE, UA, US) oraz XK (Kosowo). Nie jest to lista rynków sklepu — adres klienta czy kraj odwiedzającego może wskazywać dowolny kraj. Traktuj enum jako otwarty: kod obsługujący jego wartości (np. switch) powinien mieć gałąź domyślną na wartości, których nie znasz.
Specjalna wartość ZZ oznacza „kraj zapisany, ale nierozpoznany" — np. starszy adres, w którym zamiast kodu zapisano nazwę kraju. MailingAddress.countryCode ma wtedy wartość ZZ, a oryginalny tekst zostaje w polu country. ZZ występuje wyłącznie w odpowiedziach: nie wysyłaj go jako kraju adresu — poproś kupującego o wybór kraju. null oznacza, że kraju w ogóle nie podano.
Gdzie sklep wysyła, wynika ze stref wysyłki skonfigurowanych przez sprzedawcę, a nie z enuma. Listę krajów dostawy zwracają shop.shipsToCountries i localization.availableCountries — z nich buduj wybór kraju dostawy w kasie. Kraje objęte sankcjami (Rosja, Białoruś, Korea Północna) nigdy nie trafiają na te listy, a adres dostawy w takim kraju nie dostaje żadnej metody wysyłki (userErrors[{ code: 'NO_SHIPPING_METHODS' }]), więc zamówienia z dostawą tam nie da się złożyć. Adres klienta w tych krajach (np. w książce adresowej) pozostaje dozwolony.
Kraj w kontekście żądania (extensions.context.country, localization.country) pochodzi kolejno z: argumentu country dyrektywy @inContext (wartość w cudzysłowie, np. @inContext(country: "DE")), geolokalizacji żądania i kraju domyślnego sklepu. Wartość, która nie jest kodem kraju (np. "Polska"), jest pomijana i rozstrzyga następne źródło. Gdy żadne źródło nie da kraju, extensions.context.country ma wartość null, a localization.country — isoCode: ZZ z walutą sklepu. Przy renderowaniu po stronie serwera żądanie do API wysyła Twój serwer, nie przeglądarka kupującego — jeśli znasz kraj kupującego, przekaż go jawnie przez @inContext(country: …).
Przykładowe zapytanie
curl -X POST https://<api-url>/storefront/graphql \
-H "Content-Type: application/json" \
-H "X-Shop-Slug: moj-sklep" \
-H "X-Preferred-Currency: PLN" \
-d '{
"query": "query { shop { name currencyCode } }"
}'
Autoryzacja klienta
Aby uzyskać token dostępu klienta, użyj mutacji customerLogin z danymi logowania (email + hasło). Zwrócony accessToken przesyłaj w nagłówku Authorization: Bearer — albo (dla storefrontów przeglądarkowych) zapisz go w httpOnly cookie customerAccessToken przez BFF helpers SDK (createSetTokenHandler):
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Token ma datę wygaśnięcia (expiresAt). Odnów go mutacją customerRefreshToken (czyta tożsamość z bieżącego cookie / nagłówka Bearer, nie przyjmuje argumentu).
Obsługa błędów
Mutacje zwracają błędy w polu userErrors lub customerUserErrors — zależnie od domeny. Dodatkowo mutacje koszyka mogą zwrócić pole warnings z ostrzeżeniami nieblokującymi.
{
result {
# ... dane wyniku
}
# Koszyk: userErrors + warnings
userErrors { message code field }
warnings { message code target }
# Klient: customerUserErrors (zamiast userErrors)
customerUserErrors { message code field }
}
CartUserError
Typ błędu dla mutacji koszyka (cartCreate, cartAddLines, cartUpdateLines itp.):
| Pole | Typ | Opis |
|---|---|---|
message | String! | Czytelny komunikat błędu |
code | CartErrorCode! | Typed kod (np. NOT_ENOUGH_IN_STOCK, INVALID_MERCHANDISE_LINE) |
field | [String!] | Ścieżka do pola, które wywołało błąd |
CartErrorCode zawiera ~30 wartości, w tym rozszerzenia DoSwiftly: ATTRIBUTE_REQUIRED, ATTRIBUTE_OPTION_INVALID, BUNDLE_ONLY_NOT_DIRECTLY_PURCHASABLE.
CartWarning
Ostrzeżenie nieblokujące — koszyk jest zmodyfikowany, ale operacja się powiodła:
| Pole | Typ | Opis |
|---|---|---|
message | String! | Komunikat ostrzeżenia |
code | CartWarningCode! | MERCHANDISE_NOT_AVAILABLE, MERCHANDISE_NOT_ENOUGH_STOCK, PAYMENTS_AMOUNT_REGION_MISMATCH |
target | String! | ID linii koszyka lub ścieżka pola którego dotyczy ostrzeżenie |
CustomerUserError
Typ błędu dla mutacji klienta (customerCreate, customerUpdate, customerAddressCreate itp.):
| Pole | Typ | Opis |
|---|---|---|
message | String! | Czytelny komunikat błędu |
code | CustomerErrorCode! | Typed kod (np. INVALID_CREDENTIALS, TOKEN_INVALID, TAKEN) |
field | [String!] | Ścieżka do pola, które wywołało błąd |
CustomerErrorCode zawiera ~22 wartości zgodne z powszechną konwencją błędów Storefront API (np. ALREADY_ENABLED, BAD_DOMAIN, CUSTOMER_DISABLED) oraz rozszerzenia DoSwiftly (TOKEN_EXPIRED, ADDRESS_CREATE_FAILED).
UserError
Ogólny typ błędu dla pozostałych mutacji (wysyłka, karty podarunkowe, zwroty itp.):
| Pole | Typ | Opis |
|---|---|---|
message | String! | Czytelny komunikat błędu |
code | String | Kod błędu (np. INVALID, BLANK) |
field | String | Ścieżka do pola, które wywołało błąd |
Paginacja
API używa paginacji kursorkowej (cursor-based). Zapytania paginowane przyjmują parametry:
| Parametr | Typ | Opis |
|---|---|---|
first | Int | Liczba elementów do pobrania (domyślnie 20, max 100) |
after | String | Kursor po którym zacząć pobieranie |
Odpowiedź paginowana zawiera:
{
edges {
node { ... }
cursor
}
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
totalCount
}
Przykład paginacji
query Products($after: String) {
products(first: 10, after: $after) {
edges {
node {
id
title
}
cursor
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
}
Aby pobrać następną stronę, przekaż wartość endCursor jako zmienną $after.
Ceny i waluty
Ceny w API są reprezentowane przez dwa typy. Pola domyślne używają Money (waluta bazowa sklepu). Pełna transparentność konwersji dostępna przez opt-in pola *WithConversion zwracające PriceMoney.
Money
Domyślny typ cenowy — waluta bazowa sklepu. Wszystkie pola cenowe na ProductVariant, CartCost, CartLineCost zwracają Money:
type Money {
amount: String! # Kwota jako string (np. "29.99")
currencyCode: String! # Kod waluty ISO 4217 (np. "PLN")
}
PriceMoney (opt-in)
Rozszerzony typ cenowy z informacjami o konwersji walutowej. Dostępny przez pola *WithConversion (priceWithConversion, subtotalAmountWithConversion itp.) — nie jest to domyślny typ cenowy:
type PriceMoney {
amount: String! # Kwota w walucie klienta
currencyCode: String! # Waluta klienta
baseAmount: String! # Kwota w walucie bazowej sklepu
baseCurrencyCode: String! # Waluta bazowa sklepu
exchangeRate: Float # Kurs wymiany
marginApplied: Float # Marża zastosowana przez sklep (np. 0.02 = 2%)
rateTimestamp: DateTime # Timestamp kursu
isConverted: Boolean! # Czy cena została przeliczona
}
Wzorzec opt-in dla currency converter UI:
query ProductWithConversion($id: ID!) {
product(id: $id) {
variants {
price { amount currencyCode } # domyślne — waluta sklepu
priceWithConversion { # opt-in — pełna transparentność
amount currencyCode baseAmount baseCurrencyCode exchangeRate isConverted
}
}
}
}
Jeśli nagłówek X-Preferred-Currency nie jest podany lub jest równy walucie bazowej sklepu, pole isConverted zwraca false, a amount i baseAmount są identyczne.
Konwencje
- ID -- wszystkie identyfikatory są typu
ID(UUID) - Daty -- format ISO 8601 (np.
2026-01-28T12:00:00.000Z) - Handle/Slug -- przyjazne URL identyfikatory tekstowe (np.
koszulka-premium) - Kwoty -- zawsze jako string w jednostkach głównych (np.
"29.99", nie2999)
Przewodniki w tym portalu są po polsku, ale auto-generowana referencja typów — oraz opisy pól w pakiecie @doswiftly/storefront-operations i podpowiedziach IDE — używa angielskich opisów. Pochodzą one wprost z opublikowanego schematu GraphQL (schema.graphql), wspólnego dla globalnej publiczności npm. To świadoma decyzja: jedno źródło prawdy dla opisów pól, identyczne w IDE, w pakiecie npm i w tej referencji.
Stabilność API i przestarzałe pola
Storefront API rozwija się addytywnie — nowe pola, typy i opcjonalne argumenty dochodzą bez psucia istniejących zapytań. Twój storefront nie wymaga zmian, gdy API zyskuje nowe możliwości.
Gdy jakieś pole ma zostać wycofane, najpierw zostaje oznaczone w schemacie jako przestarzałe (@deprecated):
- Codegen i podpowiedzi IDE pokażą ostrzeżenie przy każdym użyciu przestarzałego pola.
- Opis przestarzałego pola wskazuje, czym je zastąpić oraz orientacyjny termin usunięcia.
- Przestarzałe pole nadal zwraca dane przez okres przejściowy — nie znika z dnia na dzień, masz czas na migrację.
Jak reagować:
- Traktuj ostrzeżenie o przestarzałym polu jako sygnał do migracji — przejdź na wskazane pole zastępcze przed podanym terminem.
- Usunięcie pola pojawia się jako zmiana MAJOR pakietu
@doswiftly/storefront-operations. Aktualizując do takiej wersji, zapoznaj się z changelogiem i przebuduj projekt (pnpm codegen). - Nie ignoruj ostrzeżeń deprecacji — pole działa w okresie przejściowym, ale po jego upływie i wydaniu wersji MAJOR zapytania używające usuniętego pola przestaną być akceptowane.
Pre-built operations: pakiet npm
Zamiast pisać zapytania od zera, możesz zainstalować @doswiftly/storefront-operations — paczka npm z gotowym schema.graphql i nazwanymi operacjami (queries, mutations, fragments) odpowiadającymi 1-do-1 niniejszemu API. Pakiet pozwala uruchomić codegen lokalnie bez kontaktu z backendem.
pnpm add @doswiftly/storefront-operations
Pakiet zawiera:
schema.graphql— pełny schemat GraphQL używany wcodegen.tsqueries.graphql/mutations.graphql/fragments.graphql— gotowe nazwane operacje (Relay-style)operations.json— te same operacje jako strukturalny JSON (dla narzędzi MCP / programmatic LLM)AGENTS.md+llms-full.txt— pełny opis API dla agentów AI (Cursor, Claude Code, Copilot)
Konfiguracja codegen dla projektu konsumenta:
// codegen.ts
import { CodegenConfig } from '@graphql-codegen/cli';
const config: CodegenConfig = {
schema: 'node_modules/@doswiftly/storefront-operations/schema.graphql',
documents: [
'node_modules/@doswiftly/storefront-operations/{queries,mutations,fragments}.graphql',
'src/**/*.graphql',
],
generates: {
'src/generated/graphql.ts': {
plugins: ['typescript', 'typescript-operations', 'typed-document-node'],
},
},
};
export default config;
Jeśli używasz Cursor / Claude Code / GitHub Copilot do budowania storefrontu, agent automatycznie wczytuje node_modules/@doswiftly/storefront-operations/AGENTS.md (entry point z konwencjami) i llms-full.txt (pełny katalog operacji z opisami i body GraphQL). Nie musisz nic konfigurować — masz zero-shot kontekst całego API zamiast halucynacji nazw.
Pakiet jest synchronizowany z backendem przez automatyczny pipeline (pre-commit hook + GitHub Actions guard) — wystarczy bumpnąć wersję i pnpm codegen w swoim projekcie.
Następne kroki
- Zapytania — Use-case guide po polsku z auto-gen reference
- Mutacje — Use-case guide po polsku z auto-gen reference
- Typy GraphQL — Auto-generowana referencja typów (objects/inputs/enums/scalars)
- Konwencje nazewnictwa