Firebase Data Connect ti consente di creare connettori per le tue istanze PostgreSQL gestite con Google Cloud SQL. Questi connettori sono combinazioni di query e mutazioni per utilizzare i dati dello schema.
La Guida introduttiva ha introdotto uno schema di app di recensioni di film per PostgreSQL.
Questa guida ha anche introdotto operazioni amministrative sia implementabili che ad hoc, incluse le mutazioni.
- Le mutazioni implementabili sono quelle che implementi per chiamare dalle app client in un connettore, con gli endpoint API che definisci. Data Connect integra l'autenticazione e l'autorizzazione in queste mutazioni e genera SDK client in base alla tua API.
- Le mutazioni amministrative ad hoc vengono eseguite da ambienti privilegiati per compilare e gestire le tabelle. Puoi crearli ed eseguirli nella console Firebase, da ambienti privilegiati utilizzando Firebase Admin SDK e in ambienti di sviluppo locali utilizzando la nostra estensione VS Code Data Connect.
Questa guida esamina più da vicino le mutazioni implementabili.
Caratteristiche delle mutazioni di Data Connect
Data Connect ti consente di eseguire mutazioni di base in tutti i modi che ti aspetteresti dato un database PostgreSQL:
- Eseguire operazioni CRUD
- Gestire operazioni in più passaggi con le transazioni
Tuttavia, con le estensioni di Data Connect a GraphQL, puoi implementare mutazioni avanzate per app più veloci ed efficienti:
- Utilizza gli scalari chiave restituiti da molte operazioni per semplificare le operazioni ripetute sui record
- Utilizza i valori del server per compilare i dati con le operazioni fornite dal server.
- Esegui query nel corso di operazioni di mutazione in più passaggi per cercare dati, risparmiando righe di codice e round trip al server.
Utilizzare i campi generati per implementare le mutazioni
Le operazioni Data Connect estenderanno un insieme di campi generati automaticamente Data Connect in base ai tipi e alle relazioni tra i tipi nello schema. Questi campi vengono generati dagli strumenti locali ogni volta che modifichi lo schema.
Puoi utilizzare i campi generati per implementare le mutazioni, dalla creazione, all'aggiornamento e all'eliminazione di singoli record in singole tabelle, fino ad aggiornamenti multitabella più complessi.Supponiamo che lo schema contenga un tipo Movie
e un tipo Actor
associato.
Data Connect genera i campi movie_insert
,
movie_update
, movie_delete
e altri ancora.
Mutazione con il campo
movie_insert
Il campo |
Utilizza questo campo per creare un singolo filmato. mutation CreateMovie($data: Movie_Data!) { movie_insert(data: $data) { key } } |
Mutazione con il campo
movie_update
Il campo |
Utilizza questo campo per aggiornare un singolo film in base alla chiave. mutation UpdateMovie($myKey: Movie_Key!, $data: Movie_Data!) { movie_update(key: $myKey, data: $data) { key } } |
Mutazione con il campo
movie_delete
Il campo |
Utilizza questo campo per eliminare un singolo film in base alla chiave. mutation DeleteMovie($myKey: Movie_Key!) { movie_delete(key: $myKey) { key } } |
Elementi essenziali di una mutazione
Le mutazioni Data Connect sono mutazioni GraphQL con estensioni Data Connect. Come per una normale mutazione GraphQL, puoi definire un nome di operazione e un elenco di variabili GraphQL.
Data Connect estende le query GraphQL con direttive personalizzate come
@auth
e @transaction
.
Pertanto, la seguente mutazione ha:
- Definizione del tipo
mutation
- Il nome di un'operazione (mutazione)
SignUp
- Un singolo argomento dell'operazione
$username
- Una singola direttiva,
@auth
- Un singolo campo
user_insert
.
mutation SignUp($username: String!) @auth(level: USER) {
user_insert(data: {
id_expr: "auth.uid"
username: $username
})
}
Ogni argomento di mutazione richiede una dichiarazione di tipo, un tipo integrato come String
o un tipo personalizzato definito dallo schema come Movie
.
Scrivere mutazioni di base
Puoi iniziare a scrivere mutazioni per creare, aggiornare ed eliminare singoli record dal tuo database.
Crea
Eseguiamo creazioni di base.
# Create a movie based on user input
mutation CreateMovie($title: String!, $releaseYear: Int!, $genre: String!, $rating: Int!) {
movie_insert(data: {
title: $title
releaseYear: $releaseYear
genre: $genre
rating: $rating
})
}
# Create a movie with default values
mutation CreateMovie2 {
movie_insert(data: {
title: "Sherlock Holmes"
releaseYear: 2009
genre: "Mystery"
rating: 5
})
}
o un upsert.
# Movie upsert using combination of variables and literals
mutation UpsertMovie($title: String!) {
movie_upsert(data: {
title: $title
releaseYear: 2009
genre: "Mystery"
rating: 5
genre: "Mystery/Thriller"
})
}
Eseguire gli aggiornamenti
Ecco gli aggiornamenti. I produttori e i registi sperano sicuramente che queste valutazioni medie siano in linea con le tendenze.
Il campo movie_update
contiene un argomento id
previsto per identificare un record
e un campo data
che puoi utilizzare per impostare i valori in questo aggiornamento.
mutation UpdateMovie(
$id: UUID!,
$genre: String!,
$rating: Int!,
$description: String!
) {
movie_update(id: $id,
data: {
genre: $genre
rating: $rating
description: $description
})
}
Per eseguire più aggiornamenti, utilizza il campo movie_updateMany
.
# Multiple updates (increase all ratings of a genre)
mutation IncreaseRatingForGenre($genre: String!, $rating: Int!) {
movie_updateMany(
where: { genre: { eq: $genre } },
data:
{
rating: $rating
})
}
Utilizza le operazioni di incremento, decremento, aggiunta e anteposizione con _update
Nelle mutazioni _update
e _updateMany
puoi impostare esplicitamente i valori in
data:
, ma spesso è più sensato applicare un operatore come increment per aggiornare
i valori.
Per modificare l'esempio di aggiornamento precedente, supponiamo che tu voglia incrementare la valutazione
di un determinato film. Puoi utilizzare la sintassi rating_update
con l'operatore inc
.
mutation UpdateMovie(
$id: UUID!,
$ratingIncrement: Int!
) {
movie_update(id: $id, data: {
rating_update: {
inc: $ratingIncrement
}
})
}
Data Connect supporta i seguenti operatori per gli aggiornamenti dei campi:
inc
per incrementare i tipi di datiInt
,Int64
,Float
,Date
eTimestamp
dec
per decrementare i tipi di datiInt
,Int64
,Float
,Date
eTimestamp
Per gli elenchi, puoi anche eseguire l'aggiornamento con singoli valori o elenchi di valori utilizzando:
add
per aggiungere elementi se non sono già presenti ai tipi di elenchi, ad eccezione degli elenchi vettorialiremove
per rimuovere tutti gli elementi, se presenti, dai tipi di elenchi, ad eccezione degli elenchi vettorialiappend
per aggiungere elementi ai tipi di elenchi, ad eccezione degli elenchi di vettoriprepend
per anteporre elementi ai tipi di elenchi, ad eccezione degli elenchi di vettori
Esegui eliminazioni
Naturalmente, puoi eliminare i dati dei film. I conservatori di film vorranno sicuramente che le pellicole fisiche vengano mantenute il più a lungo possibile.
# Delete by key
mutation DeleteMovie($id: UUID!) {
movie_delete(id: $id)
}
Qui puoi utilizzare _deleteMany
.
# Multiple deletes
mutation DeleteUnpopularMovies($minRating: Int!) {
movie_deleteMany(where: { rating: { le: $minRating } })
}
Scrivere mutazioni sulle relazioni
Osserva come utilizzare la mutazione implicita _upsert
in una relazione.
# Create or update a one to one relation
mutation MovieMetadataUpsert($movieId: UUID!, $director: String!) {
movieMetadata_upsert(
data: { movie: { id: $movieId }, director: $director }
)
}
Progettare schemi per mutazioni efficienti
Data Connect fornisce due importanti funzionalità che ti consentono di scrivere mutazioni più efficienti e salvare operazioni di andata e ritorno.
Gli scalari chiave sono identificatori di oggetti concisi che Data Connect vengono assemblati automaticamente dai campi chiave negli schemi. Gli scalari chiave riguardano l'efficienza, in quanto ti consentono di trovare in una singola chiamata informazioni sull'identità e sulla struttura dei tuoi dati. Sono particolarmente utili quando vuoi eseguire azioni sequenziali sui nuovi record e hai bisogno di un identificatore univoco da passare alle operazioni imminenti, nonché quando vuoi accedere alle chiavi relazionali per eseguire operazioni aggiuntive più complesse.
Utilizzando i valori del server, puoi consentire al server di compilare dinamicamente
i campi delle tabelle utilizzando valori archiviati o facilmente calcolabili in base
a particolari espressioni CEL lato server nell'argomento expr
. Ad esempio, puoi definire un campo con un timestamp applicato quando si accede al campo utilizzando l'ora memorizzata in una richiesta di operazione, updatedAt: Timestamp!
@default(expr: "request.time")
.
Scrivere mutazioni avanzate: consentire a Data Connect di fornire valori utilizzando la sintassi field_expr
Come descritto in Scalari chiave e valori del server,
puoi progettare lo schema in modo che il server compili i valori per i campi comuni
come id
e le date in risposta alle richieste del client.
Inoltre, puoi utilizzare i dati, ad esempio gli ID utente, inviati negli oggetti Data Connect request
dalle app client.
Quando implementi le mutazioni, utilizza la sintassi field_expr
per attivare
aggiornamenti generati dal server o accedere ai dati dalle richieste. Ad esempio, per trasmettere
l'autorizzazione uid
memorizzata in una richiesta a un'operazione _upsert
, trasmetti
"auth.uid"
nel campo userId_expr
.
# Add a movie to the user's favorites list
mutation AddFavoritedMovie($movieId: UUID!) @auth(level: USER) {
favorite_movie_upsert(data: { userId_expr: "auth.uid", movieId: $movieId })
}
# Remove a movie from the user's favorites list
mutation DeleteFavoritedMovie($movieId: UUID!) @auth(level: USER) {
favorite_movie_delete(key: { userId_expr: "auth.uid", movieId: $movieId })
}
In alternativa, in una comune app di elenchi di cose da fare, quando crei un nuovo elenco, puoi
passare id_expr
per indicare al server di generare automaticamente un UUID per l'elenco.
mutation CreateTodoListWithFirstItem(
$listName: String!
) @transaction {
# Step 1
todoList_insert(data: {
id_expr: "uuidV4()", # <-- auto-generated. Or a column-level @default on `type TodoList` will also work
name: $listName,
})
}
Per ulteriori informazioni, consulta gli scalari _Expr
nella
documentazione di riferimento sugli scalari.
Scrivere mutazioni avanzate: operazioni in più passaggi
Esistono molte situazioni in cui potresti voler includere più campi di scrittura (come gli inserimenti) in una mutazione. Potresti anche voler leggere il database durante l'esecuzione di una mutazione per cercare e verificare i dati esistenti prima di eseguire, ad esempio, inserimenti o aggiornamenti. Queste opzioni consentono di risparmiare operazioni di andata e ritorno e quindi costi.
Data Connect consente di eseguire una logica in più passaggi nelle mutazioni supportando:
Più campi di scrittura
Più campi di lettura nelle mutazioni (utilizzando la parola chiave del campo
query
).L'istruzione
@transaction
, che fornisce il supporto delle transazioni familiare dei database relazionali.La direttiva
@check
, che ti consente di valutare i contenuti delle letture utilizzando le espressioni CEL e, in base ai risultati di questa valutazione:- Procedi con le creazioni, gli aggiornamenti e le eliminazioni definiti da una mutazione
- Procedi per restituire i risultati di un campo di query
- Utilizza i messaggi restituiti per eseguire la logica appropriata nel codice client
La direttiva
@redact
, che consente di omettere i risultati del campo di query dai risultati del protocollo di trasferimento.Il binding CEL
response
, che archivia i risultati accumulati di tutte le mutazioni e le query eseguite in un'operazione complessa in più passaggi. Puoi accedere all'associazioneresponse
:- In
@check
direttive, tramite l'argomentoexpr:
- Con i valori del server, utilizzando la sintassi
field_expr
- In
L'istruzione @transaction
Il supporto per le mutazioni in più passaggi include la gestione degli errori tramite le transazioni.
La direttiva @transaction
impone che una mutazione, con un singolo
campo di scrittura (ad esempio _insert
o _update
) o con più
campi di scrittura, venga sempre eseguita in una transazione di database.
Le mutazioni senza
@transaction
eseguono ogni campo radice uno dopo l'altro in sequenza. L'operazione mostra gli errori come errori parziali dei campi, ma non gli effetti delle esecuzioni successive.Le mutazioni con
@transaction
hanno la garanzia di riuscire o fallire completamente. Se uno dei campi all'interno della transazione ha esito negativo, l'intera transazione viene annullata.
Le direttive @check
e @redact
L'istruzione @check
verifica che i campi specificati siano presenti nei risultati della query. Per testare i valori dei campi viene utilizzata un'espressione Common Expression Language (CEL). Il comportamento predefinito dell'istruzione è di controllare e rifiutare
i nodi il cui valore è null
o []
(elenchi vuoti).
La direttiva @redact
oscura una parte della risposta del client. I campi
oscurati vengono comunque valutati per gli effetti collaterali (incluse le modifiche ai dati e
@check
) e i risultati sono comunque disponibili per i passaggi successivi nelle espressioni CEL.
Utilizza @check
, @check(message:)
e @redact
Un utilizzo importante di @check
e @redact
è la ricerca di dati correlati per decidere
se determinate operazioni devono essere autorizzate, utilizzando la ricerca nella logica, ma
nascondendola ai client. La query può restituire messaggi utili per la gestione
corretta nel codice client.
A titolo illustrativo, il seguente campo di query verifica se un richiedente dispone di un ruolo "amministratore" appropriato per visualizzare gli utenti che possono modificare un film.
query GetMovieEditors($movieId: UUID!) @auth(level: USER) {
moviePermission(key: { movieId: $movieId, userId_expr: "auth.uid" }) @redact {
role @check(expr: "this == 'admin'", message: "You must be an admin to view all editors of a movie.")
}
moviePermissions(where: { movieId: { eq: $movieId }, role: { eq: "editor" } }) {
user {
id
username
}
}
}
Per saperne di più sulle direttive @check
e @redact
nei controlli di autorizzazione,
consulta la sezione relativa alla ricerca dei dati di autorizzazione.
Utilizzare @check
per convalidare le chiavi
Alcuni campi di mutazione, come _update
, potrebbero non funzionare se non esiste un record con una chiave specificata. Analogamente, le ricerche possono restituire null o un elenco vuoto. Questi
non sono considerati errori e pertanto non attivano rollback.
Per evitare questo risultato, verifica se le chiavi possono essere trovate utilizzando l'istruzione @check
.
# Delete by key, error if not found
mutation MustDeleteMovie($id: UUID!) @transaction {
movie_delete(id: $id) @check(expr: "this != null", message: "Movie not found, therefore nothing is deleted")
}
Utilizza il binding response
per concatenare le mutazioni in più passaggi
L'approccio di base per creare record correlati, ad esempio un nuovo Movie
e
una voce MovieMetadata
associata, consiste nel:
- Chiama una mutazione
_insert
perMovie
- Memorizza la chiave restituita del filmato creato
- Quindi, chiama una seconda mutazione
_insert
per creare il recordMovieMetadata
.
Con Data Connect, invece, puoi gestire questo caso comune in un'unica operazione in più passaggi accedendo ai risultati del primo _insert
nel secondo _insert
.
Creare un'app di recensioni di film di successo richiede molto lavoro. Monitoriamo la nostra lista di cose da fare con un nuovo esempio.
Utilizzare response
per impostare i campi con i valori del server
Nella seguente mutazione dell'elenco delle cose da fare:
- Il binding
response
rappresenta l'oggetto di risposta parziale finora, che include tutti i campi di mutazione di primo livello prima di quello corrente. - I risultati dell'operazione
todoList_insert
iniziale, che restituisce il campoid
(chiave), vengono accessibili in un secondo momento inresponse.todoList_insert.id
per poter inserire immediatamente un nuovo elemento dell'elenco di cose da fare.
mutation CreateTodoListWithFirstItem(
$listName: String!,
$itemContent: String!
) @transaction {
# Sub-step 1:
todoList_insert(data: {
id_expr: "uuidV4()", # <-- auto-generated. Or a column-level @default on `type TodoList` will also work
name: $listName,
})
# Sub-step 2:
todo_insert(data: {
listId_expr: "response.todoList_insert.id" # <-- Grab the newly generated ID from the partial response so far.
content: $itemContent,
})
}
Utilizza response
per convalidare i campi utilizzando @check
response
è disponibile anche in @check(expr: "...")
, quindi puoi utilizzarlo per
creare una logica lato server ancora più complessa. In combinazione con i passaggi query { … }
nelle mutazioni, puoi ottenere molto di più senza ulteriori round trip client-server.
Nell'esempio seguente, tieni presente che @check
ha già accesso a response.query
perché @check
viene sempre eseguito dopo il passaggio a cui è collegato.
mutation CreateTodoInNamedList(
$listName: String!,
$itemContent: String!
) @transaction {
# Sub-step 1: Look up List.id by its name
query
@check(expr: "response.query.todoLists.size() > 0", message: "No such TodoList with the name!")
@check(expr: "response.query.todoLists.size() < 2", message: "Ambiguous listName!") {
todoLists(where: { name: $listName }) {
id
}
}
# Sub-step 2:
todo_insert(data: {
listId_expr: "response.todoLists[0].id" # <-- Now we have the parent list ID to insert to
content: $itemContent,
})
}
Per saperne di più sul binding response
, consulta la
documentazione di riferimento CEL.
Comprendere le operazioni interrotte con @transaction
e query @check
Le modifiche in più passaggi possono riscontrare errori:
- Le operazioni del database potrebbero non riuscire.
- La logica di
@check
della query potrebbe interrompere le operazioni.
Data Connect consiglia di utilizzare la direttiva @transaction
con
le mutazioni in più passaggi. Ciò si traduce in un database più coerente e in risultati di mutazione più facili da gestire nel codice client:
- Al primo errore o
@check
non riuscito, l'operazione verrà interrotta, quindi non è necessario gestire l'esecuzione di campi successivi o la valutazione di CEL. - I rollback vengono eseguiti in risposta a errori del database o alla logica
@check
, per ottenere uno stato coerente del database. - Al codice client viene sempre restituito un errore di rollback.
Potrebbero esserci alcuni casi d'uso in cui scegli di non utilizzare @transaction
: potresti optare per la coerenza finale se, ad esempio, hai bisogno di una velocità effettiva, una scalabilità o una disponibilità maggiori. Tuttavia, devi gestire il database e il codice client per consentire i risultati:
- Se un campo non va a buon fine a causa di operazioni sul database, i campi successivi continueranno
a essere eseguiti. Tuttavia, gli errori
@check
interrompono comunque l'intera operazione. - I rollback non vengono eseguiti, il che significa che il database si trova in uno stato misto con alcuni aggiornamenti riusciti e altri non riusciti.
- Le operazioni con
@check
potrebbero dare risultati più incoerenti se la logica di@check
utilizza i risultati di letture e/o scritture in un passaggio precedente. - Il risultato restituito al codice client conterrà un mix più complesso di risposte positive e negative da gestire.
Direttive per le mutazioni di Data Connect
Oltre alle direttive utilizzate per definire tipi e tabelle, Data Connect fornisce le direttive @auth
, @check
, @redact
e @transaction
per aumentare il comportamento delle operazioni.
Direttiva | Applicabile a | Descrizione |
---|---|---|
@auth |
Query e mutazioni | Definisce il criterio di autorizzazione per una query o una mutazione. Consulta la guida all'autorizzazione e all'attestazione. |
@check |
Campi query nelle operazioni in più passaggi |
Verifica che i campi specificati siano presenti nei risultati della query. Per testare i valori dei campi viene utilizzata un'espressione Common Expression Language (CEL). Vedi Operazioni in più passaggi. |
@redact |
Query | oscura una parte della risposta del cliente. Vedi Operazioni in più passaggi. |
@transaction |
Mutazioni | Impone l'esecuzione di una mutazione sempre in una transazione di database. Vedi Operazioni in più passaggi. |
Passaggi successivi
Potrebbe interessarti:
- Generare mutazioni per le tue app utilizzando gli strumenti di assistenza AI
- Autorizzare le mutazioni in base alla guida all'autorizzazione
- Chiamata di mutazioni dal codice client per web, iOS, Android e Flutter.
- Esecuzione di operazioni sui dati collettive con modifiche