Diff odpowiedzi API: porównaj payloady JSON API online
Wklej po lewej oczekiwaną odpowiedź, po prawej rzeczywiście otrzymaną, i zobacz każde pole, które się zmieniło. Stworzone do pracy backendowej, QA i integracyjnej. Nic nie opuszcza Twojej przeglądarki.
Czym jest to narzędzie do diffowania odpowiedzi API
Darmowe narzędzie działające w przeglądarce do porównywania dwóch ciał odpowiedzi HTTP API. Wklej po lewej JSON otrzymany ze stagingu, po prawej JSON z produkcji, a różnice zostaną podświetlone znak po znaku. Tekst nigdy nie opuszcza Twojej maszyny, co ma znaczenie, bo prawdziwe odpowiedzi API regularnie zawierają adresy email klientów, tokeny sesji, wewnętrzne ID użytkowników i inne rzeczy, których nie chcesz wysyłać do zewnętrznego serwisu diff.
Zostało stworzone na ten moment, gdy niestabilny test integracyjny pada w CI, a Ty masz zrzut ekranu z Postmana z działającą odpowiedzią na laptopie i log CI z popsutą odpowiedzią z build runnera. Stawianie pełnego contract testu Pact dla jednorazowego dochodzenia jest przesadą. Dwa panele tekstowe, jeden diff, i zwykle jesteś w stanie zawęzić problem do jednego pola w niespełna minutę.
Pod spodem silnik diffa jest ten sam, który napędza nasze narzędzie compare-json. Po prostu opakowaliśmy go pod workflow testów API. Jeśli Twoje odpowiedzi to XML lub envelope SOAP, obsłuży je nasza strona compare-xml. Jeśli porównujesz tekst dowolny, na przykład log webhooka albo audit trail, właściwym miejscem jest nasze narzędzie compare-text.
Jak diff odpowiedzi API faktycznie pomaga
Diff odpowiedzi API zajmuje miejsce między dwiema pokrewnymi, ale odrębnymi koncepcjami testowymi. Diff schematu OpenAPI 3.1 mówi Ci, co Twój kontrakt deklaruje, że się zmieniło: nowe opcjonalne pole, przemianowana property, surowszy enum. Snapshot testing (snapshoty Jest, snapshoty Vitest, pytest-snapshot) mówi, co Twój kod wyprodukował względem zapisanej fixture. To narzędzie siedzi po stronie runtime. Dajesz mu dwa prawdziwe ciała odpowiedzi, a ono pokazuje każdy bajt, który się różni, niezależnie od tego, czy schemat dopuszcza zmianę i czy fixture snapshota jest aktualna.
Dlaczego to przydatne? Bo bugi, które najmocniej gryzą w pracy integracyjnej z REST, to nie naruszenia schematu. To subtelne dryfy: serializator, który po upgradzie Jacksona po cichu zmienił datę z ISO-8601 na timestamp Unixa, schemat Marshmallow, który zaczął emitować null zamiast pomijać brakujące pole, ViewSet DRF, który po zmianie middleware zaczął opakowywać payload w envelope data. Spec OpenAPI się nie zmienił. Snapshot nie został zaktualizowany. Testy w izolacji przeszły. Integracja padła. Diff body odpowiedzi łapie wszystkie te przypadki, bo nie obchodzi go kontrakt; obchodzą go bajty.
Pola lotne to główna rzecz, którą trzeba pilnować. Timestampy, ID requestów, ID trace, UUID generowane po stronie serwera i kursory paginacji będą się różnić między dwoma capture'ami tego samego endpointu, nawet gdy nic istotnego się nie zmieniło. Właściwy ruch to znormalizować przed diffem: zamień timestampy na placeholder, usuń ID trace, posortuj tablice, których kolejność nie jest istotna kontraktowo. Narzędzia takie jak Pact ogarniają to matcherami; w tym narzędziu ogarniasz to edytując panele. Zajmuje to dziesięć sekund i znika tło szumu.
Jak zrobić diff odpowiedzi API w trzech krokach
Dwa panele tekstowe, jeden diff. Bez logowania, bez uploadu, bez kablowania proxy.
- 1
Złap pierwszą odpowiedź
Stuknij w endpoint przez curl, httpie, Postmana, Insomnię albo cokolwiek używa Twój zespół. curl -s https://api.example.com/v1/users/123 | jq to dobra baza, bo jq ładnie formatuje JSON, co czyni diff dużo łatwiejszym do czytania. Skopiuj body (samo JSON, bez nagłówków) i wklej w lewy panel. Jeśli ciągniesz z loga CI, usuń prefiks z timestampem, który dodaje większość loggerów, żeby diff trafił na faktyczny payload.
- 2
Złap drugą odpowiedź
Stuknij w źródło porównania: drugie środowisko, drugą wersję API, drugiego dostawcę. Ta sama forma capture'a, ten sam krok pretty-printa. Wklej do prawego panelu. Jeśli jeden capture pochodzi z nagranej fixture (vcrpy, pollyjs, MSW, nock), a drugi jest na żywo, najpierw znormalizuj oczywiste pola lotne: wyczyść request_id, podmień timestampy na stałą, usuń wszystkie nagłówki trace, które wyciekły do body. Pozostały diff to faktyczny sygnał.
- 3
Przeczytaj wyróżnione różnice
Usunięcia pojawiają się jako czerwone przekreślenia po lewej; dodania jako zielone po prawej. Liczniki zmian w nagłówkach mówią, ile odrębnych edycji znalazł diff. Skup się najpierw na trzech rzeczach: zmiany w stringach status, brakujące lub dodane klucze, zmiany typu wartości (string na number, object na null). Te trzy kategorie pokrywają niemal każdą prawdziwą regresję API. Zmiany formatu i kolejności są zwykle szumem, chyba że Twój consumer od nich zależy.
Kiedy diff odpowiedzi API jest właściwym wyborem
Reprodukcja niestabilnego testu integracyjnego
Test przechodzi lokalnie, pada w CI. Masz odpowiedź złapaną przez lokalny przebieg Postmana i odpowiedź złapaną przez agenta build CI (większość systemów CI potrafi zrzucić request/response z flagą verbose). Wklej obie do narzędzia diffa. W dziewięciu na dziesięć przypadków różnica jest środowiskowa: inny feature flag, przeterminowana fixture, offset strefy czasowej na runnerze. Pozostały jeden raz to prawdziwy bug, a Ty właśnie zlokalizowałeś go do konkretnego pola.
Walidacja fixture wobec świeżej odpowiedzi
W repo masz zacommitowany plik fixture, który mockuje API trzeciej strony do testów. Dostawca upstream właśnie wypuścił nową wersję minor. Stuknij w endpoint na żywo curlem, wklej tę odpowiedź obok fixture, a zobaczysz dokładnie, które pola dryfowały. To ręczna wersja tego, co automatyzują narzędzia replay w stylu VCR. Przydatne, gdy chcesz zaktualizować jedną fixture bez ponownego nagrywania całego pakietu testów.
Sprawdzenie wstecznej kompatybilności wersji API
Masz zaraz wypuścić v2 wewnętrznego API. Klient v1 nadal istnieje na produkcji. Stuknij zarówno w /v1/orders/42, jak i w /v2/orders/42 i zrób diff odpowiedzi. Każde usunięte pole, każdy przemianowany klucz, każda zmiana typu wartości to breaking change dla klienta v1. Każde nowe pole jest addytywne i bezpieczne. To bieda-wersja consumer-driven contract testu; nie skaluje się na dziesiątki endpointów, ale do szybkiego sanity checka na jeden czy dwa wystarcza.
Wyłapanie regresji w serializatorze
Twój zespół podniósł Jacksona, Marshmallow, DRF lub podobną warstwę serializacji. Testy przechodzą. Potem downstream consumer zgłasza, że jego parser się dławi. Złap odpowiedź tego samego endpointu na starym i nowym branchu i zrób diff. Częste znaleziska: format daty zmienił się z 2026-05-09T10:00:00Z na timestamp Unixa, dziesiętne zgubiły końcowe zera, enum zaczął serializować się jako integer zamiast stringa, albo w payloadzie zaczęły pojawiać się pola null, które wcześniej były pomijane.
Porównanie payloadów webhooków od dwóch dostawców lub wersji
Webhook V1 i V2 Stripe dla tego samego typu eventu wyglądają niemal identycznie i wcale takie nie są. Tak samo payloady eventów webhooków GitHuba między wersjami API i subskrypcje eventów Slacka między scope'ami. Wklej przykładowy payload z każdego do narzędzia diffa, żeby zobaczyć przemianowane pola, przesunięte zagnieżdżone obiekty i nowe bloki metadanych. To szybsze niż czytanie obu stron dokumentacji dostawców obok siebie, zwłaszcza gdy dokumentacja prześlizguje się nad tym, jakie pola faktycznie pojawiają się w praktyce.
Debug klasycznego "działa na stagingu, popsute na prodzie"
Klasyczny ból głowy z deploya. To samo zapytanie klienta zwraca subtelnie różny JSON ze stagingu i z produkcji. Złap oba, wklej oba, a różnica to zwykle config flag, brakujący feature gate albo przeterminowana zacheowana odpowiedź. To samo dotyczy debugowania multi-region (us-east-1 vs eu-west-1 zwracające inne dane), problemów z cache CDN (Cloudflare zacheował przeterminowane body) i opóźnień read-replica, gdy zapis nie zdążył się rozpropagować wszędzie.
Edge case'y diffa odpowiedzi API, które warto znać
Przypadki, w których diff body odpowiedzi nie zgadza się z tym, co powiedziałby Twój framework testów, narzędzie OpenAPI albo Twoje oczy. Warto przelecieć, zanim założysz, że diff znalazł prawdziwego buga.
| Topic | What this tool does |
|---|
| Pola lotne (timestampy, ID) | created_at, updated_at, request_id, trace_id, UUID generowane po stronie serwera i kursory paginacji zawsze będą się różnić między dwoma capture'ami. Znormalizuj je jq 'del(...)', podmień na stałą albo po prostu wyedytuj przed diffem. Sygnał, na którym Ci faktycznie zależy, jest gdzie indziej. |
|---|
| null vs brakujący klucz | {"foo": null} i {} są w JSON-ie różne, a wiele serializatorów (Marshmallow, Jackson, System.Text.Json) ma ustawienia, które przełączają się między nimi. Diff to wyjawi. Niektórzy klienci traktują je jako równoważne, niektórzy nie; właściwa odpowiedź to to, co robi Twój consumer, i dlatego to częste źródło regresji po upgradzie serializatora. |
|---|
| Kolejność kluczy | RFC 8259 definiuje obiekty JSON jako nieuporządkowane. Dwie semantycznie identyczne odpowiedzi z różną kolejnością kluczy pokażą się tutaj jako diff, bo porównanie tekstowe jest wrażliwe na kolejność. Jeśli chcesz diffa niewrażliwego na kolejność, posortuj obie strony wcześniej przez jq --sort-keys. Uważaj na rzadkiego consumera, który zależy od kolejności (niektóre flow podpisów, niektóre stare parsery). |
|---|
| Kolejność tablic | Tablice w JSON-ie są uporządkowane, ale wiele API zwraca tablice, których kolejność tak naprawdę nie jest kontraktowa: lista uprawnień, lista feature flag, lista subskrypcji webhooków. Diff zaznaczy przesortowaną tablicę jako zmianę, nawet jeśli żaden consumer się nie przejmie. Jeśli przesortowanie jest nieszkodliwe w Twojej domenie, posortuj obie strony po stabilnym kluczu przed diffem. |
|---|
| Surowość JSON.parse | Diff traktuje Twój input jako nieprzejrzysty tekst. Jeśli któryś z Twoich capture'ów ma końcowy przecinek, niecytowany klucz albo komentarz (wszystko nielegalne w ścisłym JSON-ie), diff i tak zadziała, ale będzie wyglądał głośniej, niż musi. Najpierw przepuść oba capture'y przez jq ., żeby przeformatować i odrzucić nieprawidłowy input. jq używa ścisłego parsera RFC 8259. |
|---|
| Owijanie odpowiedzi (envelope data vs płaskie) | Wiele API owija payloady w envelope {"data": ...}, czasem dodając obok meta, links albo included (JSON:API i podobne). Migracja z płaskiego do owiniętego (albo odwrotnie) to breaking change, który natychmiast pokazuje się w diffie, ale łatwo go przegapić w review schematu, bo wewnętrzny rekord wygląda tak samo. |
|---|
| Zmiany kursorów paginacji | Paginacja kursorowa używa nieprzejrzystych tokenów (next_cursor, after, page_token) zaprojektowanych tak, by zmieniały się przy każdym wywołaniu. Zawsze pokażą się jako diff. Usuń je przed porównaniem albo porównuj tylko zawartość tablicy data, ignorując blok paginacji w całości. |
|---|
| Formaty odpowiedzi błędów | Body błędów to dziki zachód. Niektóre API zwracają płaskie {"error": "..."}, niektóre RFC 7807 Problem Details z type, title, status, detail, instance, a niektóre kształt zależny od dostawcy. Migracja na RFC 7807 z formatu autorskiego wyprodukuje duży diff, który w większości jest poprawą; migracja w drugą stronę to regresja, którą warto wyłapać wcześnie. |
|---|
Diff odpowiedzi API: często zadawane pytania
Czym to się różni od wbudowanej funkcji diff w Postmanie?
Postman ma widok porównania odpowiedzi w Collection Runnerze i funkcję Visualize dla pojedynczych odpowiedzi. Obie są dobre, jeśli żyjesz w Postmanie i Twoje odpowiedzi są już zapisane jako wpisy historii Postmana. To narzędzie jest agnostyczne wobec dostawcy. Możesz wklejać z Postmana, Insomni, curla, httpie, loga CI, snippeta ze Stack Overflow albo wiadomości na Slacku od kolegi. Bez konta, bez workspace, bez sync. Do jednorazowych porównań między narzędziami to jest szybsze. Do testów API współdzielonych w zespole na jednej platformie własna funkcja Postmana jest w porządku.
Jak obsłużyć pola lotne, jak timestampy i ID requestów?
Pragmatyczny ruch to normalizacja przed diffem. Otwórz oba wklejenia w panelach, a potem wprost wyedytuj pola lotne: zamień timestampy na stały string, usuń wartości request_id i trace_id, wykasuj kursory paginacji, które zmieniają się przy każdym wywołaniu. Diff podświetli tylko pozostałe różnice, czyli te, które naprawdę mają znaczenie. Do powtarzanych porównań tego samego endpointu możesz też przepuścić odpowiedź przez jq z filtrem delete (jq 'del(.meta.request_id)') przed wklejeniem.
Czym to się różni od diffa schematu OpenAPI?
Diff schematu porównuje kontrakty: mówi Ci, że POST /orders dorzucił opcjonalne pole discount_code albo że enum status zyskał nową wartość. Narzędzia świadome OpenAPI, jak oasdiff czy Spectral, robią to dobrze. To narzędzie porównuje rzeczywiste body odpowiedzi. Oba są komplementarne. Diff schematu łapie zmiany kontraktu; diff odpowiedzi łapie dryf między kontraktem a rzeczywistością, a tam właśnie kryją się bugi serializacji, niezgodności środowisk i przeterminowane fixture.
Czy radzi sobie z dużymi odpowiedziami?
W praktyce tak, do kilku tysięcy linii sformatowanego JSON-a po każdej stronie. Powyżej tego diff znakowy z czyszczeniem semantycznym zwalnia, bo działa w Twojej przeglądarce, nie na serwerze. Dla bardzo dużych payloadów (pomyśl o stronicowanym dumpie 10 000 rekordów) właściwym podejściem jest pociąć odpowiedź na mniejsze kawałki po rekordzie albo po kluczu top-level i zdiffować każdy kawałek osobno. Albo uruchomić diff strukturalny w wierszu poleceń przez jd lub diff <(jq . a.json) <(jq . b.json) dla czystej szybkości.
Czy działa dla odpowiedzi XML lub SOAP?
Bezpośrednio nie. Ta strona jest dostrojona do JSON-a, którego używa większość nowoczesnych payloadów REST i webhooków. Jeśli musisz diffować XML, envelope'y SOAP, RSS albo konfigurację w stylu POM, właściwym miejscem jest nasze narzędzie compare-xml; obsługuje wcięcia i formatowanie namespace poprawnie. Dla surowych ciał odpowiedzi mieszających nagłówki i body, albo dla API plain-text (niektóre stare systemy nadal zwracają text/plain), compare-text robi robotę bez prób narzucania struktury.
Czy zachowuje kolejność kluczy, czy je sortuje?
Zachowuje kolejność kluczy, którą wkleisz. Obiekty JSON są formalnie nieuporządkowane wedle RFC 8259, więc dwie semantycznie identyczne odpowiedzi z kluczami w innej kolejności pokażą się tu jako diff. Jeśli chcesz ignorować kolejność, najpierw znormalizuj obie strony przepuszczając przez jq --sort-keys albo odpowiednik. Większość klientów nie zależy od kolejności kluczy, więc normalizacja sortowanymi kluczami to bezpieczny default do porównań odpowiedzi; pamiętaj jednak, że niektórzy starsi consumerzy (stare mosty XML-na-JSON, niektóre flow podpisów cyfrowych) zależą od kolejności.
Prywatność i dlaczego ma znaczenie dla odpowiedzi API
Payloady odpowiedzi API regularnie zawierają rzeczy, których nie chcesz wycieknąć. Adresy email klientów. Wewnętrzne ID użytkowników. Tokeny sesji. Tokeny Auth bearer, które pomyłkowo wylądowały w polu body. ID klientów Stripe. Sekrety webhooków. Pola PII jak imiona, adresy i numery telefonów. Wklejenie któregoś z nich do hostowanego w chmurze serwisu diff to samo w sobie zdarzenie przetwarzania danych i w zależności od branży może naruszać Twoje kontrole SOC 2, umowy powierzenia danych RODO albo Business Associate Agreement HIPAA. To narzędzie działa w całości w Twojej przeglądarce. Nic nie jest wysyłane, logowane ani przekazywane do żadnego serwisu trzeciego. Diff, podświetlanie i renderowanie wykonują się na Twojej maszynie. Zweryfikowanie tej deklaracji jest wprost: otwórz DevTools w przeglądarce, przełącz się na zakładkę Network, wklej obie odpowiedzi i obserwuj. Przy porównaniu nie ma żadnych żądań wychodzących. W szerszych wytycznych do projektowania i bezpieczeństwa API solidnym punktem odniesienia są API design best practices Microsoftu.