GraphQL - QConSPGraphQL represents a massive leap forward for API development. Type safety,...
Transcript of GraphQL - QConSPGraphQL represents a massive leap forward for API development. Type safety,...
GraphQLo que, como e quando
QCon SP 2018
Introdução pessoalProblemas de APIs REST
Soluções GraphQLCaso: GitHub
Caso: Cartão Elo
Agenda
Gustavo Sverzut Barbieri
- Desenvolvedor desde os 9 anos
- Web desde 1998 (Perl, CGI…)
- UNICAMP 2001-2005
- Serviços de desenvolvimento de sw
- Apaixonado por eficiência
- Sistemas embarcados e IoT
- Sistemas Complexos
- P&D
Engenheiro de ComputaçãoProFUSION
Problemas RESTPor que criar o GraphQL?
REST: Representational State Transfer
- Modelo de Arquitetura
- Vários pontos de acesso (URL), um por recurso
- Cada operação (GET, POST, PUT…) retorna um conjunto de dados fixo
- Adicionar ou remover dados ou parâmetros quebra a API (nova versão)
REST: ilustrado
Dado portador CPF 12345678900, pegue os 4 últimos dígitos (last4) e o BIN do primeiro token do primeiro cartão.
GET /v1/card-holder/12345678900{…, "name": "José da Silva", "cards": [ "xyz", "xpto", …],…}
GET /v1/card/xyz{…, "last4": "1234", "bin": "5035", "cardHolder": "1234", "tokens": ["abc", "def", …],…}
GET /v1/card-token/abc{…, "last4": "1234", "bin": "6046", "card": "xyz", "cardHolder": "1234",…}
CardHolder Card Card
Token
GET /v1/card-holder/12345678900{…, "name": "José da Silva", "cards": [ "xyz", "xpto", …],…}
GET /v1/card/xyz{…, "last4": "1234", "bin": "5035", "cardHolder": "1234", "tokens": ["abc", "def", …],…}
GET /v1/card-token/abc{…, "last4": "1234", "bin": "6046", "card": "xyz", "cardHolder": "1234",…}
REST: ilustrado
estadotransferido(Portador)
estadotransferido(Cartão)
estadotransferido(Token)
Dado portador CPF 12345678900, pegue os 4 últimos dígitos (last4) e o BIN do primeiro token do primeiro cartão.
CardHolder Card Card
Token
REST: ilustradoGET /v1/card-holder/12345678900{…, "name": "José da Silva", "cards": [ "xyz", "xpto", …],…}
GET /v1/card/xyz{…, "last4": "1234", "bin": "5035", "cardHolder": "1234", "tokens": ["abc", "def", …],…}
GET /v1/card-token/abc{…, "last4": "1234", "bin": "6046", "card": "xyz", "cardHolder": "1234",…}
dados inúteis!
Dado portador CPF 12345678900, pegue os 4 últimos dígitos (last4) e o BIN do primeiro token do primeiro cartão.
CardHolder Card Card
Token
REST: problemas
- API bem normalizada resulta em muitas conexões HTTP;- Tráfego de dados inúteis ao aplicativo;- Endpoints otimizados para aplicativos
- Mudanças de requisitos no front-end costumam precisar de adaptações no backend;
- Falta Documentação;- Falta Validação e Garantias;- Falta ambiente de testes/playground.
REST: resolvendo problemas de ambiente
- Documentação via Swagger- Validação com JSON Schema- Testes com Postman ou cURL
REST: resolvendo problemas de execução
Extensões:- Parâmetros para controlar campos a
retornar;- Parâmetros para controlar paginação,
ordenação...;- Desnormalização para reduzir número de
consultas;… mas são específicas de fornecedores!
GET /v2/card/xyz?fields=last4,bin,tokens{…, "last4": "1234", "bin": "5035", "cardHolder": "1234", "tokens": ["abc", "def", …],…}
GET /v2/card/xyz?fields=last4,bin,tokens.limit(1)
{ "last4": "1234", "bin": "5035", "tokens": ["abc", "def", …],}
GET /v2/card-holder/1234?fields=name,cards.limit(1){last4,bin}
{ "name": "José da Silva", "cards": [{"last4": "1234, "bin": "5035"}],}
Facebook Graph API
REST: problemas com extensões
GET /v2/card-holder/1234?fields=name,cards.limit(1){last4,bin}
- Linguagem de domínio específica (DSL)- Validação- Documentação
GraphQLResolvendo problemas REST
cursor
GraphQL
- Linguagem de Consulta de Grafos- Nós: dados
- Arestas: relacionamentos
- Não é:- Protocolo de Rede
- Descrição de Banco de Dados
- Descrição de Classes em OOP
- Origem: Facebook após tratar diversos problemas com REST
Card
Token
CardHolder
Addr.
City
Zip
Name
ID
ID
cursor
GraphQL
- Declaração de tipos e consultas via Schema
- Tipagem forte
- Sempre verificado
- Documentação embutida no schema, com consulta/introspecção
- Interfaces
# Portador de cartão… type CardHolder implements Node { # Identificador Global Único … id: ID!
# Nome completo do portador … name: String
# Cartões em posse… cards( # Limita a lista às primeiras entradas… first: Int, # Inicia após o cursor opaco… after: String, # … outros argumentos … ): CardsConnection}
type Query { node(id: ID!): Node cardHolders(☰): CardHoldersConnection}
GraphQL# Portador de cartão… type CardHolder implements Node { # Identificador Global Único … id: ID!
# Nome completo do portador … name: String
# Cartões em posse… cards( # Limita a lista às primeiras entradas… first: Int, # Inicia após o cursor opaco… after: String, # … outros argumentos … ): CardsConnection}
type Query { node(id: ID!): Node cardHolders(☰): CardHoldersConnection}
comentários são armazenados como descrição,convenção por formatação Markdown
declaração de Objetos e interfaces: validação e documentação!
Todos os campos também são consultas, com tipos de retorno
Todas consultas podem ter argumentos,documentados e com tipos
GraphQL
- Consultas aninhadas- Linguagem de consultas bem definida
- Argumentos
- Variáveis
- Resultados espelham estrutura da consulta
- Fragmentos de consulta
{"data": { "node": {
"name": "João da Silva", "cards": { "edges": [ "node": { "last4": "1234", "bin": "5035" } ] } }}
query Nome($holderId: ID!) { node(id: $holderId) { ... on CardHolder { name cards(first: 1) { edges { node { last4 bin } } } } }}
query Nome($holderId: ID!) { node(id: $holderId) { ... on CardHolder { name cards(first: 1) { edges { node { last4 bin } } } } }}
GraphQL
{"data": { "node": {
"name": "João da Silva", "cards": { "edges": [ "node": { "last4": "1234", "bin": "5035" } ] } }}
operação: queryvariáveis
GraphQL
query Nome($holderId: ID!) { node(id: $holderId) { ... on CardHolder { name cards(first: 1) { edges { node { last4 bin } } } } }}
{"data": { "node": {
"name": "João da Silva", "cards": { "edges": [ "node": { "last4": "1234", "bin": "5035" } ] } }}
consulta raiz
argumentos
consultas aninhadas
resultado espelhado
query Nome($holderId: ID!) { node(id: $holderId) { ... on CardHolder { name cards(first: 1) { edges { node { last4 bin } } } } }}
GraphQL
{"data": { "node": {
"name": "João da Silva", "cards": { "edges": [ "node": { "last4": "1234", "bin": "5035" } ] } }}
fragmentos: seleção de tipo
("Cast")
GraphQL: múltiplas consultas raiz
- Consultas executadas em paralelo
- Apelidos para diferenciar consultas com argumentos diferentes
query { n0: node(id: "id0") { ... on CardInterface { last4 holder { name } } } n1: node(id: "id0") { ... on CardInterface { last4 holder { name } } } bin(number: "509069") { issuer { name } }}
{"data": { "n0": {
"last4": "1234", "holder": { "João ..." } } }, "n1": {
"last4": "2468", "holder": { "José ..." } } }, "bin": { "issuer": { "name": "Banco ...", } }}
GraphQL: múltiplas consultas raiz
query { n0: node(id: "id0") { ... on CardInterface { last4 holder { name } } } n1: node(id: "id1") { ... on CardInterface { last4 holder { name } } } bin(number: "509069") { issuer { name } }}
apelidos renomeiam retorno
em paraleloexecução
em paralelo
em paralelo
{"data": { "n0": {
"last4": "1234", "holder": { "João ..." } } }, "n1": {
"last4": "2468", "holder": { "José ..." } } }, "bin": { "issuer": { "name": "Banco ...", } }}
GraphQL: mutações
- Mutações alteram e retornam o estado
- Executadas em série- Consulta de retorno em paralelo
mutation { a0: associatePSPMerchant(☰) { pspId legalId } a1: associatePSPMerchant(☰) { pspId legalId }}
{"data": { "a0": { "pspId": "1234", "legalId": "1234...", }, "a1": { "pspId": "222", "legalId": "2345...", }}
GraphQL: mutações
mutation { a0: associatePSPMerchant(☰) { pspId legalId } a1: associatePSPMerchant(☰) { pspId legalId }}
{"data": { "a0": { "pspId": "1234", "legalId": "1234...", }, "a1": { "pspId": "222", "legalId": "2345...", }}
em paralelo
em paralelo
executado primeiro
executado depois
GraphQL: erros
- Consultas retornam: null- Até o primeiro elemento
null-able- Motor sempre garante retorno
correto- Listagem de erros
query { a: bin(number: "invalid") { issuer { name } } b: bin(number: "509069") { issuer { name # server bug! } } c: bin(number: "509069") { issuer { url # server bug! } }}
{"data": { "a": null,
"b": null,
"b": { "issuer": { "url": null } } }, "errors": [ {"message": "Bin inválido", "path": ["a"] }, {"message": "Campo null!", "path": ["b", "issuer", "name"] } {"message": "Server Bug", "path": ["c", "issuer", "url"] } ] }
- bin() pode retornar null- BIN { issuer } é não-nulo- CardIssuer { name } é não-nulo- CardIssuer { url} é null-able
Relay
- Relay: adiciona GraphQL a clientes React.JS;- Foco em desempenho e facilidade de uso;- Uso extensivo de fragmentos;- Convenções adicionais:
- Identificação de Objetos: cache e atualização
- Conexões: paginação
- Mutações: previsibilidade e idempotência
https://facebook.github.io/relay/docs/en/graphql-server-specification.html
adotados por todos os frameworks,cliente e servidor: Apollo, Graphene...
Relay: Identificação de Objetos
- Interface declara objeto com identificação global;- Permite cache global do aplicativo;- Fragmentos obtém dados, populando cache;- Relay mantém o cache e informa utilizadores sobre atualizações
interface Node { id: ID!}
type Query { node(id: ID!): Node}
cache
React Components
Relay: Conexões
- Conexões de Nós do Grafo;- Arestas podem conter mais informações, ex: data da associação, custo…- Parâmetros de paginação;- Informações de paginação;- Cursores opacos.
type CardsConnection { edges: [CardsEdge] pageInfo: PageInfo! # outros campos que achar conveniente totalCount: Int}type CardsEdge { node: Card cursor: String! # opaco # outros campos que achar conveniente}type PageInfo { hasPreviousPage: Boolean! hasNextPage: Boolean! startCursor: String endCursor: String}
type Query { cards( first: Int # limita elementos no retorno after: String # cursor de início last: Int # limita elementos no retorno before: String # cursor de término filter: CardFilterInput # filtros ): CardsConnection}
Relay: Mutações- Assinatura com nomenclatura padrão;- clientMutationId para reconciliação e idempotência;
type ActivateCardTokenInput { clientMutationId: String # opaco # outros campos que achar conveniente cardTokenId: String sensitive: String}type ActivateCardTokenPayload { clientMutationId: String # opaco # outros campos que achar conveniente cardToken: CardToken # null se não existe}type Mutation { activateCardToken( input: ActivateCardTokenInput ): ActivateCardTokenPayload}
activateCardToken(input:{clientMutationId: "x", ☰})
activateCardToken(input:{clientMutationId: "x", ☰})
executa
cliente servidor
detectaréplica
tenta novamente
GraphQL: Benefícios
- Schema- Linguagem de Domínio Específico (DSL) fácil e bem documentada- Tipagem forte e garantida pelo motor- Consultas de introspeção built-in (__schema, __type)- Documentação faz parte do Schema- Múltiplas consultas ou mutações por requisição
GraphQL: Playground - GraphiQL
https://github.com/graphql/graphiql
GraphQL: não...
GraphQL não especifica:- Transporte, em geral HTTP;- Segurança, em geral TLS (HTTPS);- Serialização de dados, em geral JSON;- Autenticação e Autorização, em geral OAuth v2 via HTTP + Headers.
GitHubCaso de Sucesso
API v4, Setembro de 2016
- Escalabilidade
- Flexibilidade
- Paginação
- Tipagem
- Documentação
GitHub: motivos da mudança
- API REST responsável por 60% dos acessos ao DB- Resultados continham muitos dados inúteis, "*_url" para navegação…- Integradores reclamavam de falta de dados úteis- 2-3 requisições para visualização completa de um recurso- Complicado manter documentação correta, com tipos e garantias
GitHub: a mudança - Primeiro objetivo: reações Emoji nos comentários- Foi necessário modelar uma grande parte do sistema:
- Usuário
- Repositório
- Issues / Pull Requests
- Comentários
- Ajuda: aliados no time de frontend (uso de React & Relay)- Em produção concomitantemente com REST- Bons resultados = v4!
User
Repository
IssuesPR
Comments
Reactions
GitHub: o que disseram no anúncio
GraphQL represents a massive leap forward for API development.
Type safety, introspection, generated documentation, and predictable responses
benefit both the maintainers and consumers of our platform. We’re looking forward to our new era of a GraphQL-backed platform, and we hope that you do, too!
https://githubengineering.com/the-github-graphql-api/
Schema!
GitHub: justificativa da mudança
GitHub chose GraphQL for our API v4 because it offers significantly more flexibility for our integrators. The ability to define precisely the data you want—and only the data you want—is a powerful advantage over the REST API v3 endpoints. GraphQL lets you replace multiple REST requests with a single call to fetch the data you specify.
https://developer.github.com/v4/#why-is-github-using-graphql
Eficiência & Flexibilidade
Cartão EloCaso de Sucesso - Brasil
Outubro de 2017
- GraphQL desde o início
- Várias unidades de negócios
- Uniformidade
- Experiência do Desenvolvedor
https://dev.elo.com.br/
Experiência do Desenvolvedor
Design APIPortal Dev
FuncionamentoAPI Gateway
Backend
Cartão Elo
Experiência de Usuário:
- Suporte Nacional- Segurança- Documentação- Facilidade de Uso- Extensível
- Moto: API First
- Consumo interno e externo
- Diversas Áreas de Negócios:- Cadastro de Portadores- Seguros- Tabela de Bins- Precificação- Tokenização- Histórico de Transações- ...
GraphQL!
Cartão Elo
https://dev.elo.com.br/documentacao/tabela-de-bins#tabela-de-bins/Consultas
Playground similar ao GraphiQL
Documentação gerada via Schema
Cartão Elo
BINCard Holder
Card Card Token
CardIssuer
Card Brand
Card Network
Card Usage
Merchant
Address
o que? dados como grafo
como? navegando informação
quando? eficiência e facilidade