Przejdź do głównej zawartości

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łówekWymaganyOpis
X-Shop-SlugTakIdentyfikator sklepu (slug), np. moj-sklep. Alternatywa: custom subdomena (moj-sklep.doswiftly.pl).
X-Preferred-CurrencyNieKod waluty (ISO 4217), np. EUR, USD. Jeśli nie podany, używana jest waluta bazowa sklepu.
X-LanguageNieKod języka (ISO 639-1), np. pl, en. Wsparcie w przygotowaniu.
AuthorizationNie*Bearer <token> — token dostępu klienta (JWT). Dla storefrontów przeglądarkowych alternatywą jest httpOnly cookie customerAccessToken (ustawiane przez BFF helpers SDK).
Content-TypeTakapplication/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.countryisoCode: 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.):

PoleTypOpis
messageString!Czytelny komunikat błędu
codeCartErrorCode!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:

PoleTypOpis
messageString!Komunikat ostrzeżenia
codeCartWarningCode!MERCHANDISE_NOT_AVAILABLE, MERCHANDISE_NOT_ENOUGH_STOCK, PAYMENTS_AMOUNT_REGION_MISMATCH
targetString!ID linii koszyka lub ścieżka pola którego dotyczy ostrzeżenie

CustomerUserError

Typ błędu dla mutacji klienta (customerCreate, customerUpdate, customerAddressCreate itp.):

PoleTypOpis
messageString!Czytelny komunikat błędu
codeCustomerErrorCode!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.):

PoleTypOpis
messageString!Czytelny komunikat błędu
codeStringKod błędu (np. INVALID, BLANK)
fieldStringŚcieżka do pola, które wywołało błąd

Paginacja

API używa paginacji kursorkowej (cursor-based). Zapytania paginowane przyjmują parametry:

ParametrTypOpis
firstIntLiczba elementów do pobrania (domyślnie 20, max 100)
afterStringKursor 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", nie 2999)
Język referencji typów

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ć:

  1. Traktuj ostrzeżenie o przestarzałym polu jako sygnał do migracji — przejdź na wskazane pole zastępcze przed podanym terminem.
  2. 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).
  3. 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 w codegen.ts
  • queries.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;
Asystenci AI w storefront repo

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