Error Handling in GraphQL Queries: Best Practices e Implementazione
La gestione degli errori (Error Handling) è una parte fondamentale nello sviluppo di API robuste e affidabili. In GraphQL, la gestione degli errori è progettata per fornire un feedback chiaro e dettagliato ai client, mantenendo al tempo stesso la capacità di rispondere parzialmente a query complesse. In questa guida, esploreremo come gestire gli errori nelle query GraphQL, come utilizzare gli errori personalizzati e come implementare best practices per un feedback efficace.
1. Come Funziona l’Error Handling in GraphQL?
In GraphQL, quando si verifica un errore durante l’esecuzione di una query, l’errore viene incluso nella risposta sotto la chiave errors
, mentre i dati validi che sono stati recuperati con successo sono inclusi nella chiave data
. Questo approccio permette ai client di ricevere risposte parziali anche quando una parte della query fallisce.
1.1. Esempio di Risposta con Errore
{
"data": {
"user": null
},
"errors": [
{
"message": "User not found",
"locations": [{ "line": 2, "column": 3 }],
"path": ["user"]
}
]
}
In questo esempio, il campo user
ha generato un errore, quindi è impostato su null
, e l’errore è descritto nella chiave errors
.
2. Tipi di Errori in GraphQL
2.1. Errori di Validazione
Gli errori di validazione si verificano quando una query non è conforme allo schema GraphQL. Questi errori possono riguardare campi inesistenti, tipi non corretti, o query malformate.
Esempio di Errore di Validazione
Se un client invia una query con un campo non esistente:
query {
user(id: "1") {
id
name
age
nonExistingField
}
}
La risposta potrebbe essere:
{
"errors": [
{
"message": "Cannot query field 'nonExistingField' on type 'User'.",
"locations": [{ "line": 5, "column": 5 }]
}
]
}
2.2. Errori di Esecuzione
Gli errori di esecuzione si verificano durante la risoluzione di una query valida. Questi errori possono includere problemi come l’accesso a risorse non esistenti, problemi di autorizzazione, o eccezioni durante l’accesso al database.
Esempio di Errore di Esecuzione
Se una query cerca di recuperare un utente che non esiste:
query {
user(id: "99") {
id
name
}
}
E il resolver non trova l’utente:
{
"data": {
"user": null
},
"errors": [
{
"message": "User not found",
"locations": [{ "line": 2, "column": 3 }],
"path": ["user"]
}
]
}
3. Utilizzare Errori Personalizzati
Gli errori personalizzati consentono di fornire messaggi di errore più specifici e utili, che possono aiutare i client a comprendere meglio il problema e a reagire in modo appropriato.
3.1. Creare un Errore Personalizzato in Apollo Server
In Apollo Server, puoi utilizzare ApolloError
per creare errori personalizzati con codici di errore specifici.
Esempio di Implementazione
const { ApolloError } = require("apollo-server");
const resolvers = {
Query: {
user: (parent, args, context) => {
const user = users.find((user) => user.id === args.id);
if (!user) {
throw new ApolloError("User not found", "USER_NOT_FOUND", {
invalidArgs: args.id,
});
}
return user;
},
},
};
In questo esempio, se l’utente non viene trovato, viene lanciato un errore ApolloError
con un messaggio personalizzato e un codice di errore (USER_NOT_FOUND
).
3.2. Fornire Dettagli Aggiuntivi negli Errori
Puoi aggiungere informazioni extra agli errori, come gli argomenti che hanno causato l’errore o dettagli sul contesto.
throw new ApolloError("User not found", "USER_NOT_FOUND", {
invalidArgs: args.id,
hint: "Check if the user ID is correct",
});
4. Gestione degli Errori a Livello di Middleware
In Apollo Server, puoi intercettare e gestire gli errori a livello globale utilizzando middleware o funzioni di callback come formatError
.
4.1. Utilizzare formatError
per Personalizzare gli Errori
formatError
ti permette di modificare gli errori prima che vengano inviati al client.
Esempio di Utilizzo di formatError
const server = new ApolloServer({
typeDefs,
resolvers,
formatError: (err) => {
// Personalizza l'errore per il client
if (err.extensions.code === "INTERNAL_SERVER_ERROR") {
return new Error("An unexpected error occurred");
}
return err;
},
});
In questo esempio, gli errori con il codice INTERNAL_SERVER_ERROR
vengono sostituiti con un messaggio generico, nascondendo i dettagli dell’errore.
4.2. Logging degli Errori
Puoi loggare gli errori per scopi di monitoraggio e debug utilizzando middleware.
const server = new ApolloServer({
typeDefs,
resolvers,
formatError: (err) => {
console.error(`[Error]: ${err.message}`);
return err;
},
});
5. Best Practices per l’Error Handling in GraphQL
5.1. Fornire Messaggi di Errore Utili
Assicurati che i messaggi di errore siano chiari e forniscano abbastanza dettagli per aiutare i client a risolvere il problema, senza esporre dettagli sensibili del server.
5.2. Mantenere la Consistenza nei Codici di Errore
Utilizza codici di errore consistenti in tutta l’API per facilitare il riconoscimento e la gestione degli errori da parte dei client.
5.3. Loggare gli Errori Critici
Logga sempre gli errori critici, come errori di accesso al database o problemi di autenticazione, per permettere un rapido debug e risoluzione.
5.4. Non Esporre Informazioni Sensibili
Evita di includere informazioni sensibili o dettagli del server nei messaggi di errore che vengono restituiti ai client. Usa formatError
per filtrare o modificare i messaggi di errore quando necessario.
5.5. Restituire Risultati Parziali Quando Possibile
Permetti ai client di ottenere risultati parziali anche se una parte della query fallisce, mantenendo l’API il più resiliente possibile.
Conclusione
Gestire gli errori in GraphQL richiede un approccio ben strutturato per garantire che l’API sia robusta e affidabile. Utilizzando errori personalizzati, gestendo gli errori a livello di middleware, e seguendo le best practices, puoi costruire un sistema che fornisce feedback utili ai client, semplificando la diagnosi e la risoluzione dei problemi.