[ BLOG // 2026-07-26 // 3 MIN ]

Produktové feedy: jak dostat katalog cykloservisu na srovnávače zboží

Proč nestačí mít e-shop — a jak jeden generovaný XML feed dostane katalog malého cykloservisu na nákupní srovnávače, dvakrát denně a bez ruční práce.

PROJEKTY nextjseshopfeedmedusacase-study

Postavit e-shop je dneska ta jednodušší část. Horší je dostat ho před lidi. V Česku velká část nákupů začíná na srovnávačích zboží — zákazník nehledá konkrétní obchod, zadá „brzdové destičky Shimano” a čeká, co mu vypadne. Aby se tam produkty z obchodu vůbec objevily, potřebuje e-shop produktový feed: strojově čitelný seznam celého katalogu, který si srovnávač pravidelně stahuje.

Pro Opravnu Bicyklů, cykloservis v Liberci s e-shopem postaveným na Next.js a Medusa.js, jsem udělal přesně tohle. Jeden XML feed, generovaný automaticky z živého katalogu, který drží krok se skladem a cenami.

Formát, který srovnávače znají

Feed je XML ve formátu, který používá Google Merchant Center a který v té či oné podobě konzumují srovnávače i nákupní platformy. Každá varianta produktu je jedna položka:

<item>
  <g:id>SKU-123</g:id>
  <g:item_group_id>PROD-45</g:item_group_id>
  <g:title>Brzdové destičky Shimano - metalické</g:title>
  <g:link>https://…/shop/brzdove-desticky-shimano</g:link>
  <g:image_link>https://…/img.jpg</g:image_link>
  <g:availability>in_stock</g:availability>
  <g:price>899.00 CZK</g:price>
  <g:condition>new</g:condition>
  <g:brand>Shimano</g:brand>
  <g:mpn>SKU-123</g:mpn>
</item>

Důležitý je item_group_id — říká, že několik položek (třeba různé barvy nebo velikosti) patří k jednomu produktu. Srovnávač pak nabídne varianty pohromadě, místo aby je rozházel jako samostatné zboží. A availability se nevyplňuje ručně: čte se ze skutečného stavu skladu, takže když se poslední kus vyprodá, feed to řekne srovnávači dřív, než zákazník stihne objednat vzduch.

Tři vrstvy, každá dělá jednu věc

Generování feedu je rozdělené na tři na sobě nezávislé části. Žádný velký skript, který všechno míchá dohromady.

  • Repository vytáhne z katalogu produkty, varianty, ceny a sklad. Nic víc.
  • Mapper je čistá funkce: vezme surová data a přemapuje je na položky feedu. Tady se rozhoduje, co je platné a co ne.
  • Generate z položek složí XML, ošetří speciální znaky, doplní absolutní URL a zapíše soubor.

Mapper je čistá funkce bez vedlejších efektů. Dá se otestovat na stole — žádné databáze, žádné soubory, jen vstup a výstup.

Právě mapper řeší tu špinavou práci, která rozhoduje o kvalitě feedu:

  • Přeskočí produkt bez obrázku, názvu nebo odkazu — srovnávač by ho stejně odmítl.
  • Přeskočí variantu bez platné ceny (nula nebo nesmysl se do feedu nesmí).
  • Složí název varianty do názvu produktu, ale jen když dává smysl — interní názvy typu „default” nebo „výchozí” zahodí.
  • Na konci vrátí i statistiku: kolik produktů a variant viděl, kolik jich přeskočil a proč. Když feed najednou zeštíhlí, je hned jasné kde.

Dvakrát denně, bez souběhu

Feed se negeneruje při každé změně — to by bylo zbytečné. Běží plánovač, který ho přegeneruje dvakrát denně. Aby se při nasazení ve více instancích nestalo, že dvě instance zapisují do stejného souboru zároveň, hlídá generování sdílený zámek (Redis, s fallbackem do paměti, když Redis není). Jedna instance generuje, ostatní počkají.

Proč je to zajímavé

Produktový feed zní jako rutina — „vyexportuj katalog do XML”. Jenže v detailech je rozdíl mezi feedem, který srovnávač spolkne, a feedem, který produkty tiše zahodí. Správné seskupení variant, dostupnost ze živého skladu, ošetření XML znaků a přeskočení nevalidních dat — to jsou věci, které rozhodují o tom, jestli se obchod na srovnávači reálně objeví.

Pro malý obchod je to navíc násobič: jednou postavený feed znamená, že katalog běží na srovnávačích sám, bez ručního exportu pokaždé, když se něco změní.

// ALL_POSTS
ZPĚT NA BLOG