Desenvolvido pelo Facebook e lançado como um padrão aberto para todos usarem, o GraphQL é pensado como uma alternativa às APIs REST. Assim como o REST, o GraphQL fornece uma maneira de criar e consumir APIs baseadas na web, mas as consultas e os dados retornados usam esquemas formais e um sistema de tipos para garantir a consistência.

Neste artigo, abordaremos os conceitos básicos de design e implementação de uma API GraphQL e discutiremos muitas das principais considerações e decisões que você tomará durante o processo.

Linguagens e frameworks GraphQL

Se você está planejando usar GraphQL como sua API de aplicativo web, há uma grande chance de que os componentes de linguagem e dados que você já está usando suportem seus esforços. As bibliotecas GraphQL estão disponíveis para quase todas as principais linguagens em uso de produção. Os clientes estão disponíveis para C#/.NET, Go, Java e Android, JavaScript, Swift/Objective-C e Python, e as bibliotecas de servidor cobrem ainda mais terreno.

Se você está começando inteiramente do zero, você ainda está melhor escolhendo qualquer linguagem, tempo de execução e camada de dados com os quais você esteja mais familiarizado de outros projetos. Usar GraphQL não impõe muitas restrições no servidor ou cliente, e é independente de banco de dados. No entanto, você pode precisar executar mais ou menos integração manual da sua camada de dados, dependendo do que for. (Mais sobre isso na próxima seção.)

Usaremos a implementação Python do GraphQL como referência neste artigo. Os conceitos e funcionalidades serão mais ou menos os mesmos para outras linguagens.

Esquema de consulta de dados do GraphQL

O GraphQL aceita consultas construídas a partir de campos fortemente tipados em vários arranjos hierárquicos. A parte da criação de uma API GraphQL na qual você precisa pensar mais é qual esquema fornecer para consultas.

Em muitos casos, os campos de consulta podem ser mapeados um a um para uma fonte de dados subjacente, para expor todos os campos relevantes no banco de dados (ou outra fonte de dados) para suas consultas. Como as consultas GraphQL podem ser consideravelmente mais abertas e variadas do que suas contrapartes REST, você deve planejar desde o início quais campos podem ser consultados e como eles serão mapeados para seu banco de dados.

Por exemplo, se tivermos uma tabela de banco de dados para filmes, com os campos title e year (como um inteiro), poderíamos usar uma consulta GraphQL como esta:


type Character {
    title: String!
    year: Int
}

O ! seguindo String significa que um determinado campo é obrigatório, então precisaríamos de pelo menos um título para realizar esta consulta.

Você também deve garantir que os campos que você expõe por meio do GraphQL usem tipos que correspondam corretamente aos dados subjacentes. Por exemplo, o GraphQL não tem um tipo de dado nativo “date” ou “datetime”, em grande parte devido à grande diversidade de implementações disponíveis. Se você quiser permitir pesquisas por intervalos de datas, precisará impor a formatação das datas conforme obtidas por meio da API e também garantir que essas solicitações de data sejam traduzidas em suas contrapartes adequadas para o banco de dados de back-end quando você o consultar.

Dependendo do framework que você estiver usando, esse trabalho pode já ter sido feito para você. Graphene, uma biblioteca GraphQL popular para Python, fornece valores de data e hora formatados em ISO-8601 como um tipo nativo, então você não precisa se preocupar com isso sozinho.

Se o seu conjunto de dados tiver muitos campos, comece expondo os menor subconjunto funcional daqueles campos que não exigem imposições de tipo complexas — por exemplo, consultas simples de string ou numéricas. Você pode então expandir gradualmente os campos disponíveis conforme descobre como implementar consultas para eles por meio do conector GraphQL que está usando.

Armazenando e recuperando dados GraphQL

Armazenar e recuperar dados do seu back-end normalmente usa o middleware suportado pela biblioteca GraphQL para sua linguagem.

Em muitos casos, você pode fazer com que o GraphQL execute esse trabalho por meio de camadas de dados para frameworks de aplicativos comuns. A biblioteca Graphene do Python para GraphQL, por exemplo, oferece suporte ao framework web Django, junto com o ORM integrado do Django. O Graphene também oferece suporte ao ORM SQLAlchemy e adicionou suporte para os frameworks populares Starlette e FastAPI. Ele também pode interoperar com os conectores de dados do Google App Engine e o framework Relay JavaScript (usado pelo React).

Se você estiver usando uma camada de dados que não é descrita por nenhum desses componentes, você pode usar o middleware do Graphene e os objetos DataLoader para fechar a lacuna. Eles fornecem a você lugares para conectar manualmente a integração que você precisa com sua camada de dados. Com DataLoadervocê tem uma maneira de unir várias solicitações simultâneas de dados relacionados e, assim, reduzir o número de viagens de ida e volta para seu back-end.

