O autor escolheu o Fundo Livre e de Código Aberto para receber uma doação como parte do programa Write for DOnations.
Introdução
GraphQL é uma solução moderna para facilitar a comunicação entre um front end e uma fonte de dados. Todos os detalhes e capacidades de uma implementação GraphQL são delineados no Esquema GraphQL. Para escrever um esquema GraphQL funcional, é necessário entender o Sistema de Tipos GraphQL.
Neste artigo, você aprenderá sobre os tipos GraphQL: os cinco tipos escalares built-in, Enums, os tipos de envolvimento List e Non-Null, os tipos Object, e os tipos abstratos Interface e Union que trabalham junto com eles. Você revisará exemplos de cada tipo e aprenderá como usá-los para construir um esquema GraphQL completo.
Pré-requisitos
Para tirar o máximo proveito deste tutorial, você deve ter:
- Um entendimento dos conceitos fundamentais do GraphQL, que são apresentados em Uma Introdução ao GraphQL.
- A GraphQL environment, an example of which can be found in How to Set Up a GraphQL API Server in Node.js.
Tipos Escalares
Todos os dados em um esquema GraphQL se resolvem, em última análise, em vários tipos escalares, que representam valores primitivos. As respostas do GraphQL podem ser representadas como uma árvore, e os tipos escalares são as folhas nas extremidades da árvore. Pode haver muitos níveis em uma resposta aninhada, mas o último nível sempre se resolverá para um tipo escalar (ou Enum). O GraphQL vem com cinco tipos escalares integrados: Int
, Float
, String
, Boolean
e ID
.
Int
Inteiro
é um valor numérico de 32 bits com sinal e não fracionário. É um inteiro com sinal (positivo ou negativo) que não inclui decimais. O valor máximo de um inteiro de 32 bits com sinal é 2.147.483.647
. Este é um dos dois escalares embutidos usados para dados numéricos.
Flutuante
A Float
is a signed double-precision fractional value. It is a signed (positive or negative) number that contains a decimal point, such as 1.2
. This is the other built-in scalar used for numerical data.
String
A String
is a UTF-8 character sequence. The String
type is used for any textual data. This can also include data like very large numbers. Most custom scalars will be types of string data.
Booleano
A Boolean
is a true
or false
value.
ID
Um ID
é um identificador único. Este valor é sempre serializado como uma string, mesmo se o ID
for numérico. Um tipo ID
pode ser comumente representado com um Identificador Único Universal (UUID).
Escalares Personalizados
Além desses escalares incorporados, a palavra-chave scalar
pode ser usada para definir um escalar personalizado. Você pode usar escalares personalizados para criar tipos que tenham validação adicional no nível do servidor, como Data
, Hora
ou URL
. Aqui está um exemplo definindo um novo tipo Data
:
scalar Date
O servidor saberá como lidar com interações com esse novo tipo usando o GraphQLScalarType
.
Tipo Enumerado
O tipo Enum, também conhecido como tipo Enumerador, descreve um conjunto de valores possíveis.
Usando o tema da API do Jogo de Fantasia de outros tutoriais na série Como Gerenciar Dados com GraphQL, você pode criar um enum
para as Funções
e Espécies
dos personagens do jogo com todos os valores que o sistema aceitará para eles. Um Enum é definido com a palavra-chave enum
, como mostrado abaixo:
"The job class of the character."
enum Job {
FIGHTER
WIZARD
}
"The species or ancestry of the character."
enum Species {
HUMAN
ELF
DWARF
}
Desta forma, é garantido que o Job
de um personagem é FIGHTER
ou WIZARD
e nunca pode ser acidentalmente "purple"
ou alguma outra string aleatória, o que poderia ser possível se você usasse um tipo String
em vez de criar um Enum personalizado. Enums são escritos em letras maiúsculas por convenção.
Enums também podem ser usados como os valores aceitos em argumentos. Por exemplo, você pode criar um enum
Hand
para denotar se uma arma é de uma mão (como uma espada curta) ou de duas mãos (como um machado pesado), e usar isso para determinar se uma ou duas podem ser equipadas:
enum Hand {
SINGLE
DOUBLE
}
"A valiant weapon wielded by a fighter."
type Weapon {
name: String!
attack: Int
range: Int
hand: Hand
}
type Query {
weapons(hand: Hand = SINGLE): [Weapon]
}
O enum
Hand
foi declarado com SINGLE
e DOUBLE
como valores, e o argumento no campo weapons
tem um valor padrão de SINGLE
, o que significa que se nenhum argumento for passado, então ele voltará para SINGLE
.
Tipo Não-Nulo
Você pode notar que null
ou undefined
, um tipo comum que muitas linguagens consideram um primitivo, está ausente da lista de escalares embutidos. Null existe no GraphQL e representa a falta de um valor.
Todos os tipos no GraphQL são nulos por padrão e, portanto, null
é uma resposta válida para qualquer tipo. Para tornar um valor obrigatório, ele deve ser convertido para um tipo GraphQL Non-Null com um ponto de exclamação no final. Non-Null é definido como um modificador de tipo, que são tipos usados para modificar o tipo ao qual se refere. Como exemplo, String
é uma string opcional (ou nula), e String!
é uma string obrigatória (ou Non-Null).
Tipo Lista
A List type in GraphQL is another type modifier. Any type that is wrapped in square brackets ([]
) becomes a List type, which is a collection that defines the type of each item in a list.
Como exemplo, um tipo definido como [Int]
será uma coleção de tipos Int
, e [String]
será uma coleção de tipos String
. Non-Null e Lista podem ser usados juntos para tornar um tipo tanto obrigatório quanto definido como uma Lista, como [String]!
.
Tipo Objeto
Se os tipos escalares do GraphQL descrevem as “folhas” no final da resposta hierárquica do GraphQL, então os tipos Objeto descrevem os “ramos” intermediários, e quase tudo em um esquema GraphQL é um tipo de Objeto.
Os objetos consistem em uma lista de campos nomeados (chaves) e o tipo de valor ao qual cada campo será resolvido. Os objetos são definidos com a palavra-chave type
. Pelo menos um ou mais campos devem ser definidos, e os campos não podem começar com dois sublinhados (__
) para evitar conflito com o sistema de introspecção do GraphQL.
No exemplo da API de Jogo de Fantasia do GraphQL, você poderia criar um Objeto Fighter
para representar um tipo de personagem em um jogo:
"A hero with direct combat ability and strength."
type Fighter {
id: ID!
name: String!
level: Int
active: Boolean!
}
Neste exemplo, o tipo de Objeto Fighter
foi declarado, e ele tem quatro campos nomeados:
id
produz um tipoID
não nulo.name
produz um tipoString
não nulo.level
produz um tipoInt
.active
produz um tipoBoolean
não nulo.
Acima da declaração, você também pode adicionar um comentário usando aspas duplas, como neste exemplo: "Um herói com habilidade de combate direto e força."
. Isso aparecerá como a descrição para o tipo.
Neste exemplo, cada campo resolve para um tipo escalar, mas os campos de Objeto também podem resolver para outros tipos de Objeto. Por exemplo, você poderia criar um tipo Weapon
, e o esquema do GraphQL pode ser configurado onde o campo weapon
no Fighter
resolverá para um Objeto Weapon
:
"A valiant weapon wielded by a fighter."
type Weapon {
name: String!
attack: Int
range: Int
}
"A hero with direct combat ability and strength."
type Fighter {
id: ID!
name: String!
level: Int
active: Boolean!
weapon: Weapon
}
Os objetos também podem ser aninhados nos campos de outros objetos.
Tipos de Operações Raiz
Há três Objetos especiais que servem como pontos de entrada em um esquema GraphQL: Query, Mutation e Subscription. Esses são conhecidos como Tipos de Operações Raiz e seguem todas as mesmas regras que qualquer outro tipo de Objeto.
A palavra-chave schema
representa o ponto de entrada em um esquema GraphQL. Seus tipos Query, Mutation e Subscription raiz estarão no Objeto raiz schema
:
schema {
query: Query
mutation: Mutation
subscription: Subscription
}
O tipo Query é obrigatório em qualquer esquema GraphQL e representa uma solicitação de leitura, semelhante a um GET
de uma API REST. O seguinte é um exemplo de um Objeto raiz Query
que retorna uma lista de tipos Fighter
:
type Query {
fighters: [Fighter]
}
As Mutations representam uma solicitação de escrita, que seria análoga a um POST
, PUT
ou DELETE
em uma API REST. No exemplo a seguir, a Mutation
tem um campo addFighter
com um argumento nomeado (input
):
type Mutation {
addFighter(input: FighterInput): Fighter
}
Finalmente, uma Subscription corresponde a um fluxo de eventos, que seria usado em conjunto com um Websocket em um aplicativo web. Na API GraphQL Fantasy, talvez possa ser usado para encontros de batalha aleatórios, como segue:
type Subscription {
randomBattle(enemy: Enemy): BattleResult
}
Observe que o ponto de entrada schema
é frequentemente abstraído em algumas implementações GraphQL.
Argumentos de Campo
Os campos de um Objeto GraphQL são essencialmente funções que retornam um valor e podem aceitar argumentos como qualquer função. Os argumentos de campo são definidos pelo nome do argumento seguido pelo tipo. Os argumentos podem ser de qualquer tipo que não seja um Objeto. Neste exemplo, o Objeto Fighter
pode ser filtrado pelo campo id
(que resolve para um tipo ID
Não-Nulo):
type Query {
fighter(id: ID!): Fighter
}
Este exemplo específico é útil para buscar um único item no armazenamento de dados, mas os argumentos também podem ser usados para filtragem, paginação e outras consultas mais específicas.
Tipo de Interface
Assim como o tipo Objeto, o tipo de Interface abstrato consiste em uma lista de campos nomeados e seus tipos de valor associados. As interfaces se parecem com e seguem todas as mesmas regras que os Objetos, mas são usadas para definir um subconjunto da implementação de um Objeto.
Até agora no seu esquema, você possui um Objeto Fighter
, mas você também pode querer criar um Wizard
, um Healer
e outros Objetos que compartilharão muitos dos mesmos campos, mas terão algumas diferenças. Nesse caso, você pode usar uma Interface para definir os campos que todos eles têm em comum e criar Objetos que são implementações da Interface.
No exemplo a seguir, você poderia criar uma Interface BaseCharacter
usando a palavra-chave interface
com todos os campos que todo tipo de personagem possuirá:
"A hero on a quest."
interface BaseCharacter {
id: ID!
name: String!
level: Int!
species: Species
job: Job
}
Cada tipo de personagem terá os campos id
, name
, level
, species
e job
.
Agora, imagine que você tenha um tipo Fighter
e um tipo Wizard
que possuem esses campos compartilhados, mas Fighters
usam uma Weapon
e Wizards
usam Spells
. Você pode usar a palavra-chave implements
para delinear cada um como uma implementação de BaseCharacter
, o que significa que eles devem ter todos os campos da Interface criada:
"A hero with direct combat ability and strength."
type Fighter implements BaseCharacter {
id: ID!
name: String!
level: Int!
species: Species
job: Job!
weapon: Weapon
}
"A hero with a variety of magical powers."
type Wizard implements BaseCharacter {
id: ID!
name: String!
level: Int!
species: Species
job: Job!
spells: [Spell]
}
Fighter
e Wizard
são ambas implementações válidas da Interface BaseCharacter
porque possuem o subconjunto necessário de campos.
Tipo União
Outro tipo abstrato que pode ser usado com Objetos é o tipo Union. Usando a palavra-chave union
, você pode definir um tipo com uma lista de Objetos que são todos válidos como respostas.
Usando as Interfaces criadas na seção anterior, você pode criar uma União Character
que define um personagem como um Wizard
OU um Fighter
:
union Character = Wizard | Fighter
O caractere igual (=
) define a definição, e o caractere de barra vertical (|
) funciona como a declaração OU
. Note que uma União deve consistir em Objetos ou Interfaces. Tipos escalares não são válidos em uma União.
Agora, se você consultar uma lista de personagens, ela pode usar a União Character
e retornar todos os tipos Wizard
e Fighter
.
Conclusão
Neste tutorial, você aprendeu sobre muitos dos tipos que definem o sistema de tipos do GraphQL. Os tipos mais fundamentais são os tipos escalares, que são os valores que atuam como as folhas na árvore de esquema, e consistem em `Int`, `Float`, `String`, `Boolean`, `ID` e qualquer tipo escalar personalizado que uma implementação do GraphQL decida criar. Enums são listas de valores constantes válidos que podem ser usados quando você precisa de mais controle sobre uma resposta do que simplesmente declará-la como uma `String`, e também são folhas na árvore de esquema. Os tipos List e Non-Null são conhecidos como modificadores de tipo, ou tipos de envolvimento, e podem definir outros tipos como coleções ou obrigatórios, respectivamente. Os Objects são os ramos da árvore de esquema, e quase tudo em um esquema GraphQL é um tipo de Object, incluindo os pontos de entrada `query`, `mutation` e `subscription`. Os tipos Interface e Union são tipos abstratos que podem ser úteis na definição de Objects.
Para aprender mais, você pode praticar criando e modificando um esquema GraphQL lendo o tutorial Como Configurar um Servidor de API GraphQL em Node.js para ter um ambiente de servidor GraphQL funcional.
Source:
https://www.digitalocean.com/community/conceptual-articles/understanding-the-graphql-type-system