Błędy ograniczeń bazy danych w UX: zamień niepowodzenia na jasne komunikaty
Dowiedz się, jak błędy ograniczeń bazy danych mogą stać się pomocnymi komunikatami przy polach, mapując niepowodzenia związane z unikalnością, kluczami obcymi i NOT NULL w twojej aplikacji.

Dlaczego niepowodzenia ograniczeń tak irytują użytkowników
Kiedy ktoś kliknie Zapisz, spodziewa się jednego z dwóch wyników: się powiodło, albo dostanie informację, co szybko naprawić. Zbyt często otrzymują ogólny baner typu „Żądanie nie powiodło się” lub „Coś poszło nie tak.” Formularz zostaje bez zmian, nic nie jest wyróżnione i użytkownik musi zgadywać.
Ten brak informacji to powód, dla którego UX błędów ograniczeń bazy danych ma znaczenie. Baza danych narzuca reguły, których użytkownik nie widział: „ta wartość musi być unikalna”, „ten rekord musi wskazywać istniejący element”, „to pole nie może być puste”. Jeśli aplikacja ukrywa te reguły za niejasnym błędem, ludzie czują się obwinieni za coś, czego nie rozumieją.
Ogólne błędy też podważają zaufanie. Użytkownicy myślą, że aplikacja jest niestabilna, więc ponawiają próbę, odświeżają stronę lub rezygnują z zadania. W środowisku pracy kontaktują się z wsparciem, przesyłając zrzuty ekranu, które nic nie wyjaśniają, bo zrzut nie zawiera istotnych szczegółów.
Przykład: ktoś tworzy rekord klienta i dostaje „Zapis nie powiódł się.” Próbuje jeszcze raz z tym samym emailem. Dalej nie działa. Zaczyna się zastanawiać, czy system duplikuje dane, traci je, czy jedno i drugie.
Baza danych często jest ostatecznym źródłem prawdy, nawet gdy wykonujesz walidację po stronie UI. Widzi aktualny stan, w tym zmiany innych użytkowników, zadania w tle i integracje. Dlatego niepowodzenia ograniczeń będą się zdarzać i to jest normalne.
Dobry efekt jest prosty: przetłumacz regułę bazy na komunikat, który wskazuje konkretne pole i następny krok. Na przykład:
- „Email jest już używany. Spróbuj innego adresu lub zaloguj się.”
- „Wybierz prawidłowe konto. Wybrane konto już nie istnieje.”
- „Numer telefonu jest wymagany.”
W dalszej części artykułu omówimy, jak wykonać tę translację, żeby błędy zamieniały się w szybką naprawę — niezależnie od tego, czy ręcznie implementujesz cały stos, czy korzystasz z narzędzia takiego jak AppMaster.
Typy ograniczeń, z którymi się spotkasz (i co one znaczą)
Większość momentów „Żądanie nie powiodło się” pochodzi z niewielkiego zestawu reguł bazy danych. Jeśli potrafisz nazwać regułę, zwykle możesz zamienić ją w jasny komunikat przy odpowiednim polu.
Oto popularne typy ograniczeń w prostym języku:
- Ograniczenie unikalności (Unique constraint): Wartość musi być wyjątkowa. Typowe przykłady: email, nazwa użytkownika, numer faktury czy zewnętrzne ID. Kiedy występuje błąd, użytkownik nie „zrobił czegoś złego”, tylko natrafił na istniejące już dane.
- Ograniczenie klucza obcego (Foreign key constraint): Jeden rekord wskazuje inny rekord, który musi istnieć (np.
order.customer_id). Błąd występuje, gdy odwoływany rekord został usunięty, nigdy nie istniał albo UI przesłało niewłaściwe ID. - NOT NULL: Wymagana wartość jest pusta na poziomie bazy. Może się to zdarzyć nawet jeśli formularz wygląda na kompletny (np. UI nie wysłało pola albo API je nadpisało).
- Check constraint: Wartość jest poza dozwolonym zakresem, np. „quantity must be > 0”, „status musi być jednym z dozwolonych”, lub „discount musi być między 0 a 100”.
Trudność polega na tym, że ten sam problem może wyglądać inaczej w zależności od bazy i narzędzi. Postgres może nadać nazwę ograniczeniu (co pomaga), podczas gdy ORM może owinąć to w ogólny wyjątek (co utrudnia). Nawet ten sam błąd unikalności może pojawić się jako „duplicate key”, „unique violation” lub kod specyficzny dla dostawcy.
Praktyczny przykład: ktoś edytuje klienta w panelu administracyjnym, klika Zapisz i dostaje błąd. Jeśli API powie UI, że to ograniczenie unikalności na email, możesz pokazać „Ten email jest już używany” pod polem Email zamiast niejasnego toastu.
Traktuj każdy typ ograniczenia jako wskazówkę, co użytkownik może zrobić dalej: wybrać inną wartość, wskazać istniejący powiązany rekord lub uzupełnić brakującą, wymaganą wartość.
Co musi robić dobry komunikat przypisany do pola
Błąd ograniczenia bazy danych to wydarzenie techniczne, ale doświadczenie powinno wyglądać jak zwykła wskazówka. Dobry UX błędów ograniczeń zamienia „coś się zepsuło” w „oto, co poprawić”, bez zmuszania użytkownika do zgadywania.
Używaj prostego języka. Zamień techniczne terminy bazy danych, jak „unique index” czy „foreign key”, na to, co powie człowiek. „Ten email jest już używany” jest o wiele bardziej przydatne niż „duplicate key value violates unique constraint.”
Umieść komunikat tam, gdzie jest akcja. Jeśli błąd wyraźnie dotyczy jednego pola, dołącz go do tego pola, żeby użytkownik mógł od razu poprawić. Jeśli dotyczy całej akcji (np. „nie możesz usunąć tego, bo jest używane gdzie indziej”), pokaż go na poziomie formularza z jasnym krokiem następnym.
Konkretność zwycięża nad uprzejmością. Pomocny komunikat odpowiada na dwa pytania: co trzeba zmienić i dlaczego to odrzucono. „Wybierz inny login” lepsze niż „Nieprawidłowy login.” „Wybierz klienta przed zapisem” lepsze niż „Brakujące dane.”
Uważaj na dane wrażliwe. Czasem najbardziej „pomocny” komunikat ujawnia informacje. Na ekranie logowania lub resetu hasła mówienie „Nie istnieje konto dla tego emaila” może pomóc atakującym. W takich przypadkach użyj bezpieczniejszego komunikatu, np. „Jeśli konto istnieje dla tego adresu, otrzymasz wiadomość wkrótce.”
Planuj też obsługę więcej niż jednego problemu naraz. Jedno zapisanie może zawieść na kilku ograniczeniach. UI powinno umieć pokazać kilka komunikatów pól jednocześnie, bez przytłaczania ekranu.
Silny komunikat na poziomie pola używa prostych słów, wskazuje właściwe pole (lub jest wyraźnie na poziomie formularza), mówi użytkownikowi, co zmienić, nie ujawnia faktów prywatnych i obsługuje wiele błędów w jednym responście.
Zaprojektuj kontrakt błędów między API a UI
Dobre UX zaczyna się od umowy: gdy coś się nie powiedzie, API mówi UI dokładnie, co się stało, a UI pokazuje to w ten sam sposób za każdym razem. Bez takiego kontraktu wracacie do niepomocnego toastu.
Praktyczny kształt błędu powinien być mały, ale konkretny. Powinien zawierać stabilny kod błędu, pole (gdy mapuje do jednego inputu), komunikat zrozumiały dla człowieka oraz opcjonalne szczegóły do logów.
{
"error": {
"code": "UNIQUE_VIOLATION",
"field": "email",
"message": "That email is already in use.",
"details": {
"constraint": "users_email_key",
"table": "users"
}
}
}
Kluczem jest stabilność. Nie wystawiaj surowego tekstu bazy użytkownikom i nie każ UI parsować komunikatów Postgresa. Kody powinny być spójne na wszystkich platformach (web, iOS, Android) i na wszystkich endpointach.
Zdecyduj wcześniej, jak reprezentujesz błędy na poziomie pola kontra błędy formularza. Błąd pola oznacza, że jeden input jest zablokowany (ustaw field, pokaż komunikat pod inputem). Błąd formularza oznacza, że akcja nie może zostać ukończona mimo pozornie poprawnych pól (pozostaw field pusty, pokaż komunikat obok przycisku Zapisz). Jeśli wiele pól może zawieść naraz, zwróć tablicę błędów, każdą z własnym field i code.
Aby utrzymać renderowanie spójne, trzymaj zasady UI nudne i przewidywalne: pokaż pierwszy błąd na górze jako krótkie podsumowanie i inline przy polu, trzymaj komunikaty krótkie i wykonalne, używaj tych samych sformułowań w różnych przepływach (rejestracja, edycja profilu, ekrany administracyjne) i loguj details, pokazując użytkownikowi tylko message.
Jeśli budujesz z AppMaster, traktuj ten kontrakt jak każde inne wyjście API. Backend może zwracać ustrukturyzowany kształt, a generowane aplikacje web (Vue3) i mobilne mogą go renderować jednym wspólnym wzorcem, dzięki czemu każde niepowodzenie ograniczenia będzie wyglądać jak wskazówka, a nie awaria.
Krok po kroku: tłumaczenie błędów bazy na komunikaty pól
Dobry UX błędów ograniczeń zaczyna się od traktowania bazy jako ostatecznego sędziego, nie pierwszej linii informacji. Użytkownicy nie powinni widzieć surowego tekstu SQL, stack trace’ów czy „Żądanie nie powiodło się.” Powinni zobaczyć, które pole wymaga uwagi i co mają zrobić dalej.
Praktyczny przebieg, który działa w większości stosów:
- Zdecyduj, gdzie błąd jest przechwytywany. Wybierz jedno miejsce, gdzie błędy bazy zamieniane są na odpowiedzi API (często warstwa repozytorium/DAO lub globalny handler błędów). To zapobiega chaosowi „czasem inline, czasem toast”.
- Sklasyfikuj niepowodzenie. Gdy zapis się nie powiedzie, wykryj klasę: unikalność, klucz obcy, NOT NULL lub check. Korzystaj z kodów sterownika, jeśli to możliwe. Unikaj parsowania tekstu, chyba że nie masz innego wyjścia.
- Mapuj nazwy ograniczeń na klucze pól. Ograniczenia są świetnymi identyfikatorami, ale UI potrzebuje kluczy pól. Trzymaj prosty lookup, np.
users_email_key -> emailluborders_customer_id_fkey -> customerId. Umieść go blisko kodu, który jest właścicielem schematu. - Generuj bezpieczny komunikat. Buduj krótkie, przyjazne teksty zależnie od klasy, nie surowego komunikatu DB. Unique -> „Ta wartość jest już używana.” FK -> „Wybierz istniejącego klienta.” NOT NULL -> „To pole jest wymagane.” Check -> „Wartość poza dozwolonym zakresem.”
- Zwróć ustrukturyzowane błędy i renderuj inline. Wyślij spójny payload (np.
[{ field, code, message }]). W UI przypnij komunikaty do pól, przewiń i ustaw fokus na pierwszym błędnym polu, a globalny baner zostaw tylko jako krótkie podsumowanie.
Jeśli budujesz z AppMaster, zastosuj tę samą ideę: przechwyć błąd bazy w jednym miejscu backendu, przetłumacz go na przewidywalny format błędu pola, a potem pokaż go obok inputu w web lub mobilnym UI. To utrzymuje doświadczenie spójnym, nawet gdy model danych ewoluuje.
Realistyczny przykład: trzy nieudane zapisy, trzy pomocne rezultaty
Te niepowodzenia często zostają zredukowane do jednego ogólnego toastu. Każde z nich potrzebuje innego komunikatu, chociaż wszystkie pochodzą z bazy.
1) Rejestracja: email już użyty (ograniczenie unikalności)
Surowy błąd (co widać w logach): duplicate key value violates unique constraint "users_email_key"
Co powinien widzieć użytkownik: „Ten email jest już zarejestrowany. Spróbuj się zalogować lub użyj innego adresu.”
Umieść komunikat przy polu Email i pozostaw formularz z wypełnionymi danymi. Jeśli możesz, zaoferuj akcję pomocniczą typu „Zaloguj się”, żeby nie musieli zgadywać, co dalej.
2) Tworzenie zamówienia: brak klienta (klucz obcy)
Surowy błąd: insert or update on table "orders" violates foreign key constraint "orders_customer_id_fkey"
Co powinien widzieć użytkownik: „Wybierz klienta, aby złożyć to zamówienie.”
To nie wygląda jak „błąd” dla użytkownika — bardziej brak kontekstu. Wyróżnij selector Klient, zachowaj dodane pozycje zamówienia, a jeśli klient został usunięty w innej karcie, powiedz to wprost: „Ten klient już nie istnieje. Wybierz innego.”
3) Aktualizacja profilu: brak wymaganego pola (NOT NULL)
Surowy błąd: null value in column "last_name" violates not-null constraint
Co powinien widzieć użytkownik: „Nazwisko jest wymagane.”
Tak wygląda dobra obsługa ograniczeń: normalne komunikaty formularza, nie awaria systemu.
Aby pomóc wsparciu bez wycieku technicznych szczegółów do użytkowników, przechowuj pełny błąd w logach (lub w wewnętrznym panelu błędów): dołącz request ID i user/session ID, nazwę ograniczenia (jeśli dostępna) i tabelę/pole, payload API (zamaskuj dane wrażliwe), znacznik czasu, endpoint/akcję oraz komunikat widoczny dla użytkownika.
Błędy klucza obcego: pomóż użytkownikowi odzyskać sytuację
Błędy klucza obcego zwykle oznaczają, że użytkownik wybrał coś, czego już nie ma, nie jest dozwolone lub nie pasuje do obecnych reguł. Celem nie jest tylko wyjaśnienie błędu, lecz wskazanie jasnego następnego kroku.
Najczęściej błąd FK mapuje się do jednego pola: selektora, który odwołuje się do innego rekordu (Klient, Projekt, Osoba przypisana). Komunikat powinien nazwać rzecz, którą użytkownik rozpozna, a nie koncepcję bazy danych. Unikaj wewnętrznych ID czy nazw tabel. „Klient już nie istnieje” jest użyteczne. „FK_orders_customer_id violated (customer_id=42)” nie.
Dobry wzorzec odzyskiwania traktuje błąd jak nieaktualny wybór. Zachęć użytkownika do ponownego wyboru z najnowszej listy (odśwież dropdown lub otwórz picker). Jeśli rekord został usunięty lub zarchiwizowany, powiedz to wprost i skieruj do aktywnej alternatywy. Jeśli użytkownik stracił uprawnienia, powiedz „Nie masz już uprawnień do użycia tego elementu” i zaproponuj wybór innego lub kontakt z administratorem. Jeśli normalnym następnym krokiem jest utworzenie powiązanego rekordu, zaoferuj „Utwórz nowego klienta” zamiast zmuszać do ponawiania prób.
Usunięte i zarchiwizowane rekordy to typowa pułapka. Jeśli UI może pokazać nieaktywne elementy dla kontekstu, oznacz je wyraźnie (Zarchiwizowany) i zabroń wyboru. Zapobiega to błędom, ale nadal obsłuży przypadki, gdy inny użytkownik zmieni dane.
Czasem błąd klucza obcego powinien być błędem formularza, nie pola. Zrób tak, gdy nie możesz jednoznacznie ustalić, które odwołanie spowodowało błąd, gdy wiele referencji jest nieprawidłowych lub gdy prawdziwy problem to uprawnienia dotyczące całej akcji.
NOT NULL i walidacja: zapobiegaj błędowi, ale i go obsłuż
Błędy NOT NULL najłatwiej zapobiec i najbardziej irytują, gdy mimo to się pojawiają. Jeśli ktoś widzi „Żądanie nie powiodło się” po opuszczeniu wymaganego pola, baza robi pracę UI. Dobry UX polega na tym, że UI blokuje oczywiste przypadki, a API i tak zwraca jasne błędy przypisane do pola, gdy coś przejdzie.
Zacznij od wczesnych kontroli w formularzu. Oznacz wymagane pola blisko inputu, nie tylko w nagłówku. Krótka podpowiedź typu „Wymagane do paragonów” jest bardziej pomocna niż sama czerwona gwiazdka. Jeśli pole jest opcjonalne warunkowo (np. „Nazwa firmy” tylko gdy „Typ konta = Firma”), pokaż tę regułę w momencie, gdy staje się istotna.
Walidacja UI to za mało. Użytkownicy mogą ją obejść przez starsze wersje aplikacji, niestabilne połączenie, masowe importy lub automatyzację. Odzwierciedlaj te same reguły w API, żeby nie marnować rundy tylko po to, by upaść przy bazie.
Utrzymuj spójne słownictwo w całej aplikacji, żeby użytkownicy wiedzieli, co każde komunikat oznacza. Dla brakujących wartości używaj „Wymagane.” Dla limitów długości „Zbyt długie (max 50 znaków).” Dla formatów „Nieprawidłowy format (użyj [email protected]).” Dla typów „Musi być liczbą.”
Częściowe aktualizacje utrudniają NOT NULL. PATCH, który pomija wymagane pole, nie powinien zawieść, jeśli istniejąca wartość jest obecna, ale powinien zawieść, jeśli klient explicite ustawia je na null lub pustą wartość. Ustal tę zasadę raz, udokumentuj i egzekwuj konsekwentnie.
Praktyczne podejście: waliduj na trzech warstwach — reguły formularza po stronie klienta, walidacja żądania w API i ostateczna siatka bezpieczeństwa, która przechwytuje błąd NOT NULL z bazy i mapuje go na właściwe pole.
Typowe błędy prowadzące z powrotem do „Żądanie nie powiodło się”
Najszybsza droga do popsucia obsługi ograniczeń to zrobienie całej pracy po stronie bazy, a potem schowanie wyniku pod ogólnym toastem. Użytkownicy nie przejmują się tym, że constraint zadziałał. Chcą wiedzieć, co naprawić, gdzie i czy ich dane są bezpieczne.
Jednym z potknięć jest pokazywanie surowego tekstu bazy. Komunikaty typu duplicate key value violates unique constraint wyglądają jak awaria, nawet jeśli aplikacja może się z niej podnieść. Tworzą też zgłoszenia do wsparcia, bo użytkownicy kopiują strasznie brzmiący tekst zamiast poprawić jedno pole.
Inną pułapką jest poleganie na dopasowywaniu ciągów znaków. Działa, dopóki nie zmienisz sterownika, nie uaktualnisz Postgresa lub nie przemianujesz ograniczenia. Wtedy twoje mapowanie „email już użyty” przestaje działać, a ty wracasz do „Żądanie nie powiodło się.” Preferuj stabilne kody błędów i dołącz nazwę pola, którą UI rozumie.
Zmiany schematu łamią mapowanie pól częściej niż się spodziewasz. Zmiana nazwy z email na primary_email może zamienić jasny komunikat w dane bez miejsca do wyświetlenia. Spraw, by mapowanie było częścią tego samego zestawu zmian co migracja i testuj głośno, gdy klucz pola jest nieznany.
Wielkim zabójcą UX jest zamienianie każdego błędu ograniczenia w HTTP 500 bez ciała. To mówi UI „to wina serwera”, więc nie może pokazać wskazówek przy polu. Większość błędów ograniczeń da się naprawić przez użytkownika, więc zwracaj odpowiedź jak walidację z detalami.
Kilka wzorców do obserwacji:
- Komunikaty o unikalnym emailu, które potwierdzają istnienie konta (używaj neutralnego sformułowania w przepływach rejestracji)
- Obsługa „po jednym błędzie na raz” i ukrywanie kolejnych uszkodzonych pól
- Formularze wieloetapowe, które tracą błędy po przejściu wstecz/dalej
- Ponowne próby wysyłające nieaktualne wartości i nadpisujące poprawne komunikaty pól
- Logowanie, które pomija nazwę ograniczenia lub kod błędu, utrudniając śledzenie błędów
Na przykład: jeśli formularz rejestracji mówi „Email już istnieje”, możesz ujawnić istnienie konta. Bezpieczniejszy komunikat to „Sprawdź maila lub spróbuj się zalogować”, jednocześnie przypinając błąd do pola email.
Szybka lista kontrolna przed wypuszczeniem
Zanim wypuścisz, sprawdź drobne detale, które decydują, czy błąd ograniczenia będzie pomocną wskazówką, czy ślepą uliczką.
Odpowiedź API: czy UI może na niej działać?
Upewnij się, że każdy błąd w stylu walidacji zwraca wystarczającą strukturę, by wskazać konkretne pole. Dla każdego błędu zwróć field, stabilny code i czytelny message. Pokryj typowe przypadki bazy (unique, foreign key, NOT NULL, check). Techniczne szczegóły zostaw do logów, nie dla użytkowników.
Zachowanie UI: czy pomaga użytkownikowi odzyskać?
Nawet idealny komunikat będzie źle odebrany, jeśli formularz utrudnia poprawę. Ustaw fokus na pierwszym błędnym polu i przewiń do niego, jeśli trzeba. Zachowaj to, co użytkownik już wpisał (szczególnie przy wielu błędach). Pokazuj najpierw błędy przy polach, a krótkie podsumowanie tylko gdy to pomocne.
Logi i testy: czy wyłapujesz regresje?
Obsługa ograniczeń często psuje się cicho gdy schematy się zmieniają, więc traktuj to jak funkcję, którą trzeba utrzymywać. Loguj błąd DB wewnętrznie (nazwa ograniczenia, tabela, operacja, request ID), ale nigdy nie pokazuj go bezpośrednio użytkownikowi. Dodaj testy dla przynajmniej jednego przykładu każdego typu ograniczenia i weryfikuj, że mapowanie pozostaje stabilne, nawet jeśli dokładne sformułowanie błędu bazy się zmieni.
Następne kroki: ujednolić to w całej aplikacji
Większość zespołów naprawia błędy ograniczeń po jednym ekranie na raz. To pomaga, ale użytkownicy zauważają luki: jeden formularz pokazuje jasny komunikat, inny dalej mówi „Żądanie nie powiodło się.” Spójność to to, co zmienia to z poprawki w powtarzalny wzorzec.
Zacznij tam, gdzie boli najbardziej. Przejrzyj tydzień logów lub zgłoszeń do wsparcia i wybierz kilka ograniczeń, które pojawiają się najczęściej. Te „top offenders” powinny być pierwsze, którym nadajesz przyjazne komunikaty przypisane do pól.
Traktuj tłumaczenie błędów jako małą funkcję produktową. Miej jedną wspólną mapę na całą aplikację: nazwa ograniczenia (lub kod) -> nazwa pola -> komunikat -> wskazówka do odzyskania. Trzymaj komunikaty proste i wskazówki wykonawcze.
Lekki plan wdrożenia, który pasuje do napiętego cyklu produktowego:
- Zidentyfikuj 5 ograniczeń, które najczęściej trafiają do użytkowników, i napisz dokładne komunikaty, które chcesz pokazać.
- Dodaj tabelę mapowania i używaj jej we wszystkich endpointach zapisujących dane.
- Ustandaryzuj sposób renderowania błędów w formularzach (to samo miejsce, ten sam ton, to samo zachowanie fokusu).
- Przejrzyj komunikaty z nietechnicznym kolegą i zapytaj: „Co byś zrobił dalej?”
- Dodaj jeden test na formularz, który sprawdza, czy właściwe pole jest wyróżnione i czy komunikat jest czytelny.
Jeśli chcesz wdrożyć takie zachowanie bez ręcznego pisania każdego ekranu, AppMaster (appmaster.io) wspiera backendowe API oraz generowane aplikacje web i natywne mobilne. To ułatwia wykorzystanie jednego ustrukturyzowanego formatu błędów we wszystkich klientach, więc informacje przy polach pozostają spójne, gdy model danych się zmienia.
FAQ
Traktuj to jak normalną informację zwrotną formularza, a nie awarię systemu. Pokaż krótki komunikat przy konkretnym polu, które trzeba poprawić, zachowaj wpisane dane i w prosty sposób wyjaśnij następny krok.
Błąd na poziomie pola wskazuje jedno pole i mówi, co należy tam poprawić, np. „Email jest już używany.” Ogólny komunikat zmusza do zgadywania, ponownych prób i kontaktu z pomocą, bo nie wiadomo, co zmienić.
Korzystaj ze stabilnych kodów błędów dostarczanych przez sterownik bazy danych, gdy to możliwe, a następnie mapuj je na typy przyjazne użytkownikowi: unikalność, klucz obcy, wymagane pole, zakres itp. Unikaj parsowania surowego tekstu z bazy, bo zmienia się wraz ze sterownikami i wersjami.
Przechowuj prostą mapę nazwa_ograniczenia → klucz_pola_UI w backendzie, blisko miejsca, które zarządza schematem. Np. mapuj unique na email, żeby UI mógł podświetlić poprawne pole bez domysłów.
Domyślnie użyj „Ta wartość jest już używana” i podaj jasny następny krok, jak „Spróbuj inny” lub „Zaloguj się”, w zależności od kontekstu. W procesach rejestracji lub resetu hasła stosuj neutralne sformułowania, by nie potwierdzać istnienia konta.
Wyjaśnij to jako nieaktualny lub nieprawidłowy wybór rozpoznawalny dla użytkownika, np. „Ten klient już nie istnieje. Wybierz innego.” Jeśli bezpośrednio da się utworzyć powiązany rekord, zaproponuj tę opcję zamiast wielokrotnych prób.
Oznacz wymagane pola w UI i waliduj przed wysłaniem, ale zawsze traktuj bazę jako ostatnią linię obrony. Gdy NOT NULL i tak wystąpi, pokaż prosty komunikat „Wymagane” przy polu i zachowaj resztę formularza.
Zwróć tablicę błędów, z każdym elementem zawierającym klucz pola, stabilny kod i krótki komunikat, żeby UI mógł wyświetlić je wszystkie jednocześnie. Na kliencie ustaw fokus na pierwszym błędnym polu, ale nie ukrywaj pozostałych komunikatów.
Użyj spójnego formatu, który oddziela to, co widzi użytkownik, od danych do logów — np. komunikat dla użytkownika plus wewnętrzne szczegóły (nazwa ograniczenia, request ID). Nigdy nie pokazuj surowych błędów SQL w UI i nie każ UI parsować tekstów bazy.
Centralizuj tłumaczenie błędów w jednym miejscu backendu, zwracaj jednolity kształt błędu i renderuj go tak samo we wszystkich formularzach. AppMaster pozwala stosować ten sam ustrukturyzowany kontrakt błędów dla backendu oraz generowanych aplikacji web i mobilnych, co ułatwia utrzymanie spójności.