Nada disso, a propósito, impede que você mesmo execute o cache em qualquer camada do aplicativo. Por exemplo, as respostas que você retorna podem ser armazenadas em cache por meio de um proxy, enquanto os dados de back-end podem ser armazenados em cache usando Memcached ou Redis. Dito isso, seria sua responsabilidade garantir que esses caches sejam despejados sempre que os dados forem alterados.

Consultas e mutações GraphQL

O GraphQL usa um formato de consulta específico, chamado de “consulta de mutação”, para criar, atualizar ou excluir elementos de um conjunto de dados. Pense um pouco sobre como essas consultas funcionarão — não apenas quais consultas você permitirá e quais campos você exigirá para elas, mas também quais dados você retornará da consulta após a mutação.

Ao projetar uma consulta de mutação, você pode permitir o retorno de qualquer número de campos de saída. Dito isso, provavelmente não é uma boa ideia aninhar objetos de resposta em mais de uma ou duas camadas de profundidade, pois isso torna os resultados difíceis de analisar — ​​tanto ao olhar para a consulta em si quanto ao escrever código para manipular os resultados.

Outra ressalva importante é não deixar que os velhos hábitos de design da API REST ditem a maneira como você organiza suas consultas de mutação. Por exemplo, em vez de criar várias consultas de mutação para lidar com diferentes tipos de alterações no mesmo objeto — um padrão comum ao REST — você pode consolidá-las em uma única consulta de mutação. Uma maneira de fazer isso seria usar campos distintos e não opcionais para registrar cada operação possível, conforme o “upvote/downvote” neste exemplo.

Outra seria usar um campo de valor mais um tipo de enumeração para descrever o comportamento desejado com esse valor. Uma grande vantagem de uma enumeração é que ela é inequívoca: você pode usá-la para refletir a intenção precisamente, então ela é altamente autodocumentada. Há uma boa chance de que a biblioteca GraphQL da sua linguagem lhe dará uma maneira de usar enumerações que seja consistente com a implementação do próprio conceito da linguagem. Por exemplo, enumerações GraphQL no Graphene para Python podem se parecer muito com a biblioteca padrão do Python enum aula.

Cache GraphQL e aceleração de desempenho

Por baixo de tudo isso, uma consulta GraphQL pesquisa e recupera dados da mesma forma que qualquer outra consulta. Isso significa que ela pode ser acelerada por muitos dos mesmos métodos usados ​​para acelerar APIs de consulta:

  • Armazenamento em cache: Qualquer serviço que tenha um banco de dados como back-end, ou retorne dados de um front-end, pode se beneficiar do cache em ambas as extremidades. Tenha em mente que a responsabilidade de expirar esses caches é sua, então você provavelmente terá que usar os ganchos de middleware do framework GraphQL (como os descritos acima para o Graphene) para acionar essas coisas. É recomendado que você use identificadores exclusivos sempre que possível para dar suporte ao cache do lado do cliente.
  • Cursores e paginação: Uma solicitação deve ter algum limite superior padrão para quantos registros ela retorna de uma vez, para evitar que tanto o cliente quanto o servidor sejam inundados. Também faz sentido permitir que os clientes descrevam explicitamente o número máximo de registros a serem retornados e qual “página” de registros solicitar. A documentação oficial do GraphQL tem algumas dicas úteis sobre como integrar metáforas de paginação no formato de solicitação do GraphQL.

Ferramentas GraphQL

Além das bibliotecas disponíveis para várias linguagens, o GraphQL tem uma série de ferramentas nativas e de terceiros para facilitar o desenvolvimento de clientes, servidores, esquemas e camadas de processamento de consultas:

  • O Apollo GraphQL dedica seus recursos para criar ferramentas de código aberto para GraphQL, incluindo clientes GraphQL e servidores GraphQL. Ele também mantém o GraphQL Tools, um conjunto de utilitários para gerar e simular esquemas GraphQL e “costurar” várias APIs em uma única API — executando a missão declarada do GraphQL de consolidar vários endpoints de API e torná-los mais gerenciáveis.
  • Se você está pensando em portar uma API gerada pelo Swagger existente para o GraphQL, a ferramenta Swagger2GraphQL foi feita para o trabalho. Ela também permite a manutenção lado a lado de uma API gerada pelo Swagger legada, para que você possa usar ambos os padrões durante um período de transição.
  • Por fim, o próprio grupo GraphQL do Facebook tem algumas ferramentas que vale a pena mencionar. GraphiQL é um IDE no navegador para criar consultas GraphQL; ele pode ser usado internamente ou como uma solução pública. Há também uma implementação JavaScript do GraphQL, um servidor GraphQL sobre HTTP e suíte de clientes, e um GraphQL Language Service para IDEs.