GraphQL subscription: come funzionano sulla rete | We.Inc
Cosa sono le GraphQL subscription, la regola del singolo campo radice, un percorso passo per passo tra i messaggi graphql-ws, l'opzione SSE e quando il polling è la scelta migliore.
Una GraphQL subscription è un'operazione di lunga durata: il client invia una volta un documento subscription, e il server invia un nuovo risultato ogni volta che si verifica l'evento corrispondente. GraphQL non stabilisce il trasporto. In pratica funziona su WebSocket, con il protocollo graphql-ws, oppure su Server-Sent Events. Ogni operazione subscription deve selezionare esattamente un campo radice.
Subscription, polling e refetch
Approccio
Come arrivano gli aggiornamenti
Ideale per
Subscription
Il server invia un risultato per ogni evento
Modifiche piccole e frequenti che gli utenti si aspettano di vedere subito
Polling
Il client ripete una query a intervalli
Dati che cambiano ogni tanto
Refetch su azione
Il client rifà la query dopo un'azione dell'utente
Schermate che cambiano solo quando l'utente agisce
Notifica push
Un messaggio fuori dall'app
Utenti mobile con l'app chiusa
Lo strumento qui sotto riproduce una subscription sul protocollo graphql-ws, messaggio per messaggio. Ogni passo mostra il frame JSON che attraversa il socket e quale lato lo invia, dal primo handshake al complete finale.
Premi Avanti per scorrere la conversazione un frame alla volta.
Leggi chi invia ogni frame e cosa significa.
Osserva l'id dell'operazione: ogni frame di una stessa subscription porta lo stesso id.
Usa Indietro o Ricomincia per rivedere un passo.
Parti dal polling e passa a una subscription solo quando qualche secondo di ritardo peggiora davvero l'esperienza.
Invia un piccolo evento in ogni risultato, come il nuovo messaggio, invece dell'intera lista, e lascia che il client lo unisca.
Prevedi le riconnessioni: dopo la caduta di un socket il client deve sottoscriversi di nuovo e recuperare ciò che ha perso.
Controlla l'autenticazione all'apertura della connessione e di nuovo dentro ogni resolver, perché i diritti di un utente possono cambiare durante una sessione lunga.
Domande frequenti
Perché un solo campo radice per subscription?
Ogni subscription corrisponde a un unico flusso di eventi. graphql.org mostra che mettere due campi radice in una stessa operazione subscription non è valido; scrivi invece due subscription con nome.
Quale subprotocol WebSocket usa graphql-ws?
Negozia graphql-transport-ws durante l'handshake, con tipi di frame come connection_init, subscribe, next e complete. Altre librerie GraphQL per WebSocket possono usare un protocollo diverso, quindi verifica che client e server concordino prima di fare qualsiasi altro debug.
Le subscription possono funzionare senza WebSocket?
Sì. Il protocollo graphql-sse invia ogni risultato come Server-Sent Event chiamato next e termina con complete. Funziona su semplice HTTP, il che aiuta dietro proxy che bloccano gli upgrade WebSocket.
Le subscription scalano come le query?
No. graphql.org fa notare che ogni client sottoscritto è legato a un'istanza del server e richiede uno stato mantenuto per tutta la sua durata, quindi serve anche un sistema pub/sub per distribuire gli eventi tra i server.
Cosa succede quando ti sottoscrivi
Il client invia un documento che inizia con la parola chiave subscription, sceglie un campo radice ed elenca i campi che vuole in ogni evento. Il server lo valida una volta e lo tiene aperto.
Quando qualcosa pubblica un evento corrispondente, come una nuova recensione, il server esegue il selection set su quell'evento e invia il risultato. Ogni risultato ha la normale forma data ed errors, quindi il codice client lo legge come legge una query.
Il protocollo graphql-ws in breve
Dopo l'upgrade WebSocket, il client invia connection_init, spesso con un token di autenticazione nel payload, e attende connection_ack. Solo allora può inviare frame subscribe. Ogni subscribe porta un id univoco, e ogni frame next, error o complete di quell'operazione lo ripete, così molte subscription condividono un solo socket.
Ognuno dei due lati può inviare ping e l'altro risponde pong. Il server chiude il socket con il codice 4408 se connection_init non arriva in tempo, e con 4409 quando un client riusa un id ancora in esecuzione.
Quando il polling è la scelta migliore
Le subscription richiedono connessioni sticky, un livello pub/sub e codice client attento a risottoscrizioni e race condition. Per una dashboard che si aggiorna ogni minuto, una query a intervalli è più semplice e gestisce meglio i guasti.
graphql.org suggerisce le subscription per dati che cambiano spesso e a piccoli passi, e polling, notifiche push o refetch per dati più lenti o client mobile con poca banda.
Schema e codice client
Dichiarare un campo subscription
Subscription è un tipo radice come Query e Mutation. Ogni campo è un flusso che un client può aprire.
Sottoscriversi con il client graphql-ws
createClient apre il socket in modo lazy, e iterate restituisce un async iterator che produce un risultato per evento.
La regola del singolo campo radice
Dividere due flussi in due operazioni
Una singola subscription che elenca reviewCreated e un altro campo radice non supera la validazione. Due operazioni con nome funzionano.
Il client invia connection_init
Inviato subito dopo l'apertura del socket. Il payload è libero; spesso i server ne leggono un token.
Il server invia connection_ack
Il server ha accettato la connessione. Prima di questo, un frame subscribe chiuderebbe il socket con 4401 Unauthorized.
Il client invia subscribe
L'id è scelto dal client e deve essere univoco tra le sue operazioni in corso. Il payload è la solita query con le variabili.
Il server invia next
È stata pubblicata una recensione, quindi il server ha eseguito il selection set e inviato il risultato con lo stesso id.
Il server invia di nuovo next
Un altro evento, un altro frame. Si ripete finché la subscription resta aperta.
Il client invia complete
L'utente ha lasciato la pagina, quindi il client ferma l'operazione. Il socket può restare aperto per altre subscription.