Tecnologia / Artigos / GraphQL /
Um sistema de erros para o GraphQL

Cléber

![caminhao-dos-bombeiros.jpg](/files/162) *Photo by [Sergi Kabrera](https://unsplash.com/@skabrera) on [Unsplash](https://unsplash.com/s/photos/siren)* --- Muitas são as propostas para implementação de formas de alertar os usuários de uma API GraphQL sobre **erros**. Alguns apostam no "padrão" de criar um *array* de `errors`, como boa parte das bibliotecas fazem, mas esse formato é geralmente confuso e tem foco muito mais em erros de sintaxe do que em informar os clientes a respeito dos reais problemas e, especialmente, ligar adequadamente os erros às consultas. Minha sugestão é a seguinte: ```graphql # Uma query de exemplo: type Query { getAppTokens( userHash: String! portalHash: String! ): AppTokenResponse } # Retorno de exemplo: type AppTokenResponse { status: RequestStatus! data: [AppToken] } # Indicador de status da requisição: type RequestStatus { code: Int! message: String } ``` # status e data Se tudo der certo, o campo `data` virá preenchido com o que quer que tenha sido pedido pelo cliente. Mas, como você pode reparar, **esse campo não é garantido** (uso o termo "garantido" ao invés de "obrigatório" porque, do ponto de vista do cliente, é uma garantia, não uma obrigação). Só o que o backend garante entregar é o campo `status`, e dentro desse campo, só o que é garantido é o campo `code`. ## data Como separamos `data` em um campo à parte, **temos toda a liberdade para definir campos garantidos na resposta**, já que esse campo só será preenchido em caso de sucesso. Repare que se tivéssemos deixado tudo misturado (tanto informações de retorno quanto informações sobre o sucesso ou não da requisição), praticamente nenhum campo da resposta poderia ser garantido. Se houver qualquer erro, `data` terá valor `null`. ## status Se `code` for **zero** (`0`), é porque a requisição foi respondida com sucesso. Alguns podem achar estranho usar `0` para isso, porque zero dá, digamos, uma sensação de vazio, coisa que dificilmente representaria "sucesso". Porém, lembre-se: só existe 1 jeito de as coisas darem certo (que é: "*deu certo*"), mas existem **N motivos** para as coisas darem errado ("*disco cheio*, "*falta de memória*", "*dados inválidos*", "*falha no servidor*", et cetera). Tenho sugerido o seguinte padrão para esses códigos de erro: `CCHHHXXXX`. ### CC A **categoria** do erro, começando com `10` (para o código sempre ter 9 dígitos). As categorias são diversas, como; * Recursos locais (memória, sistema operacional, threads, processos) * I/O local * I/O de rede * Banco de dados * Serviços externos ### HHH O **código HTTP** mais apropriado para o caso. Se não houver nenhum apropriado, usar `000`. Por exemplo: se é um problema irrecuperável que não tem nada a ver com o que o cliente enviou, `500`. Se o cliente enviou dados inválidos, `400`. ### XXXX Um "índice local" que indique para o desenvolvedor em que ponto da aplicação ocorreu o erro. Cada um é livre para implementar seu próprio padrão e eu tenho usado o padrão `AABB`, sendo `AA` uma espécie de categoria, módulo ou região do código e `BB` o código local. ## No news = good news Se o status é zero, **não há mensagem**. Entenda: mensagens são problemas. Não somente "indicam" problemas, mas geram problemas. Você certamente escolherá um idioma para elas, então **já está tomando decisões em nome dos clientes**. Eles não pediram para ler mensagens em inglês, mas **você** escolheu que seria assim (felizes são as equipes que tem o tempo e recursos necessários para implementar seleção de idioma na API). Logo, ao invés de ficar se intrometendo na vida dos outros, simplesmente dê um passo para trás e só exiba qualquer mensagem quando for realmente necessário. E mais: **exiba mensagens que ajudem o cliente a entender onde errou**. Hoje mesmo eu passei um sufoco com uma biblioteca que dava mensagens como `Error: AppToken is not a pointer`. Que diabos isso significa? Que eu disse que era um ponteiro mas não deveria ser? Ou eu fiz ser um ponteiro quando não deveria ser? Melhoria imensa ocorreria se essa mensagem fosse: `Error: AppToken should be a pointer`. Viu? Simplesmente usar as palavras corretas já faz toda a diferença. ## Mensagens são para desenvolvedores Não cabe ao backend escolher que tipo de linguagem e linguajar serão usados para comunicação com o usuário final. **Isso é papel do frontend**, seja ele qual for. Se vai mostrar mensagem em português, se vai dar muitos detalhes ou não, se vai falar como um estadista inglês ou como um torcedor do XV de Jaú vendo jogo com os amigos no bar, isso tudo é responsabilidade do *frontend*. *Backend* implementa, via GraphQL, **comportamento**. Essas mensagens de erro servem para ajudar **os desenvolvedores** do *front* a entenderem o que acontece enquanto programam. # Adendo: só trate erros como erros Se o cliente pede uma lista de determinada entidade e não há nenhuma no banco de dados, **isso não é um erro**. O *backend* simplesmente retornará uma lista vazia. Evite tratar qualquer comportamento fora do "caminho feliz" como um erro. Às vezes não há nada para mostrar. Às vezes há coisas demais para enviar de uma vez só. E tudo isso é normal. Se a distinção entre erros e comportamentos normais for se perdendo na cabeça do cliente da sua API, logo você estará "classificando severidade" de erros -- que é como tratar tudo como urgente e acabar com listas de "pouco urgente", "muito urgente" e "muito muito urgentíssimo". É sinal que algo está conceitualmente errado.

Curti

37 visitantes curtiram esse Item.

Anterior: GraphQL versus REST | Próximo: Artigos / Diretrizes de Desenvolvimento