{"id":2725,"date":"2026-07-21T18:00:32","date_gmt":"2026-07-21T18:00:32","guid":{"rendered":"https:\/\/news.jurskitech.pl\/blog\/uncategorized\/czy-twoj-saas-traci-uzytkownikow-przez-powolne-api-3-bledy-i-naprawa\/"},"modified":"2026-07-21T18:00:32","modified_gmt":"2026-07-21T18:00:32","slug":"czy-twoj-saas-traci-uzytkownikow-przez-powolne-api-3-bledy-i-naprawa","status":"publish","type":"post","link":"https:\/\/news.jurskitech.pl\/blog\/warto-wiedziec\/czy-twoj-saas-traci-uzytkownikow-przez-powolne-api-3-bledy-i-naprawa\/","title":{"rendered":"Czy Tw\u00f3j SaaS traci u\u017cytkownik\u00f3w przez powolne API? 3 b\u0142\u0119dy i naprawa"},"content":{"rendered":"<h2 id=\"wstp\">Wst\u0119p<\/h2>\n<p>Prowadzisz SaaS i wydaje Ci si\u0119, \u017ce wszystko dzia\u0142a? U\u017cytkownicy si\u0119 loguj\u0105, ale po tygodniu odchodz\u0105? Cz\u0119sto winowajc\u0105 jest nie frontend, nie serwer, a API \u2013 cichy zab\u00f3jca konwersji. W praktyce JurskiTech widzieli\u015bmy SaaSy, kt\u00f3re traci\u0142y 40% u\u017cytkownik\u00f3w tylko przez op\u00f3\u017anienia w odpowiedziach API. W tym artykule poka\u017c\u0119 3 b\u0142\u0119dy, kt\u00f3re pope\u0142niaj\u0105 nawet do\u015bwiadczeni CTO, i jak je naprawi\u0107, aby Twoja aplikacja dzia\u0142a\u0142a jak burza.<\/p>\n<h2 id=\"1zbytwielezapytaproblemn1\">1. Zbyt wiele zapyta\u0144 \u2013 problem N+1<\/h2>\n<h3 id=\"cototakiego\">Co to takiego?<\/h3>\n<p>B\u0142\u0105d N+1 to klasyk \u2013 pobierasz list\u0119 u\u017cytkownik\u00f3w (1 zapytanie), a potem dla ka\u017cdego z nich wykonujesz osobne zapytanie o szczeg\u00f3\u0142y (N zapyta\u0144). Efekt? 100 u\u017cytkownik\u00f3w = 101 zapyta\u0144, a dla 1000 \u2013 1001. To zabija czas odpowiedzi.<\/p>\n<h3 id=\"przykad\">Przyk\u0142ad<\/h3>\n<p>Widzieli\u015bmy aplikacj\u0119 CRM, kt\u00f3ra \u0142adowa\u0142a dashboard przez 8 sekund. Przyczyna? Dashboard pobiera\u0142 list\u0119 klient\u00f3w, a nast\u0119pnie dla ka\u017cdego klienta \u2013 jego zam\u00f3wienia, notatki, aktywno\u015b\u0107. Wystarczy\u0142o eager loading i jedno zapytanie JOIN, aby skr\u00f3ci\u0107 czas do 0,5 sekundy.<\/p>\n<h3 id=\"rozwizanie\">Rozwi\u0105zanie<\/h3>\n<p>U\u017cyj eager loading (np. w ActiveRecord: <code>.includes(:orders)<\/code>), optymalizuj zapytania przez JOIN lub GraphQL, kt\u00f3ry pozwala pobra\u0107 dok\u0142adnie to, co potrzebne w jednym \u017c\u0105daniu.<\/p>\n<h2 id=\"2brakpaginacjiifiltrowaniapostronieapi\">2. Brak paginacji i filtrowania po stronie API<\/h2>\n<h3 id=\"problem\">Problem<\/h3>\n<p>Endpoint zwraca wszystkie rekordy naraz \u2013 nawet je\u015bli frontend wy\u015bwietla tylko 10. W przypadku 10 000 rekord\u00f3w API wysy\u0142a megabajty danych, kt\u00f3re s\u0105 ignorowane. To marnuje przepustowo\u015b\u0107 i wyd\u0142u\u017ca czas odpowiedzi.<\/p>\n<h3 id=\"przykad-1\">Przyk\u0142ad<\/h3>\n<p>Startup fintechowy mia\u0142 endpoint <code>GET \/transactions<\/code>, kt\u00f3ry zwraca\u0142 wszystkie transakcje u\u017cytkownika (nawet 200 000). \u0141adowanie zajmowa\u0142o 10 sekund. Dodanie parametr\u00f3w <code>limit<\/code> i <code>offset<\/code> skr\u00f3ci\u0142o czas do 200 ms.<\/p>\n<h3 id=\"rozwizanie-1\">Rozwi\u0105zanie<\/h3>\n<p>Zaimplementuj paginacj\u0119 (cursor lub offset), filtrowanie po stronie API i sortowanie. Niech baza danych robi ci\u0119\u017ck\u0105 prac\u0119, a nie frontend.<\/p>\n<h2 id=\"3brakcacheowaniaczstodanychdanych\">3. Brak cache\u2019owania cz\u0119sto \u017c\u0105danych danych<\/h2>\n<h3 id=\"dlaczegotobd\">Dlaczego to b\u0142\u0105d?<\/h3>\n<p>Ka\u017cde zapytanie do bazy to koszt. Je\u015bli 1000 u\u017cytkownik\u00f3w pyta o list\u0119 produkt\u00f3w, a te zmieniaj\u0105 si\u0119 co godzin\u0119, to mo\u017cna cache\u2019owa\u0107 odpowied\u017a przez 5 minut. Bez tego baza jest przeci\u0105\u017cona, a API wolne.<\/p>\n<h3 id=\"przykad-2\">Przyk\u0142ad<\/h3>\n<p>Platforma e-learningowa mia\u0142a list\u0119 kurs\u00f3w, kt\u00f3ra zmienia\u0142a si\u0119 rzadko. Brak cache\u2019owania powodowa\u0142, \u017ce przy 5000 zapyta\u0144 na minut\u0119 baza osi\u0105ga\u0142a 100% CPU. Wdro\u017cenie Redis cache skr\u00f3ci\u0142o czas odpowiedzi z 2 s do 20 ms.<\/p>\n<h3 id=\"rozwizanie-2\">Rozwi\u0105zanie<\/h3>\n<p>U\u017cyj pami\u0119ci podr\u0119cznej (Redis, Memcached) dla endpoint\u00f3w, kt\u00f3re nie zmieniaj\u0105 si\u0119 cz\u0119sto. Ustaw TTL (time-to-live) adekwatny do cz\u0119stotliwo\u015bci zmian. Pami\u0119taj o inwalidacji cache przy aktualizacjach.<\/p>\n<h2 id=\"podsumowanie\">Podsumowanie<\/h2>\n<p>Wydajne API to podstawa user experience w SaaS. B\u0142\u0119dy N+1, brak paginacji i cache\u2019owania to trzy najcz\u0119stsze problemy, kt\u00f3re widzimy w audytach. Oczywi\u015bcie jest ich wi\u0119cej (np. brak indeks\u00f3w, nieefektywne serializery), ale te trzy s\u0105 naj\u0142atwiejsze do naprawy i daj\u0105 najwi\u0119kszy efekt. Je\u015bli Tw\u00f3j SaaS traci u\u017cytkownik\u00f3w \u2013 zacznij od audytu API. Cz\u0119sto wystarczy kilka zmian, aby konwersja wzros\u0142a o kilkadziesi\u0105t procent. A je\u015bli potrzebujesz pomocy \u2013 w JurskiTech mamy w tym do\u015bwiadczenie.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Wst\u0119p Prowadzisz SaaS i wydaje Ci si\u0119, \u017ce wszystko dzia\u0142a? U\u017cytkownicy si\u0119 loguj\u0105, ale po tygodniu odchodz\u0105? Cz\u0119sto winowajc\u0105 jest nie frontend, nie serwer, a API \u2013 cichy zab\u00f3jca konwersji. W praktyce JurskiTech widzieli\u015bmy SaaSy, kt\u00f3re traci\u0142y 40% u\u017cytkownik\u00f3w tylko przez op\u00f3\u017anienia w odpowiedziach API. W tym artykule poka\u017c\u0119 3 b\u0142\u0119dy, kt\u00f3re pope\u0142niaj\u0105 nawet do\u015bwiadczeni<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[699,617,1003,984],"class_list":["post-2725","post","type-post","status-publish","format-standard","hentry","category-warto-wiedziec","tag-api-gateway","tag-b2b-saas","tag-debugowanie-wydajnosci","tag-optymalizacja-api"],"_links":{"self":[{"href":"https:\/\/news.jurskitech.pl\/blog\/wp-json\/wp\/v2\/posts\/2725","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/news.jurskitech.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/news.jurskitech.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/news.jurskitech.pl\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/news.jurskitech.pl\/blog\/wp-json\/wp\/v2\/comments?post=2725"}],"version-history":[{"count":0,"href":"https:\/\/news.jurskitech.pl\/blog\/wp-json\/wp\/v2\/posts\/2725\/revisions"}],"wp:attachment":[{"href":"https:\/\/news.jurskitech.pl\/blog\/wp-json\/wp\/v2\/media?parent=2725"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/news.jurskitech.pl\/blog\/wp-json\/wp\/v2\/categories?post=2725"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/news.jurskitech.pl\/blog\/wp-json\/wp\/v2\/tags?post=2725"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}