Firebase SQL Connect ti consente di creare connettori per le istanze PostgreSQL gestite con Google Cloud SQL. Questi connettori sono combinazioni di query e mutazioni per l'utilizzo dei dati dallo schema.
La guida introduttiva ha presentato uno schema di app per le recensioni di film per PostgreSQL.
Questa guida ha anche introdotto operazioni amministrative sia implementabili sia 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. SQL Connect integra l'autenticazione e l'autorizzazione in queste mutazioni e genera SDK client basati sulla tua API.
- Le mutazioni amministrative ad hoc vengono eseguite da ambienti con privilegi per popolare e gestire le tabelle. Puoi crearle ed eseguirle nella Firebase console, da ambienti con privilegi utilizzando la Firebase Admin SDK, e in ambienti di sviluppo locali utilizzando la nostra estensione SQL Connect VS Code.
Questa guida esamina più da vicino le mutazioni implementabili.
Funzionalità delle SQL Connect mutazioni
SQL Connect ti consente di eseguire mutazioni di base in tutti i modi che ti aspetteresti da un database PostgreSQL:
- Eseguire operazioni CRUD
- Gestire operazioni multi-step con le transazioni
Tuttavia, con le estensioni di SQL 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 popolare i dati con le operazioni fornite dal server
- Esegui query nel corso di operazioni di mutazione multi-step per cercare i dati, risparmiando righe di codice e round trip al server.
Utilizzare i campi generati per implementare le mutazioni
Le operazioni SQL Connect estenderanno un insieme di campi generati automaticamente da SQL 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 più complessi di più tabelle.Supponiamo che lo schema contenga un tipo Movie e un tipo Actor associato.
SQL Connect genera i campi movie_insert,
movie_update, movie_delete e altri ancora.
Mutazione con il
movie_insert campo
|
Il campo |
Utilizza questo campo per creare un singolo film. mutation CreateMovie($data: Movie_Data!) { movie_insert(data: $data) { key } } |
Mutazione con il
movie_update campo
|
Il campo |
Utilizza questo campo per aggiornare un singolo film in base alla sua chiave. mutation UpdateMovie($myKey: Movie_Key!, $data: Movie_Data!) { movie_update(key: $myKey, data: $data) { key } } |
Mutazione con il
movie_delete campo
|
Il campo |
Utilizza questo campo per eliminare un singolo film in base alla sua chiave. mutation DeleteMovie($myKey: Movie_Key!) { movie_delete(key: $myKey) { key } } |
Elementi essenziali di una mutazione
Le mutazioni SQL Connect sono mutazioni GraphQL con SQL Connect estensioni. Come per una normale mutazione GraphQL, puoi definire un nome di operazione e un elenco di variabili GraphQL.
SQL Connect estende le query GraphQL con istruzioni personalizzate come
@auth e @transaction.
Pertanto, la seguente mutazione ha:
- Una definizione del tipo
mutation - Un nome di operazione (mutazione)
SignUp - Un singolo argomento di operazione
$username - Una singola istruzione,
@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 database.
Creare
Eseguiamo le 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
})
}
Oppure 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 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
})
}
Utilizzare le operazioni di incremento, decremento, aggiunta e anteposizione con _update
Nelle mutazioni _update e _updateMany puoi impostare in modo esplicito i valori in
data:, ma spesso è più logico 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
}
})
}
SQL Connect supporta i seguenti operatori per gli aggiornamenti dei campi:
incper incrementare i tipi di datiInt,Int64,Float,DateeTimestampdecper decrementare i tipi di datiInt,Int64,Float,DateeTimestamp
Per gli elenchi, puoi anche eseguire l'aggiornamento con singoli valori o elenchi di valori utilizzando:
addper aggiungere elementi se non sono già presenti nei tipi di elenco, ad eccezione degli elenchi di vettoriremoveper rimuovere tutti gli elementi, se presenti, dai tipi di elenco, ad eccezione degli elenchi di vettoriappendper aggiungere elementi ai tipi di elenco, ad eccezione degli elenchi di vettoriprependper anteporre elementi ai tipi di elenco, ad eccezione degli elenchi di vettori
Eseguire eliminazioni
Naturalmente, puoi eliminare i dati dei film. I conservatori di film vorranno sicuramente che i film fisici vengano mantenuti 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 _upsert implicita su 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
SQL Connect fornisce due funzionalità importanti che ti consentono di scrivere mutazioni più efficienti e di risparmiare operazioni di round trip.
Gli scalari chiave sono identificatori di oggetti concisi che SQL Connect assembla automaticamente dai campi chiave negli schemi. Gli scalari chiave sono incentrati sull'efficienza e ti consentono di trovare in una singola chiamata informazioni sull'identità e sulla struttura dei dati. Sono particolarmente utili quando vuoi eseguire azioni sequenziali su 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 popolare dinamicamente i
campi nelle tabelle utilizzando valori archiviati o facilmente calcolabili in base a
espressioni CEL lato server specifiche nell'argesso 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 SQL Connect di fornire valori utilizzando la sintassi field_expr
Come discusso in Scalari chiave e valori del server,
puoi progettare lo schema in modo che il server popoli i valori per i campi comuni
come ids e date in risposta alle richieste del client.
Inoltre, puoi utilizzare i dati, come gli ID utente, inviati in
SQL Connect request oggetti dalle app client.
Quando implementi le mutazioni, utilizza la sintassi field_expr per attivare gli aggiornamenti generati dal server o accedere ai dati dalle richieste. Ad esempio, per passare
l'autorizzazione uid memorizzata in una richiesta a un'operazione _upsert, passa
"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 un'app di elenco di attività da svolgere, 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 nel
riferimento agli scalari.
Scrivere mutazioni avanzate: operazioni multi-step
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.
SQL Connect ti consente di eseguire la logica multi-step nelle mutazioni supportando:
Più campi di scrittura
Più campi di lettura nelle mutazioni (utilizzando la parola chiave del campo
query).L'istruzione
@transactiondirettiva, che fornisce il supporto per le transazioni familiare dai database relazionali.L'istruzione
@checkdirettiva, 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 query
- Utilizza i messaggi restituiti per eseguire la logica appropriata nel codice client
L'istruzione
@redactdirettiva, che ti consente di omettere i risultati del campo query dai risultati del protocollo di rete.Il binding
responseCEL, che memorizza i risultati accumulati di tutte le mutazioni e le query eseguite in un'operazione complessa multi-step. Puoi accedere al bindingresponse:- Nelle istruzioni
@check, tramite l'argomentoexpr: - Con i valori del server, utilizzando la sintassi
field_expr
- Nelle istruzioni
L'istruzione @transaction
Il supporto per le mutazioni multi-step include la gestione degli errori tramite le transazioni.
L'istruzione @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
@transactioneseguono ogni campo radice uno dopo l'altro in sequenza. L'operazione mostra eventuali errori come errori di campo parziali, ma non gli impatti delle esecuzioni successive.Le mutazioni con
@transactionhanno la garanzia di riuscire completamente o di non riuscire completamente. Se uno dei campi all'interno della transazione non riesce, viene eseguito il rollback dell'intera transazione.
Le istruzioni @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 è controllare e rifiutare i nodi il cui valore è null o [] (elenchi vuoti).
L'istruzione @redact redige una parte della risposta dal client. I campi redatti vengono comunque valutati per gli effetti collaterali (incluse le modifiche dei dati e @check) e i risultati sono comunque disponibili per i passaggi successivi nelle espressioni CEL.
Utilizzare @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.
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 istruzioni @check e @redact nei controlli di autorizzazione,
consulta la discussione sulla ricerca dei dati di autorizzazione.
Utilizzare @check per convalidare le chiavi
Alcuni campi di mutazione, come _update, potrebbero non eseguire alcuna operazione se non esiste un record con una chiave specificata. Allo stesso modo, le ricerche potrebbero restituire null o un elenco vuoto. Questi non sono considerati errori e pertanto non attiveranno i rollback.
Per evitare questo risultato, verifica se è possibile trovare le chiavi 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")
}
Utilizzare il binding response per concatenare le mutazioni multi-step
L'approccio di base per la creazione di record correlati, ad esempio una nuova Movie e
una voce MovieMetadata associata, è:
- Chiama una mutazione
_insertperMovie - Memorizza la chiave restituita del film creato
- Quindi, chiama una seconda mutazione
_insertper creare il recordMovieMetadata.
Tuttavia, con SQL Connect, puoi gestire questo caso comune in una singola operazione multi-step accedendo ai risultati del primo _insert nel secondo _insert.
Creare un'app per le recensioni di film di successo è un lavoro impegnativo. Monitoriamo il nostro elenco di attività da svolgere con un nuovo esempio.
Utilizzare response per impostare i campi con i valori del server
Nella seguente mutazione dell'elenco di attività da svolgere:
- Il binding
responserappresenta 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_insertiniziale, che restituisce ilid(chiave), vengono accessibili in un secondo momento inresponse.todoList_insert.id, in modo da poter inserire immediatamente una nuova attività da svolgere.
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,
})
}
Utilizzare 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é un @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 ulteriori informazioni sul binding response, consulta il
riferimento CEL.
Comprendere le operazioni interrotte con @transaction e query @check
Le mutazioni multi-step possono riscontrare errori:
- Le operazioni del database potrebbero non riuscire.
- La logica `query
@check` potrebbe terminare le operazioni.
SQL Connect consiglia di utilizzare l'istruzione @transaction con
le mutazioni multi-step. In questo modo, si ottengono risultati di database e mutazioni più coerenti e più facili da gestire nel codice client:
- Al primo errore o
@checknon riuscito, l'operazione terminerà, quindi non è necessario gestire l'esecuzione di eventuali campi successivi o la valutazione di CEL. - I rollback vengono eseguiti in risposta agli errori del database o alla logica
@check, producendo uno stato del database coerente. - Al codice client viene sempre restituito un errore di rollback.
Potrebbero esistere 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 riesce a causa delle operazioni del database, i campi successivi continueranno a essere eseguiti. Tuttavia, i
@checknon riusciti terminano comunque l'intera operazione. - Non vengono eseguiti rollback, il che significa uno stato del database misto con alcuni aggiornamenti riusciti e alcuni non riusciti.
- Le operazioni con
@checkpotrebbero fornire risultati più incoerenti se la logica@checkutilizza i risultati di letture e/o scritture in un passaggio precedente. - Il risultato restituito al codice client conterrà un mix più complesso di risposte di successo e di errore da gestire.
Istruzioni per le mutazioni SQL Connect
Oltre alle istruzioni utilizzate per definire tipi e tabelle,
SQL Connect fornisce le @auth, @check, @redact e
@transaction istruzioni per aumentare il comportamento delle operazioni.
| Istruzione | Applicabile a | Descrizione |
|---|---|---|
@auth |
Query e mutazioni | Definisce la policy di autorizzazione per una query o una mutazione. Consulta la guida all'autorizzazione e all'attestazione. |
@check |
Campi query nelle operazioni multi-step |
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). Consulta Operazioni multi-step. |
@redact |
Query | Redige una parte della risposta dal client. Consulta Operazioni multi-step. |
@transaction |
Mutazioni | Impone che una mutazione venga sempre eseguita in una transazione di database. Consulta Operazioni multi-step. |
Passaggi successivi
Potrebbe interessarti:
- Generare mutazioni per le app utilizzando gli strumenti di assistenza AI
- Autorizzare le mutazioni in base alla guida all'autorizzazione
- Chiamare le mutazioni dal codice client per web, iOS, Android e Flutter.
- Eseguire operazioni collettive sui dati con le mutazioni