Pagini de eroare personalizate din React cu GraphQL și granițele de eroare

Pagina minunată de eroare a lui GitHub

Dacă vă place acest articol, vă rugăm să mă susțineți verificând Pull Reminders, un bot Slack care vă trimite echipa de mementouri automate pentru solicitările de tragere GitHub.

O provocare în care m-am confruntat recent în timp ce lucram cu GraphQL și React a fost modul de a gestiona erorile. În calitate de dezvoltatori, am implementat probabil 500, 404 și 403 de pagini implicite în aplicațiile prestate de server înainte, dar să ne gândim cum să facem asta cu React și GraphQL este dificil.

În această postare, voi vorbi despre modul în care echipa noastră a abordat această problemă, soluția finală pe care am implementat-o ​​și lecții interesante din specimenul GraphQL.

fundal

Proiectul la care lucram a fost o aplicație CRUD destul de tipică, construită în React folosind GraphQL, Apollo Client și Express-GraphQL. Am dorit să gestionăm anumite tipuri de erori - de exemplu, serverul fiind jos - afișând o pagină de eroare standard către utilizator.

Provocarea noastră inițială a fost să descopăr cel mai bun mod de a comunica erorile către client. GraphQL nu folosește coduri de stare HTTP precum 500, 400 și 403. În schimb, răspunsurile conțin un tablou de erori cu o listă de lucruri care au mers greșit (citiți mai multe despre erorile din specificațiile GraphQL).

De exemplu, iată cum a arătat răspunsul nostru GraphQL când s-a spart ceva pe server:

Întrucât răspunsurile de eroare GraphQL returnează codul de stare HTTP 200, singura modalitate de a identifica tipul de eroare a fost de a inspecta tabloul de erori. Aceasta părea o abordare slabă, deoarece proprietatea mesajului de eroare conținea excepția aruncată pe server. Specificația GraphQL precizează că valoarea mesajului este destinată dezvoltatorilor, dar nu specifică dacă valoarea ar trebui să fie un mesaj care poate fi citit de oameni sau ceva conceput pentru a fi gestionat programatic:

Fiecare eroare trebuie să conțină o intrare cu mesajul cheie, cu o descriere string a erorii destinate dezvoltatorului, ca ghid pentru a înțelege și corecta eroarea.

Adăugarea codurilor de eroare la răspunsurile GraphQL

Pentru a rezolva acest lucru, am adăugat coduri de eroare standardizate la obiectele noastre de eroare, care ar putea fi folosite de clienți pentru a identifica programatic erorile. Acest lucru a fost inspirat de modul în care API-ul REST de la Stripe returnează coduri de eroare în șir, pe lângă mesajele care pot fi citite de oameni.

Am decis să începem trei coduri de eroare: autentificare_error, resursă_not_fond și server_error.

Pentru a le adăuga la răspunsurile noastre GraphQL, am trecut propria funcție formatError la expres-graphql care mapează excepțiile aruncate pe server la codurile standard care se adaugă la răspuns. Specificația GraphQL descurajează, în general, adăugarea de proprietăți la obiectele de eroare, dar permite acest lucru prin cuibarea respectivelor intrări într-un obiect de extensii.

Erorile noastre de răspuns GraphQL au fost apoi ușor de clasificat:

În timp ce ne-am dezvoltat propriul mod de adăugare de coduri la răspunsurile generate de expres-graphql, apollo-server pare să ofere un comportament integrat similar.

Redarea paginilor de eroare cu Reactarea erorilor de eroare

După ce ne-am dat seama de o modalitate bună de a gestiona erorile din serverul nostru, ne-am îndreptat atenția către client.

În mod implicit, am dorit ca aplicația noastră să afișeze o pagină de eroare globală (de exemplu, o pagină cu mesajul „oops a mers ceva”) ori de câte ori am întâlnit un server_error, permission_error sau permission_not_found. Cu toate acestea, am dorit și flexibilitatea pentru a putea gestiona o eroare într-o componentă specifică dacă am fi dorit.

De exemplu, dacă un utilizator tasta ceva într-o bară de căutare și ceva nu merge bine, am dorit să afișăm un mesaj de eroare în context, mai degrabă decât să flash pe o pagină de eroare.

Pentru a realiza acest lucru, am creat mai întâi o componentă numită GraphqlErrorHandler, care ar sta între componentele de interogare și mutație ale clientului Apollo și copiii lor pentru a fi redate. Această componentă a verificat codurile de eroare din răspuns a aruncat o excepție dacă a identificat un cod de care ne pasă:

Pentru a utiliza GraphqlErrorHandler, am înfășurat componentele de interogare și mutație ale clientului Apollo:

Componenta noastră de caracteristici a folosit apoi propria componentă de interogare în loc să acceseze direct react-apollo:

Acum, când aplicația noastră React arunca excepții atunci când serverul a returnat erori, am vrut să gestionăm aceste excepții și să le mapăm la comportamentul adecvat.

Amintiți-vă mai devreme că obiectivul nostru a fost implicit să afișăm pagini globale de eroare (de exemplu, o pagină cu mesajul „oops ceva a mers prost”), dar totuși să aveți flexibilitatea de a gestiona o eroare locală în cadrul oricărei componente, dacă dorim.

Reactarea limitelor de eroare oferă un mod fantastic de a face acest lucru. Limitele erorilor sunt componente de reacție care pot prinde erori JavaScript oriunde în arborele lor de componente pentru copii, astfel încât să le poți gestiona cu un comportament personalizat.

Am creat o limită de eroare numită GraphqlErrorBoundary care ar capta orice excepție legată de server și va afișa pagina de eroare corespunzătoare:

Folosim limita de eroare ca un înveliș pentru toate componentele aplicației noastre:

Limitele de eroare pot fi utilizate mai adânc în arborele de componente dacă dorim să gestionăm erorile dintr-o componentă în loc să redăm o pagină de eroare.

De exemplu, iată ce ar arăta dacă am fi dorit un comportament de gestionare a erorilor personalizate în componenta noastră de mai devreme:

Învelire

GraphQL este încă relativ nouă, iar gestionarea erorilor este o provocare comună pe care dezvoltatorii par să o facă. Folosind coduri de eroare standardizate în răspunsurile noastre GraphQL, putem comunica clienților erori într-un mod util și intuitiv. În aplicațiile noastre React, limitele de eroare oferă o modalitate excelentă de a standardiza comportamentul de gestionare a erorilor aplicației noastre, având în același timp flexibilitate atunci când avem nevoie de ea.