L’autore ha selezionato il Fondo Gratuito e Open Source per ricevere una donazione come parte del programma Scrivi per le Donazioni.
Introduzione
GraphQL è una soluzione moderna per facilitare la comunicazione tra un frontend e una fonte di dati. Tutti i dettagli e le capacità di un’implementazione GraphQL sono descritti nello Schema GraphQL. Per scrivere uno schema GraphQL funzionante, è necessario comprendere il Sistema di Tipi GraphQL.
In questo articolo, imparerai i tipi GraphQL: i cinque tipi scalari built-in, Enum, i tipi di wrapping List e Non-Null, i tipi Object, e gli astratti Interface e Union che lavorano insieme a essi. Revisionerai esempi per ciascun tipo e imparerai come usarli per costruire uno schema GraphQL completo.
Prerequisiti
Per ottenere il massimo da questo tutorial, dovresti avere:
- Una comprensione dei concetti fondamentali di GraphQL, che sono descritti in Un’introduzione a GraphQL.
- A GraphQL environment, an example of which can be found in How to Set Up a GraphQL API Server in Node.js.
Tipi Scalari
Tutti i dati in uno schema GraphQL alla fine si risolvono in vari tipi scalars, che rappresentano valori primitivi. Le risposte GraphQL possono essere rappresentate come un albero, e i tipi scalari sono le foglie alla fine dell’albero. Possono esserci molti livelli in una risposta nidificata, ma l’ultimo livello si risolverà sempre in un tipo scalare (o Enum). GraphQL include cinque tipi scalari integrati: Int
, Float
, String
, Boolean
e ID
.
Int
Intero
è un valore numerico con segno su 32 bit non frazionario. È un intero con segno (positivo o negativo) che non include decimali. Il valore massimo di un intero con segno su 32 bit è 2.147.483.647
. Questo è uno dei due scalari incorporati utilizzati per i dati numerici.
Float
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.
Stringa
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
Un ID
è un identificatore univoco. Questo valore viene sempre serializzato come una stringa, anche se l’ID
è numerico. Un tipo ID
potrebbe essere comunemente rappresentato con un Identificatore Universale Unico (UUID).
Scala Personalizzate
In aggiunta a queste scalari incorporate, la parola chiave scalar
può essere utilizzata per definire una scala personalizzata. Puoi utilizzare scalari personalizzati per creare tipi che hanno una validazione aggiuntiva a livello di server, come Date
, Time
o Url
. Ecco un esempio di definizione di un nuovo tipo Date
:
scalar Date
Il server saprà come gestire le interazioni con questo nuovo tipo utilizzando il GraphQLScalarType
.
Tipo Enumerativo
Il tipo Enum, noto anche come tipo Enumeratore, descrive un insieme di valori possibili.
Utilizzando il tema dell’API del gioco di fantasia da altri tutorial nella serie Come Gestire i Dati con GraphQL, potresti creare un enum
per i Lavori
e le Specie
dei personaggi del gioco con tutti i valori che il sistema accetterà per essi. Un Enum viene definito con la parola chiave enum
, come segue:
"The job class of the character."
enum Job {
FIGHTER
WIZARD
}
"The species or ancestry of the character."
enum Species {
HUMAN
ELF
DWARF
}
In questo modo, è garantito che il Job
di un personaggio sia FIGHTER
o WIZARD
e non possa mai essere accidentalmente "purple"
o qualche altra stringa casuale, cosa che potrebbe accadere se si utilizzasse un tipo String
invece di creare un Enum personalizzato. Gli Enum sono scritti in maiuscolo per convenzione.
Gli Enum possono anche essere utilizzati come valori accettati negli argomenti. Ad esempio, potresti creare un Hand
enum
per indicare se un’arma è a una mano (come una spada corta) o a due mani (come un’ascia pesante), e utilizzarlo per determinare se può essere equipaggiata una o due armi:
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]
}
L’enum Hand
è stato dichiarato con i valori SINGLE
e DOUBLE
, e l’argomento sul campo weapons
ha un valore predefinito di SINGLE
, il che significa che se non viene passato alcun argomento, allora si utilizzerà il valore SINGLE
.
Tipo Non-Null
Potresti notare che null
o undefined
, un tipo comune che molti linguaggi considerano un primitivo, manca dall’elenco degli scalari incorporati. Null esiste in GraphQL e rappresenta l’assenza di un valore.
Tutti i tipi in GraphQL sono nullabili per impostazione predefinita e quindi `null
` è una risposta valida per qualsiasi tipo. Per rendere un valore obbligatorio, deve essere convertito in un tipo GraphQL Non-Null con un punto esclamativo finale. Non-Null è definito come un modificatore di tipo, che sono tipi utilizzati per modificare il tipo a cui si fa riferimento. Come esempio, `String
` è una stringa facoltativa (o nullabile), e `String!
` è una stringa obbligatoria (o Non-Null).
Tipo Elenco
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.
Come esempio, un tipo definito come `[Int]
` sarà una raccolta di tipi `Int
`, e `[String]
` sarà una raccolta di tipi `String
`. Non-Null e Elenco possono essere utilizzati insieme per rendere un tipo sia obbligatorio che definito come un Elenco, come ad esempio `[String]!
`.
Tipo Oggetto
Se i tipi scalari GraphQL descrivono le “foglie” alla fine della risposta gerarchica GraphQL, allora i tipi Oggetto descrivono gli “intervalli” intermedi e quasi tutto in uno schema GraphQL è un tipo di Oggetto.
Gli oggetti consistono in un elenco di campi denominati (chiavi) e il tipo di valore a cui ogni campo si risolverà. Gli oggetti sono definiti con la parola chiave type
. Devono essere definiti almeno uno o più campi, e i campi non possono iniziare con due trattini bassi (__
) per evitare conflitti con il sistema di introspezione di GraphQL.
Nell’esempio dell’API di gioco di fantasia GraphQL, potresti creare un oggetto Fighter
per rappresentare un tipo di personaggio in un gioco:
"A hero with direct combat ability and strength."
type Fighter {
id: ID!
name: String!
level: Int
active: Boolean!
}
In questo esempio, il tipo di oggetto Fighter
è stato dichiarato e ha quattro campi denominati:
id
restituisce un tipoID
non nullo.name
restituisce un tipoString
non nullo.level
restituisce un tipoInt
.active
restituisce un tipoBoolean
non nullo.
Prima della dichiarazione, è possibile aggiungere anche un commento usando virgolette doppie, come in questo esempio: "Un eroe con capacità di combattimento diretto e forza."
. Questo apparirà come descrizione per il tipo.
In questo esempio, ogni campo si risolve in un tipo scalare, ma i campi degli oggetti possono anche risolversi in altri tipi di oggetti. Ad esempio, potresti creare un tipo Weapon
, e lo schema GraphQL può essere impostato in modo che il campo weapon
su Fighter
si risolva in un oggetto 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
}
Gli oggetti possono anche essere annidati nei campi di altri oggetti.
Tipi di operazioni di base
Ci sono tre oggetti speciali che fungono da punti di ingresso nello schema di un GraphQL: Query, Mutation e Subscription. Questi sono conosciuti come tipi di operazioni di base e seguono tutte le stesse regole di qualsiasi altro tipo di oggetto.
La parola chiave schema
rappresenta il punto di ingresso in uno schema di GraphQL. I tipi di root Query, Mutation e Subscription saranno sull’oggetto root schema
:
schema {
query: Query
mutation: Mutation
subscription: Subscription
}
Il tipo Query è richiesto in qualsiasi schema di GraphQL e rappresenta una richiesta di lettura, simile a un GET
di un’API REST. Di seguito è riportato un esempio di un oggetto root Query
che restituisce una lista di tipi Fighter
:
type Query {
fighters: [Fighter]
}
Le Mutations rappresentano una richiesta di scrittura, che sarebbe analoga a un POST
, PUT
o DELETE
in un’API REST. Nell’esempio seguente, la Mutation ha un campo addFighter
con un argomento nominato (input
):
type Mutation {
addFighter(input: FighterInput): Fighter
}
Infine, una Subscription corrisponde a un flusso di eventi, che verrebbe utilizzato insieme a un Websocket in un’app web. Nell’API GraphQL Fantasy, potrebbe essere utilizzato per incontri casuali di battaglia, ad esempio:
type Subscription {
randomBattle(enemy: Enemy): BattleResult
}
Si noti che il punto di ingresso schema
spesso viene astratto in alcune implementazioni di GraphQL.
Argomenti del Campo
I campi di un oggetto GraphQL sono essenzialmente funzioni che restituiscono un valore e possono accettare argomenti come qualsiasi altra funzione. Gli argomenti del campo sono definiti dal nome dell’argomento seguito dal tipo. Gli argomenti possono essere di qualsiasi tipo non oggetto. In questo esempio, l’oggetto Fighter
può essere filtrato dal campo id
(che restituisce un tipo ID
non nullo):
type Query {
fighter(id: ID!): Fighter
}
Questo esempio specifico è utile per recuperare un singolo elemento dalla memoria dei dati, ma gli argomenti possono anche essere utilizzati per il filtraggio, la paginazione e altre query più specifiche.
Tipo Interfaccia
Come il tipo di oggetto, il tipo di interfaccia astratta consiste in un elenco di campi nominati e dei rispettivi tipi di valore associati. Le interfacce assomigliano e seguono tutte le stesse regole degli oggetti, ma sono utilizzate per definire un sottoinsieme dell’implementazione di un oggetto.
Finora nello schema hai un oggetto Fighter
, ma potresti voler creare anche un Wizard
, un Healer
e altri oggetti che condivideranno molte delle stesse proprietà ma avranno alcune differenze. In questo caso, puoi utilizzare un’Interfaccia per definire le proprietà che tutti hanno in comune e creare oggetti che sono implementazioni dell’Interfaccia.
Nell’esempio seguente, potresti creare un’Interfaccia BaseCharacter
utilizzando la parola chiave interface
con tutti i campi che ogni tipo di personaggio possiederà:
"A hero on a quest."
interface BaseCharacter {
id: ID!
name: String!
level: Int!
species: Species
job: Job
}
Ogni tipo di personaggio avrà i campi id
, name
, level
, species
e job
.
Ora, immagina di avere un tipo Fighter
e un tipo Wizard
che hanno questi campi condivisi, ma i Fighter
usano un Weapon
e i Wizard
usano Spells
. Puoi utilizzare la parola chiave implements
per delineare ciascuno come un’implementazione di BaseCharacter
, il che significa che devono avere tutti i campi dell’Interfaccia creata:
"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
sono entrambe implementazioni valide dell’Interfaccia BaseCharacter
perché hanno il sottoinsieme richiesto di campi.
Tipo Unione
Un altro tipo astratto che può essere utilizzato con gli oggetti è il tipo Union. Utilizzando la parola chiave union
, puoi definire un tipo con una lista di oggetti che sono tutti validi come risposte.
Utilizzando le interfacce create nella sezione precedente, puoi creare un’Unione di Character
che definisce un personaggio come Wizard
O Fighter
:
union Character = Wizard | Fighter
Il carattere uguale (=
) imposta la definizione, e il carattere pipe (|
) funziona come l’operatore OR
. Nota che un’Unione deve essere costituita da oggetti o interfacce. I tipi scalari non sono validi in un’Unione.
Ora, se richiedi una lista di personaggi, potrebbe utilizzare l’Unione di Character
e restituire tutti i tipi Wizard
e Fighter
.
Conclusione
Nel tutorial, hai appreso molti dei tipi che definiscono il sistema di tipi di GraphQL. I tipi più fondamentali sono i tipi scalari, che sono i valori che agiscono come le foglie nell’albero dello schema, e comprendono Int
, Float
, String
, Boolean
, ID
, e qualsiasi tipo scalare personalizzato che un’implementazione GraphQL decida di creare. Gli Enums sono elenchi di valori costanti validi che possono essere utilizzati quando è necessario avere più controllo su una risposta rispetto a dichiararla semplicemente come String
, e sono anch’essi foglie nell’albero dello schema. I tipi List e Non-Null sono noti come modificatori di tipo, o tipi di wrappatura, e possono definire altri tipi come collezioni o obbligatori, rispettivamente. Gli Oggetti sono i rami dell’albero dello schema, e praticamente tutto in uno schema GraphQL è un tipo di Oggetto, inclusi i punti di ingresso query
, mutation
, e subscription
. I tipi di Interfaccia e Unione sono tipi astratti che possono essere utili nella definizione degli Oggetti.
Per approfondire, puoi esercitarti nella creazione e modifica di uno schema GraphQL leggendo il tutorial Come configurare un server API GraphQL in Node.js per avere un ambiente di server GraphQL funzionante.
Source:
https://www.digitalocean.com/community/conceptual-articles/understanding-the-graphql-type-system