Como desenvolvedor front-end e Jamstacker de longa data, tive tempo mais do que suficiente para ficar frustrado com a forma como usamos APIs. O protocolo REST parecia um passo na direção certa (e era), mas eu ainda reclamei ingrata sobre suas limitações, apesar da melhoria.
Então, quando ouvi sobre GraphQL, fiquei impressionado.
A ideia é simples: a própria API define que tipo de dados ela pode entender e expõe um único endpoint para o usuário. Em seguida, o usuário fornece uma consulta a esse endpoint que se parece com JSON sem todos os valores incômodos, aspas e vírgulas.
A API retorna uma versão JSON dessa consulta com os valores preenchidos com todos os dados Você perguntou por. É uma ideia incrivelmente simples, mas resolve praticamente todos os problemas que já tive com APIs.
O que é Ariadne?
Normalmente, APIs GraphQL são criadas com JavaScript, mas meu O primeiro amor é Python, e é por isso que olhei para Ariadne. Ariadne é uma biblioteca Python que ajuda você a criar uma API GraphQL sem o peso extra.
Neste artigo, documentarei o processo de criação de uma API Ariadne GraphQL em Python 3.8, que dará ao usuário acesso a uma única estrutura simples de matriz/dicionário.
Introdução ao Ariadne
Presumo que você já tenha o Python configurado em seu computador e já instalei o Ariadne com pip3 install ariadne.
Gostaria de lhe dar uma pequena observação aqui: fique com uma única fonte de dados (como um banco de dados, uma camada de lógica de negócios ou um dicionário Python). Quando ouvi pela primeira vez sobre GraphQL, meu primeiro pensamento foi que poderia usá-lo para combinar todas as outras APIs que estou usando em um único endpoint-que poderia me livrar de todas as inconsistências das APIs REST e SOAP e obter todos os dados e a funcionalidade que eu precisava sem nenhum problema.
Isso é possível, mas é muito mais problemático do que vale a pena fazer o seu próprio. Esse conceito é chamado de API Mesh, e foi desenvolvido pelo pessoal da TakeShape.io . Se você estiver interessado em aprender mais sobre TakeShape, sinta-se à vontade para verificar a nova página de documentos , mas Vou me limitar a expor uma única fonte de dados aqui para simplificar.
Como Ariadne funciona
Agora que o clichê está fora do caminho, vamos ver como Ariadne funciona. Você pode acompanhar o guia de início rápido , mas vou simplificá-lo. É mais ou menos assim:
Primeiro, use a linguagem de definição de esquema especial do GraphQL para definir um tipo. É semelhante a uma interface TypeScript, onde você define as chaves de um objeto e os tipos de valores de cada chave.
Cada aplicativo em Ariadne precisa de um tipo chamado Query, pois ele será comparado com o entrada do programa, então vamos fazer isso agora. Será mais ou menos assim:
digite Query {hello: String! }
Essa é uma definição realmente básica. Simplificando, definimos um tipo chamado Consulta. Ele tem uma chave, chamada hello, que sempre será uma string. E aqui está um bônus: o! No final dessa linha significa que olá sempre estará em um objeto se o objeto estiver em conformidade com este tipo. Se você omitiu o ponto de exclamação, então hello seria opcional.
Agora, em nosso arquivo Python (vou chamá-lo de endpoint.py), vamos colocar essa definição de tipo em uma string e passe-a para a função gql de Ariadne. Até agora, nosso arquivo se parece com isto:
from ariadne import gql typedefs=”””type Query {hello: String!}”””Typedefs=gql (type_defs)
Isso vai validar nossa definição de tipo e lançar um erro se não o escrevermos corretamente.
Em seguida, Ariadne quer que criemos uma instância da classe ObjectType e passemos o nome do nosso tipo. Resumindo, esta será a representação Python do tipo que estamos fazendo.
Também vamos adicionar alguns clichês no final e mover nossa definição de tipo para lá. Agora endpoint.py se parece com isto:
de ariadne import ObjectType, gql, make_executable_schema de ariadne.asgi import GraphQL basetype=ObjectType (“Query”) # há um atalho para isso, mas explícito é melhor do que implícito type_defs=”””type Query {hello: String!}”””app=GraphQL (make_executable_schema (gql (type_defs), basetype), debug=True)
O objetivo principal de Ariadne é verificar a consulta de entrada e, para cada chave, execute uma função de resolução para obter o valor dessa chave. Ele faz isso com decoradores, uma maneira Pythônica legal de dar sua função a Ariadne sem mais clichês. Aqui está nosso endpoint.py com uma função de resolução para nossa chave hello:
from ariadne import ObjectType, gql, makeexecutableschema from ariadne.asgi import GraphQL basetype=ObjectType (“Query”) type_defs=”””type Query {hello: String!}”””@ Basetype.field (“hello”) def resolve_hello (obj, info): return”Olá, mundo!”app=GraphQL (makeexecutableschema (gql (type_defs), basetype), debug=True)
É basicamente isso. Ariadne tem muitos recursos fascinantes e úteis (sério, folheie seus documentos ), mas isso é tudo que você precisa para obter começou e entender como funciona. Se você estiver interessado em testar isso, no entanto, ele precisa ir para um servidor.
Você pode transformar sua máquina local temporariamente em uma usando Uvicorn . Resumindo, você deseja instalar com pip install uvicorn, fazer cd para a pasta onde está seu endpoint.py e executar uvicorn endpoint: app. Em seguida, visite 127.0.0.1:8000, onde você verá a interface GraphQL de Ariadne. Parece legal:
Há apenas uma advertência: a página do documento de introdução que segui aproximadamente aqui faz um bom ponto na metade do caminho. “Os resolvedores do mundo real raramente são tão simples: eles geralmente leem dados de alguma fonte, como um banco de dados, entradas de processo ou valor de resolução (sic) no contexto de um objeto pai.”
Tradução em simples Inglês? “Nossa API não faz absolutamente nada útil. Você faz uma consulta e ele diz: Olá, mundo !, o que não é engraçado nem útil. A função de resolução que criamos precisa receber entradas, obter dados de algum lugar e retornar um resultado que valha muito. ”
Bem, agora que temos nosso padrão, vamos tentar fazer com que esta API valha a pena seu peso em sal acessando um banco de dados rudimentar feito de arrays e dicionários Python.
Construindo um exemplo de API GraphQL
Hmm… o que devemos construir? Aqui está o que estou pensando:
A consulta de entrada deve ter o nome slugged de uma das minhas sitcoms favoritas como parâmetro A consulta retornará um tipo de Sitcom, que deve ter campos para o nome (que seria uma string) , number_of_seasons (Int) e characters (uma matriz de caracteres) O tipo de caractere terá os campos first_name, last_name e actor_name, todos eles strings
Isso parece factível! Teremos apenas dois tipos (sitcom e personagem), e os dados que estamos expondo podem ser facilmente armazenados em uma estrutura de dicionário Python. Aqui estão os dicts que estarei usando:
characters={“jeff-winger”: {“first_name”:”Jeffrey”,”last_name”:”Winger”,”actor_name”:”Joel McHale”},”michael-scott”: {“first_name”:”Michael”,”last_name”:”Scott”,”actor_name”:”Steve Carell”},…} sitcoms={“office”: {“name”:”The Office (US)”,”number_of_seasons”: 9, # mas vamos ser reais, 7″characters”: [“michael-scott”,”jim-halpert”,”pam-beesly”,”dwight-schrute”,…]},”community”: {“name”:”Community”,”number_of_seasons”: 6, #sixseasonsandamovie”characters”: [“jeff-winger”,”britta-perry”,”abed-nadir”,”ben-chang”,…]},…}
Queremos definir nossos tipos como fizemos anteriormente com nosso tipo de consulta. Vamos tentar:
query=ObjectType (“Query”) sitcom=ObjectType (“Sitcom”) character=ObjectType (“Character”) type_defs=”””type Query {result (name: String!): Sitcom} tipo Sitcom {name: String! number_of_seasons: Int! characters: [Character!]!} type Character {first_name: String! last_name: String! actor_name: String!}”””app=GraphQL (make_executable_schema (gql (type_defs), query , sitcom, character), debug=True)
Entre parênteses está o tipo de consulta, que é um argumento. Estamos passando um nome (que sempre será uma string) para a chave de resultado do tipo de consulta e que será enviado ao nosso resolvedor. Vou pular um pouco mais nisso em um segundo.
Caso você esteja se perguntando sobre esse [Personagem!]! bit, isso significa apenas que o array é obrigatório, bem como os caracteres dentro dele. Na prática, a matriz deve estar presente e deve conter caracteres.
Além disso, no boilerplate no final, estamos passando todos os três tipos para a função make_executable_schema. Isso diz a Ariadne que ele pode começar a usar os dois. Na verdade, poderíamos adicionar quantos tipos quisermos.
Então, é assim que funciona. O cliente enviará uma solicitação parecida com esta:
{resultado (nome:"comunidade")}
O servidor vai levar isso, enviar”comunidade”para o resolvedor para o campo de resultado e retorna não qualquer sitcom, mas o sitcom certo. Vamos construir esses resolvedores agora.
Aqui está nosso endpoint.py completo:
de ariadne import ObjectType, gql, make_executable_schema de ariadne.asgi import GraphQL import json com open (‘sitcoms.json’) como sitcom_file: sitcom_list=json.loads (sitcom_file.read ()) com open (‘characters.json’) como character_file: character_list=json.loads (character_file.read ()) query=ObjectType (“Query”) sitcom=ObjectType (“Sitcom”) character=ObjectType (“Character”) type_defs=”””type Query {result (name: String!): Sitcom} type Sitcom {name: String! Number_of_seasons: Int! Characters: [Character!]!} Type Character {first_name: String! Last_name: String! Actor_name: String!}”””@ Query.field (“result”) def getSitcom (* _, nome): return sitcom_list [nome] se nome em sitcom_list else Nenhum @sitcom. campo (“caracteres”) def getCharacters (sitcom, _): characters=[] para nome em sitcom [“characters”]: characters.append (character_list [name] if name in character_list else None) caracteres de retorno app=GraphQL (make_executable_schema (gql (type_defs), query, sitcom, character), debug=True)
Esse é o programa completo! Estamos usando os dados dos arquivos JSON para preencher as respostas às consultas de entrada do GraphQL.
Benefícios adicionais do uso do Ariadne
Mas não precisamos terminar! Aqui estão algumas ideias da minha cabeça sobre o que fazer a seguir.
Estávamos apenas usando uma estrutura de armazenamento de dados JSON rudimentar, o que é uma prática ruim, mas razoável para um aplicativo de amostra como este. Para qualquer coisa maior do que este app de brinquedo, gostaríamos de usar uma fonte de dados mais robusta, como um banco de dados adequado.
Poderíamos ter um banco de dados MySQL com uma tabela para cada sitcoms e personagens e buscar esses dados nas funções do resolvedor. Além disso, as consultas em si são apenas metade do que podemos fazer com GraphQL e Ariadne. As mutações são a outra metade. Eles permitem que você atualize registros existentes, adicione novos ou, potencialmente, exclua linhas. Eles são bastante fáceis de configurar em Ariadne .
Claro, criar uma API manter o controle de sitcoms e personagens é um pouco inútil, mas é uma experiência divertida. Tudo isso poderia ser usado de forma mais produtiva se construíssemos um serviço GraphQL como esse em torno de dados mais úteis. Digamos que você esteja executando uma API REST existente-por que não fornecer esses dados com GraphQL?
Finalmente, quando criamos uma API GraphQL, muitas vezes é tentador tentar buscar dados de um banco de dados nosso e mesclar em dados de uma fonte externa, como alguma API de terceiros. Você pode fazer isso fazendo solicitações a essas APIs externas sobre HTTP nos resolvedores, mas isso reduzirá significativamente o seu programa e deixará você se preocupando com casos extremos e tratamento de erros.
Acredite em mim, é mais problemas do que vale a pena. No entanto, para levar este projeto adiante, você pode fazer seu aplicativo Ariadne buscar dados de seu banco de dados interno, conectar a API que você acabou de criar em uma malha de API (como TakeShape) e, em seguida, combiná-la com algum outro serviço de terceiros lá.
Dessa forma, todas as coisas difíceis de mesclar são problema da malha, não seu. Já fiz isso várias vezes e é gratificante ver tudo se encaixar.
Conclusão
Não há muito nisso. Tentei explicar o máximo de detalhes que pude, caso você queira se ramificar e explorar mais algum desses pontos, mas a técnica é bastante simples.
Você pode construir praticamente qualquer coisa que imaginar. Você provavelmente encontrará alguns obstáculos, mas Ariadne tem uma comunidade maravilhosa no GitHub pronta para ajudar . Desejo a você o melhor em suas aventuras de Ariadne!