Klient miał Shopify Advanced za 280 EUR / miesiąc, 12 000 SKU, 4 magazyny, B2B + B2C dual pricing, 65 % organicznego traffic ze słowackiego i czeskiego Google. Headless migracja na Medusa.js skończyła się po 4 miesiącach, dwa tygodnie przed Black Friday. Żaden organic ranking nie został stracony, żadne niedokończone zamówienie nie zostało stracone, a integracja Stripe przeszła z 14 ręcznych kroków przy każdym nowym produkcie na zero. Niniejszy artykuł jest o tym, co robiliśmy, co spartoliliśmy i co zrobilibyśmy inaczej.
Dlaczego headless — i kiedy NIE
Headless commerce ma dwa ważne powody:
1. **Elastyczność custom checkout / B2B / multi-storefront.** Shopify (zarówno Advanced, jak i Plus) ma stałą pipeline checkout. Dla B2B z terminem płatności net-60, custom pricing tiers, multi-warehouse splitting lub complex tax rules (PL-DE-AT cross-border) Shopify staje się walką z limitami API i szablonami Liquid. 2. **Performance storefront + UX.** Storefront Next.js z ISR (Incremental Static Regeneration) + edge caching osiąga LCP < 1,2 s na 4G. Shopify Liquid-rendered storefront ma LCP 2,5–4,0 s. Dla biznesu SEO-driven to mierzalna różnica w przychodach.
**Kiedy NIE headless:** - Katalogi < 500 SKU + standardowy checkout B2C → Shopify lub BigCommerce pozostaje tańszy i szybszy - Zespół nie ma kapacity na własny storefront Next.js (custom CMS, custom checkout, integracje) - Roadmapa płatności/księgowości/marketingu jest cała Shopify-native (Shopify Payments, Shopify Tax, Shopify Email)
Dla tych klientów płynniejszy jest istniejący „semi-headless" — Shopify Hydrogen + Oxygen, który daje frontend headless storefront bez opuszczania ekosystemu Shopify.
Stack decyzji w 2026
Backend: Medusa.js vs Saleor vs Commerce.js
**Medusa.js v2.x** - Node.js, PostgreSQL, Redis - Modułowy, plugin architecture - Najlepszy developer experience w 2026 - Słabszy zestaw funkcji B2B out-of-the-box (potrzebny plugin lub własny kod dla quotes, approvals, customer hierarchy) - **Cena:** open source self-hosted lub Medusa Cloud od 200 EUR / miesiąc - **Kiedy:** B2C + light B2B, zespół dev z doświadczeniem Node
**Saleor** - Python, Django, GraphQL-first - Najsilniejszy zestaw funkcji B2B (customer groups, channel-based pricing, quote requests) - Multilanguage + multi-currency natywnie - Wyższa learning curve, narzut GraphQL - **Cena:** open source self-hosted lub Saleor Cloud od 500 EUR / miesiąc - **Kiedy:** B2B-first, multi-storefront, complex pricing
**Commerce.js / Swell.is** - Hosted-only, żadnego self-host - Szybki setup, ale vendor lock-in - **Kiedy:** PoC / MVP, mały katalog
Dla 12 k SKU retailera słowackiego z 30 % udziałem B2B wybraliśmy **Medusa.js v2** — własny hosting przez Hetzner Cloud (CCX23, 4 vCPU + 16 GB RAM, 35 EUR / miesiąc), PostgreSQL 16 na oddzielnym node, Redis dla cache + queue.
Frontend: Next.js 16 z App Router
- ISR (Incremental Static Regeneration) dla stron produktowych — revalidate every 60s
- Edge runtime dla /api routes (Cloudflare Workers lub Vercel Edge)
- Tailwind 4 + shadcn/ui dla szybkiego rozwoju UI
- Server Components dla product detail (szybki TTFB), Client Components dla cart / interactive elements
- Bundle size target: < 180 KB shipped JS dla home page
Usługi peryferyjne
| Funkcja | Wybór 2026 | Cena | |---------|------------|------| | Search | Algolia | 0,50 EUR / 1k search ops, ~180 EUR / miesiąc dla 12k SKU + 400k miesięcznych searches | | Płatności | Stripe (Standard + Connect dla B2B) | 1,4 % + 0,25 EUR per transakcja EU, B2B Connect 0,5 % flat | | Email / Marketing | Klaviyo | 90 EUR / miesiąc dla 10k profili | | Observability | Sentry + PostHog | Sentry 26 EUR / miesiąc, PostHog 0 EUR (free tier do 1M events) | | Image CDN | Cloudflare Images | 5 EUR / miesiąc + 1 EUR / 100k delivery | | DAM (Digital Asset Management) | Cloudinary lub self-hosted Imgproxy | 90 EUR / miesiąc lub 15 EUR self-hosted | | Tax | Avalara AvaTax (PL + EU OSS) | 250 EUR / miesiąc | | Shipping | InPost API + DHL API | 0 EUR (consumption-based) | | Reviews | Trustpilot Business | 200 EUR / miesiąc |
Całkowity OPEX: ~750 EUR / miesiąc (vs Shopify Advanced 280 EUR + nadmiarowe Shopify Apps 350 EUR = 630 EUR pierwotnie).
Plan migracji — 4 miesiące, 6 faz
Miesiąc 1: Audyt + przygotowanie danych
**Tydzień 1–2: Audyt katalogu** - Eksport Shopify product CSV + Metafields export - Mapping Shopify product schema → Medusa product schema (variants, options, prices, tax_rates) - Identyfikacja data quality issues (brakujące SEO descriptions, duplicate handles, broken images) - 12 000 SKU rozłożyło się na: 9 200 unique products × średnio 1,3 wariantu = 12 100 product_variants w Medusa
**Tydzień 3: Audyt URL dla 301 mapping** - Crawl wszystkich Shopify URL przez Screaming Frog → 14 800 unique URLs - Categorize: product pages (12 000), collection pages (240), CMS pages (45), pagination URLs (2 200), other (300) - Plan: zachować product handle (URL slug) → Medusa ten sam handle → 0 redirect potrzebny dla 11 800 URL - Plan: collection pages mapping → większość 1:1, ale Shopify smart collections nie mapują się na Medusa product categories bezpośrednio → 80 ręcznych mappings - Pagination URLs (?page=2) → Medusa ta sama struktura, naturalnie zachowane
**Tydzień 4: Eksport klientów + zamówień** - 18 400 customers z aktywnością z ostatnich 24 miesięcy → import do Medusa - 47 000 historycznych zamówień → migracja do read-only archiwum w Medusa (nie potrzebne w aktywnym systemie, ale potrzebne dla legal compliance + customer support) - B2B customer groups + pricing tiers (Bronze/Silver/Gold) → Medusa customer_group + price_list
Miesiąc 2: Build backendu + integracje
**Tydzień 5–6: Setup Medusa + custom moduły** - Setup Hetzner Cloud, Coolify dla Docker orchestration, GitHub Actions CI/CD - Medusa modules: product, order, customer, fulfillment, payment (Stripe), tax (Avalara plugin) - Custom moduły: - PL-specific tax handling (VAT 23 %, obniżony 8 %, 5 %, zwolnione) - PL-specific invoicing (wymaga NIP, REGON, daty wystawienia, daty dostawy, terminu płatności) - B2B quote request flow (custom Medusa workflow + admin UI)
**Tydzień 7–8: Integracje peryferyjne** - Algolia: products + variants index, faceting (kategoria, cena, marka, dostępność), synonyms (PL + CZ) - Klaviyo: customer + order webhook → Klaviyo events (cart_abandoned, ordered_product, etc.) - Avalara: order_placed webhook → AvaTax calculation → store tax breakdown na order - Sentry: backend + frontend error tracking, source maps upload
Miesiąc 3: Build frontend + ciągłość SEO
**Tydzień 9–10: Storefront core pages** - Setup Next.js 16, Tailwind 4, shadcn/ui - Home, Category, Product Detail, Cart, Checkout (multi-step), Account, Order History - ISR dla Product + Category (revalidate 60s) - Server Components default, Client Components tylko dla cart/wishlist state
**Tydzień 11: Migracja SEO** - **Sitemap continuity:** Next.js sitemap.xml generuje te same URL co Shopify, plus nowe URL, jeśli powstały - **301 redirect map:** /apps/customer-portal/* → /account/*, /collections/ → /c/* (zmieniliśmy strukturę url kategorii) - **Structured data:** Product schema (price, availability, sku, brand, aggregateRating, review), Offer schema, BreadcrumbList, Organization - **Open Graph + Twitter Card:** og:image generowany przez Next.js dynamic OG image route (1200×630, branding, product hero, cena) - **robots.txt + meta robots:** zachować noindex dla /account/*, /cart, /checkout - **Canonical URLs:** explicit canonical na każdą stronę produktową, powód: Shopify używał go z parametrem ?variant=, Medusa robi to przez separate URL lub client-side state, potrzeba canonical na base product URL
**Tydzień 12: Plan cutover** - DNS cutover plan (TTL 60s 24h przed cutoverem) - Database snapshot Shopify → Medusa import (last delta) - Stripe webhook switch (Shopify Stripe → Medusa Stripe) - Klaviyo webhook switch - Powiadomienie klientów: „w sobotę nocą od 02:00 do 04:00 e-sklep niedostępny"
Miesiąc 4: Cutover + stabilizacja
**Tydzień 13: Soft launch** - Subdomena shop-new.domain.pl → Next.js storefront + Medusa backend - 5 % traffic routowane przez Cloudflare Workers split test - Sentry watch: zero new errors przez 48 h - Klaviyo monitoring: cart abandonment rate stable
**Tydzień 14: Pełny cutover** - DNS cutover (apex domain → new infra) - 301 redirect map wdrożony na Cloudflare Workers (pre-DNS-cutover już running) - Shopify pozostaje w read-only mode przez 30 dni (legal archive of past orders) - Customer support ticket spike monitor
**Tydzień 15: Post-launch fix** - 12 godzin bug fixing w pierwszych 72 h (żaden critical, ale 8 minor — checkout edge cases, B2B quote workflow, kilka issues z Algolia synonym)
**Tydzień 16: Performance tuning + SEO check** - Audit Lighthouse, LCP target < 1,5 s (osiągnięte 1,1 s) - Search Console: indexing status check, żadnych nowych 404 errors, żadnego rank dropu ponad 5 % na top 50 keywords - A/B test new product detail page vs Shopify-style → +14 % conversion rate
Co nas raniło (i co zrobilibyśmy inaczej)
Wolno: SK/PL-specific invoicing
**Plan:** 4 dni rozwoju na custom invoicing module.
**Realność:** 3 tygodnie.
Polskie wymaganie ustawowe na fakturę zawiera kilkanaście obowiązkowych pól (Ustawa o VAT art. 106e), plus electronic invoicing dla B2B powyżej określonej kwoty ma własne EDI requirements (KSeF — Krajowy System e-Faktur od 2026). Plus VAT OSS dla cross-border EU sales potrzebuje per-transaction reporting do KAS. Plus JPK_V7 (Jednolity Plik Kontrolny VAT) ma własne wymagania struktury.
**Lekcja:** szacować PL/EU compliance overhead na 3× pierwotny szacunek. Żadna funkcja EU compliance nie jest „2-day implementation".
Szybko: Algolia sync
**Plan:** 5 dni na Algolia integration + indexing.
**Realność:** 2 dni.
Medusa ma first-party Algolia plugin (`@medusajs/algolia`), który emituje webhook events (product.created, product.updated, product.deleted) → Algolia reindex. Setup to była konfiguracja API kluczy + index schema. Realnie 2 dni.
**Lekcja:** najpierw szukać first-party / community plugin. Jeśli istnieje i ma 1k+ GitHub stars, nie zaczynać custom integration.
Wolno: B2B quote workflow
**Plan:** 1 tydzień.
**Realność:** 3 tygodnie.
B2B customer chce „quote request" dla zamówienia powyżej 5 000 EUR. Workflow: 1. Customer dodaje produkty do koszyka → klika „Wnioskuj o wycenę" (zamiast „Dodaj do koszyka") 2. Quote powstaje w panelu admin, sales rep zatwierdza, ewentualnie modyfikuje cenę / shipping / payment terms 3. Customer dostaje email z linkiem na quote → może accept (utworzy order z dostosowanymi conditions) lub reject
Medusa v2 ma `quote` module w wersji alpha w 2026, ale workflow + UI musieliśmy zbudować custom. Plus admin permissions dla różnych sales repów. Plus email templates w PL.
**Lekcja:** B2B-specific workflows w headless commerce zawsze są custom. Saleor by tu może zaoszczędził 1 tydzień (ma built-in quote module w stable), ale Medusa decision był uzasadniony developer experience dla reszty stacka.
Szybko: zachowanie SEO przez 301 redirect
**Plan:** 1 tydzień na 301 mapping + monitoring.
**Realność:** 3 dni.
Klucz: zachować product handle (URL slug) z Shopify do Medusa. Shopify URL był `/products/produkt-handle`, Medusa ten sam pattern. 11 800 z 14 800 URL pozostało identycznych — żaden redirect potrzebny.
Dla reszty (collections, customer portal, kilka CMS pages) wdrożyliśmy 301 map na Cloudflare Workers (`URL Rewrite Rules`) — 280 explicitnych reguł + 12 regex patterns. **Zero indexed URL straconych** w Search Console po 60 dniach.
**Lekcja:** jeśli mogą Państwo zachować URL pattern, zróbcie to. Jeśli musicie zmieniać, zbudujcie redirect map *przed* cutoverem, nie po.
SEO checklist — co zweryfikować przed cutoverem
1. **Sitemap parity:** Next.js sitemap.xml musi zawierać wszystkie URL z pierwotnej Shopify sitemap. Diff przez skrypt `sitemap-diff` w CI. 2. **301 redirect map:** każda non-trivial zmiana URL zaindeksowana w `redirects.json`, validated przeciw production load testom. 3. **Canonical URLs:** explicit canonical na każdą stronę, powód: Shopify używał query params (?variant=), nowy stack może używać path-based URLs — canonical zapobiegnie penalizacji za duplicate content. 4. **Structured data continuity:** Product schema (offers, availability, price, sku) parytet z pierwotnym Shopify. Test przez Google Rich Results Test przed cutoverem. 5. **Open Graph migration:** og:image, og:title, og:description per page. Dynamic OG image generation przez Next.js dynamic route + edge runtime. 6. **robots.txt:** zachować noindex dla admin, account, cart, checkout. Dodać directive `Sitemap:`. 7. **Internal linking:** breadcrumbs, related products, category navigation. Spider crawl przez Screaming Frog dla potwierdzenia, że żadna strona nie jest orphan. 8. **Core Web Vitals:** LCP, FID/INP, CLS target dla top 50 landing pages. Lighthouse CI w deployment pipeline. 9. **Search Console reverification:** new property dla new infra (jeśli DNS się nie zmienia, żadna nowa verification needed, ale URL property się rekomenduje). 10. **Monitoring:** 30-dniowy post-launch watch dla indexing status, ranking changes na top 100 keywords, delta organic traffic.
ROI cutoff — kiedy migracja jest tego warta
Headless migracja tej skali kosztuje **40–80 k EUR** w nakładach engineering (łącznie z internal time + external dev shop), plus 4 miesiące timeline.
**Zwrot przez 3 lata:**
| Korzyść | Oszczędność roczna | |---------|--------------------| | Shopify Advanced + Apps savings | 4 200 EUR | | Poprawa LCP → +6 % conversion | 12k SKU × 20 EUR AOV × 1,2k orders/mies × 0,06 = ~17 280 EUR | | Eliminated Shopify Plus upgrade (would be next, 25k+ USD / year) | 23 000 EUR | | Faster B2B quote handling | 12 000 EUR (1 FTE dzień × 24 miesiące) | | **Razem oszczędności 3 lata** | **170 940 EUR** |
ROI: ~2,5 roku przy inwestycji 60 k EUR.
**Dla kogo się NIE zwróci:** - < 2 000 SKU, < 500 orders / miesiąc → Shopify pozostaje tańszy - Nie mają Państwo zespołu dev lub committed external partner dla 3-letniej konserwacji - Roadmapa nie mówi o B2B / multi-storefront / custom checkout
Praktyczna rada
Migracja to nie „kupimy Medusa, programiści to zrobią". To **projekt business + tech + SEO + ops** na 3–6 miesięcy. Największe niepowodzenia, które widzieliśmy:
1. **Underestimated EU compliance.** PL invoicing, OSS VAT, GDPR — wszystko zżera więcej czasu niż obiecuje PoC z danymi testowymi. 2. **No 301 plan = lost rankings.** 90 dni strat w organic traffic = 30–50 % straty przychodów. Wystarczy jeden zły cutover. 3. **No customer support runbook.** Cutover bez 24/7 incident planu = paniczny restart na Shopify po pierwszym kryzysie. 4. **Underestimated data migration.** 47k historycznych zamówień to nie „CSV import za weekend".
---
*Wykonujemy migracje headless commerce dla katalogów 2k–50k SKU, full-stack (Medusa.js / Saleor + Next.js + integracje + SEO continuity). Jeśli rozważają Państwo opuszczenie Shopify lub BigCommerce, pierwszy project assessment (4-godzinny warsztat) przejdzie stack decyzji, timeline, budżet i 301 redirect plan dla Państwa konkretnego katalogu zanim zobowiążą się do migracji.*