Mit Firebase Data Connect können Sie Connectors für Ihre PostgreSQL-Instanzen erstellen, die mit Google Cloud SQL verwaltet werden. Diese Connectors sind Kombinationen aus Abfragen und Mutationen, mit denen Sie Ihre Daten aus Ihrem Schema verwenden können.
In der Anleitung für die ersten Schritte wurde ein Schema für eine Filmbewertungs-App für PostgreSQL vorgestellt.
In dieser Anleitung wurden auch bereitstellbare und Ad-hoc-Verwaltungsvorgänge, einschließlich Mutationen, eingeführt.
- Bereitstellbare Mutationen sind diejenigen, die Sie implementieren, um sie über Client-Apps in einem Connector mit von Ihnen definierten API-Endpunkten aufzurufen. Data Connect integriert die Authentifizierung und Autorisierung in diese Mutationen und generiert Client-SDKs basierend auf Ihrer API.
- Ad-hoc-Mutations für die Verwaltung werden in privilegierten Umgebungen ausgeführt, um Tabellen zu füllen und zu verwalten. Sie können sie in der Firebase-Konsole, in privilegierten Umgebungen mit dem Firebase Admin SDK und in lokalen Entwicklungsumgebungen mit unserer Data Connect VS Code-Erweiterung erstellen und ausführen.
In diesem Leitfaden werden bereitstellbare Mutationen genauer betrachtet.
Funktionen von Data Connect-Mutationen
Mit Data Connect können Sie grundlegende Mutationen auf alle Arten ausführen, die Sie von einer PostgreSQL-Datenbank erwarten:
- CRUD-Vorgänge ausführen
- Mehrstufige Vorgänge mit Transaktionen verwalten
Mit den Erweiterungen von Data Connect für GraphQL können Sie jedoch erweiterte Mutationen für schnellere und effizientere Apps implementieren:
- Verwenden Sie Schlüssel-Skalare, die von vielen Vorgängen zurückgegeben werden, um wiederholte Vorgänge für Datensätze zu vereinfachen.
- Serverwerte verwenden, um Daten mit vom Server bereitgestellten Vorgängen zu füllen
- Führen Sie Abfragen im Rahmen von mehrstufigen Mutationsvorgängen aus, um Daten abzurufen. So sparen Sie Codezeilen und Roundtrips zum Server.
Generierte Felder zum Implementieren von Mutationen verwenden
Ihre Data Connect-Vorgänge erweitern automatisch eine Reihe von Feldern, die auf Grundlage der Typen und Typbeziehungen in Ihrem Schema automatisch generiert werden.Data Connect Diese Felder werden von lokalen Tools generiert, wenn Sie Ihr Schema bearbeiten.
Sie können generierte Felder verwenden, um Mutationen zu implementieren, von der Erstellung, Aktualisierung und Löschung einzelner Datensätze in einzelnen Tabellen bis hin zu komplexeren Aktualisierungen mehrerer Tabellen.Angenommen, Ihr Schema enthält einen Typ Movie
und einen zugehörigen Typ Actor
.
Data Connect generiert die Felder movie_insert
, movie_update
, movie_delete
und weitere.
Mutation mit dem Feld
movie_insert
Das Feld |
Verwenden Sie dieses Feld, um einen einzelnen Film zu erstellen. mutation CreateMovie($data: Movie_Data!) { movie_insert(data: $data) { key } } |
Mutation mit dem Feld
movie_update
Das Feld |
Mit diesem Feld können Sie einen einzelnen Film anhand seines Schlüssels aktualisieren. mutation UpdateMovie($myKey: Movie_Key!, $data: Movie_Data!) { movie_update(key: $myKey, data: $data) { key } } |
Mutation mit dem Feld
movie_delete
Das Feld |
Mit diesem Feld können Sie einen einzelnen Film anhand seines Schlüssels löschen. mutation DeleteMovie($myKey: Movie_Key!) { movie_delete(key: $myKey) { key } } |
Wesentliche Elemente einer Mutation
Data Connect-Mutationen sind GraphQL-Mutationen mit Data Connect-Erweiterungen. Genau wie bei einer regulären GraphQL-Mutation können Sie einen Vorgangsnamen und eine Liste von GraphQL-Variablen definieren.
Data Connect erweitert GraphQL-Abfragen mit benutzerdefinierten Direktiven wie @auth
und @transaction
.
Die folgende Mutation hat also:
- Eine
mutation
-Typdefinition - Name eines
SignUp
-Vorgangs (Mutation) - Ein einzelnes Argument für den
$username
-Vorgang - Eine einzelne Richtlinie,
@auth
- Ein einzelnes Feld
user_insert
.
mutation SignUp($username: String!) @auth(level: USER) {
user_insert(data: {
id_expr: "auth.uid"
username: $username
})
}
Für jedes Mutationsargument ist eine Typdeklaration erforderlich, entweder ein integrierter Typ wie String
oder ein benutzerdefinierter, schemadefinierter Typ wie Movie
.
Grundlegende Mutationen schreiben
Sie können mit dem Schreiben von Mutationen beginnen, um einzelne Datensätze in Ihrer Datenbank zu erstellen, zu aktualisieren und zu löschen.
Erstellen
Wir beginnen mit einfachen Kreationen.
# 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
})
}
Oder ein 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"
})
}
Updates durchführen
Hier sind die Updates. Produzenten und Regisseure hoffen natürlich, dass diese durchschnittlichen Bewertungen im Trend liegen.
Das Feld movie_update
enthält ein erwartetes id
-Argument zur Identifizierung eines Datensatzes und ein data
-Feld, mit dem Sie Werte in diesem Update festlegen können.
mutation UpdateMovie(
$id: UUID!,
$genre: String!,
$rating: Int!,
$description: String!
) {
movie_update(id: $id,
data: {
genre: $genre
rating: $rating
description: $description
})
}
Verwenden Sie das Feld movie_updateMany
, um mehrere Aktualisierungen vorzunehmen.
# Multiple updates (increase all ratings of a genre)
mutation IncreaseRatingForGenre($genre: String!, $rating: Int!) {
movie_updateMany(
where: { genre: { eq: $genre } },
data:
{
rating: $rating
})
}
Inkrement-, Dekrement-, Append- und Prepend-Vorgänge mit _update
verwenden
In _update
- und _updateMany
-Mutationen können Sie zwar explizit Werte in data:
festlegen, es ist jedoch oft sinnvoller, einen Operator wie „increment“ (erhöhen) zu verwenden, um Werte zu aktualisieren.
Angenommen, Sie möchten das Beispiel für die Aktualisierung so ändern, dass die Bewertung eines bestimmten Films erhöht wird. Sie können die rating_update
-Syntax mit dem inc
-Operator verwenden.
mutation UpdateMovie(
$id: UUID!,
$ratingIncrement: Int!
) {
movie_update(id: $id, data: {
rating_update: {
inc: $ratingIncrement
}
})
}
Data Connect unterstützt die folgenden Operatoren für Feldaktualisierungen:
inc
zum Erhöhen der DatentypenInt
,Int64
,Float
,Date
undTimestamp
dec
zum Verringern der DatentypenInt
,Int64
,Float
,Date
undTimestamp
Bei Listen können Sie auch einzelne Werte oder Listen von Werten aktualisieren:
add
, um Elemente anzuhängen, wenn sie noch nicht in Listentypen vorhanden sind, mit Ausnahme von Vektorlistenremove
– damit werden alle Elemente, sofern vorhanden, aus Listentypen mit Ausnahme von Vektorlisten entfernt.append
zum Anhängen von Elementen an Listentypen, außer Vektorlistenprepend
zum Voranstellen von Elementen an Listentypen, außer Vektorlisten
Löschvorgänge ausführen
Sie können Filmdaten natürlich löschen. Filmkonservatoren möchten sicherlich, dass die physischen Filme so lange wie möglich erhalten bleiben.
# Delete by key
mutation DeleteMovie($id: UUID!) {
movie_delete(id: $id)
}
Hier können Sie _deleteMany
verwenden.
# Multiple deletes
mutation DeleteUnpopularMovies($minRating: Int!) {
movie_deleteMany(where: { rating: { le: $minRating } })
}
Mutationen für Beziehungen schreiben
Sehen Sie sich an, wie die implizite Mutation _upsert
für eine Beziehung verwendet wird.
# Create or update a one to one relation
mutation MovieMetadataUpsert($movieId: UUID!, $director: String!) {
movieMetadata_upsert(
data: { movie: { id: $movieId }, director: $director }
)
}
Schemas für effiziente Mutationen entwerfen
Data Connect bietet zwei wichtige Funktionen, mit denen Sie effizientere Mutationen schreiben und Roundtrip-Vorgänge sparen können.
Schlüssel-Skalare sind prägnante Objekt-IDs, die Data Connect automatisch aus Schlüsselfeldern in Ihren Schemas zusammenstellt. Bei wichtigen Skalaren geht es um Effizienz. Sie können mit einem einzigen Aufruf Informationen zur Identität und Struktur Ihrer Daten abrufen. Sie sind besonders nützlich, wenn Sie sequenzielle Aktionen für neue Datensätze ausführen möchten und eine eindeutige Kennung für nachfolgende Vorgänge benötigen. Außerdem können Sie damit auf relationale Schlüssel zugreifen, um zusätzliche komplexere Vorgänge auszuführen.
Mit Serverwerten können Sie Felder in Ihren Tabellen dynamisch mit gespeicherten oder leicht berechenbaren Werten gemäß bestimmten serverseitigen CEL-Ausdrücken im expr
-Argument füllen lassen. Sie können beispielsweise ein Feld mit einem Zeitstempel definieren, der angewendet wird, wenn auf das Feld mit der in einer Vorgangsanfrage gespeicherten Zeit updatedAt: Timestamp!
@default(expr: "request.time")
zugegriffen wird.
Erweiterte Mutationen schreiben: Data Connect kann Werte mit der field_expr
-Syntax bereitstellen
Wie unter Wichtige Skalare und Serverwerte beschrieben, können Sie Ihr Schema so gestalten, dass der Server Werte für gängige Felder wie id
und Datumsangaben als Reaktion auf Clientanfragen einfügt.
Außerdem können Sie Daten wie Nutzer-IDs verwenden, die in Data Connect-request
-Objekten von Client-Apps gesendet werden.
Wenn Sie Mutationen implementieren, verwenden Sie die field_expr
-Syntax, um serverseitig generierte Aktualisierungen auszulösen oder auf Daten aus Anfragen zuzugreifen. Wenn Sie beispielsweise die in einer Anfrage gespeicherte Autorisierung uid
an einen _upsert
-Vorgang übergeben möchten, übergeben Sie "auth.uid"
im Feld 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 })
}
Oder Sie übergeben id_expr
, wenn Sie in einer bekannten To-do-Listen-App eine neue To-do-Liste erstellen, um den Server anzuweisen, automatisch eine UUID für die Liste zu generieren.
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,
})
}
Weitere Informationen finden Sie unter _Expr
-Skalare in der Skalarreferenz.
Erweiterte Mutationen schreiben: Vorgänge mit mehreren Schritten
Es gibt viele Situationen, in denen Sie mehrere Schreibfelder (z. B. Einfügungen) in eine Mutation aufnehmen möchten. Möglicherweise möchten Sie auch während der Ausführung einer Mutation auf Ihre Datenbank zugreifen, um vorhandene Daten zu suchen und zu überprüfen, bevor Sie beispielsweise Einfügungen oder Aktualisierungen vornehmen. Mit diesen Optionen werden Roundtrip-Vorgänge und damit Kosten eingespart.
Mit Data Connect können Sie mehrstufige Logik in Ihren Mutationen ausführen, da Folgendes unterstützt wird:
Mehrere Schreibfelder
Mehrere Lesefelder in Ihren Mutationen (mit dem Feld-Keyword
query
).Die
@transaction
-Anweisung bietet die aus relationalen Datenbanken bekannte Transaktionsunterstützung.Die
@check
-Anweisung, mit der Sie den Inhalt von Lesevorgängen mithilfe von CEL-Ausdrücken auswerten können, und basierend auf den Ergebnissen dieser Auswertung:- Erstellen, Aktualisieren und Löschen gemäß einer Mutation
- Ergebnisse eines Abfragefelds zurückgeben
- Zurückgegebene Nachrichten verwenden, um die entsprechende Logik im Clientcode auszuführen
Die
@redact
-Anweisung, mit der Sie Abfragefeldergebnisse aus den Ergebnissen des Wire-Protokolls weglassen können.Die CEL-Bindung
response
, in der die kumulierten Ergebnisse aller Mutationen und Abfragen gespeichert werden, die in einem komplexen, mehrstufigen Vorgang ausgeführt werden. Sie können auf dieresponse
-Bindung zugreifen:- In
@check
-Anweisungen über das Argumentexpr:
- Mit Serverwerten, mit
field_expr
-Syntax
- In
Die @transaction
-Anweisung
Die Unterstützung von Mutationen mit mehreren Schritten umfasst die Fehlerbehandlung mithilfe von Transaktionen.
Die @transaction
-Anweisung erzwingt, dass eine Mutation – entweder mit einem einzelnen Schreibfeld (z. B. _insert
oder _update
) oder mit mehreren Schreibfeldern – immer in einer Datenbanktransaktion ausgeführt wird.
Bei Mutationen ohne
@transaction
wird jedes Stammfeld nacheinander ausgeführt. Bei dem Vorgang werden alle Fehler als Fehler in Teilfeldern angezeigt, aber nicht die Auswirkungen der nachfolgenden Ausführungen.Mutationen mit
@transaction
sind entweder vollständig erfolgreich oder vollständig fehlgeschlagen. Wenn eines der Felder in der Transaktion fehlschlägt, wird die gesamte Transaktion rückgängig gemacht.
Die Anweisungen @check
und @redact
Mit der Anweisung @check
wird geprüft, ob die angegebenen Felder in den Abfrageergebnissen vorhanden sind. Mit einem CEL-Ausdruck (Common Expression Language) werden Feldwerte getestet. Standardmäßig werden Knoten, deren Wert null
oder []
(leere Listen) ist, geprüft und abgelehnt.
Mit der Direktive @redact
wird ein Teil der Antwort für den Client unkenntlich gemacht. Entfernte Felder werden weiterhin auf Nebeneffekte (einschließlich Datenänderungen und @check
) ausgewertet und die Ergebnisse sind weiterhin für spätere Schritte in CEL-Ausdrücken verfügbar.
@check
, @check(message:)
und @redact
verwenden
@check
und @redact
werden hauptsächlich verwendet, um zu entscheiden, ob bestimmte Vorgänge autorisiert werden sollen. Die Suche erfolgt in der Logik, wird aber vor Clients verborgen. Ihre Anfrage kann nützliche Meldungen für die korrekte Verarbeitung im Clientcode zurückgeben.
Das folgende Abfragefeld prüft, ob ein Anfragender eine geeignete Administratorrolle hat, um Nutzer aufzurufen, die einen Film bearbeiten können.
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
}
}
}
Weitere Informationen zu @check
- und @redact
-Direktiven bei Autorisierungsprüfungen finden Sie im Abschnitt zum Nachschlagen von Autorisierungsdaten.
Schlüssel mit @check
validieren
Einige Mutationsfelder wie _update
haben möglicherweise keine Auswirkungen, wenn kein Datensatz mit einem angegebenen Schlüssel vorhanden ist. Ebenso können bei Lookups „null“ oder eine leere Liste zurückgegeben werden. Diese werden nicht als Fehler betrachtet und lösen daher keine Rollbacks aus.
Um dieses Ergebnis zu vermeiden, testen Sie, ob Schlüssel mit der @check
-Anweisung gefunden werden können.
# 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")
}
response
-Bindung verwenden, um Mutationen in mehreren Schritten zu verketten
Der grundlegende Ansatz zum Erstellen verknüpfter Datensätze, z. B. eines neuen Movie
- und eines zugehörigen MovieMetadata
-Eintrags, ist:
_insert
-Mutation fürMovie
aufrufen- Den zurückgegebenen Schlüssel des erstellten Films speichern
- Rufen Sie dann eine zweite
_insert
-Mutation auf, um denMovieMetadata
-Eintrag zu erstellen.
Mit Data Connect können Sie diesen häufigen Fall jedoch in einem einzigen mehrstufigen Vorgang bearbeiten, indem Sie in der zweiten _insert
auf die Ergebnisse der ersten _insert
zugreifen.
Eine erfolgreiche App für Filmrezensionen zu entwickeln, ist mit viel Arbeit verbunden. Sehen wir uns ein neues Beispiel für die Nachverfolgung unserer Aufgabenliste an.
Mit response
Felder mit Serverwerten festlegen
In der folgenden Mutation für die Aufgabenliste:
- Die
response
-Bindung stellt das bisherige Teilantwortobjekt dar, das alle Mutationsfelder der obersten Ebene vor dem aktuellen Feld enthält. - Die Ergebnisse des ursprünglichen
todoList_insert
-Vorgangs, der das Feldid
(Schlüssel) zurückgibt, werden später inresponse.todoList_insert.id
aufgerufen, damit wir sofort ein neues To-do-Element einfügen können.
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,
})
}
Felder mit response
validieren@check
response
ist auch in @check(expr: "...")
verfügbar. Sie können damit also noch komplexere serverseitige Logik erstellen. In Kombination mit query { … }
-Schritten in Mutationen können Sie viel mehr erreichen, ohne zusätzliche Client-Server-Roundtrips durchzuführen.
Im folgenden Beispiel hat @check
bereits Zugriff auf response.query
, da @check
immer nach dem Schritt ausgeführt wird, an den es angehängt ist.
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,
})
}
Weitere Informationen zur response
-Bindung finden Sie in der CEL-Referenz.
Unterbrochene Vorgänge mit @transaction
und query @check
nachvollziehen
Bei Mutationen mit mehreren Schritten können Fehler auftreten:
- Datenbankvorgänge können fehlschlagen.
- Die Logik der Abfrage
@check
kann Vorgänge beenden.
Data Connect empfiehlt, die @transaction
-Anweisung für Mutationen mit mehreren Schritten zu verwenden. Dies führt zu einer konsistenteren Datenbank und Mutationsergebnissen, die im Clientcode einfacher zu verarbeiten sind:
- Beim ersten Fehler oder fehlgeschlagenen
@check
wird der Vorgang beendet. Sie müssen also die Ausführung nachfolgender Felder oder die Auswertung von CEL nicht verwalten. - Rollbacks werden als Reaktion auf Datenbankfehler oder
@check
-Logik ausgeführt, um einen konsistenten Datenbankstatus zu erzielen. - Ein Rollback-Fehler wird immer an den Clientcode zurückgegeben.
Es gibt möglicherweise Anwendungsfälle, in denen Sie @transaction
nicht verwenden. Sie können sich beispielsweise für Eventual Consistency entscheiden, wenn Sie einen höheren Durchsatz, eine höhere Skalierbarkeit oder Verfügbarkeit benötigen. Sie müssen jedoch Ihre Datenbank und Ihren Clientcode so verwalten, dass die Ergebnisse möglich sind:
- Wenn ein Feld aufgrund von Datenbankvorgängen fehlschlägt, werden die nachfolgenden Felder weiterhin ausgeführt. Fehlgeschlagene
@check
s beenden jedoch den gesamten Vorgang. - Es werden keine Rollbacks durchgeführt. Das bedeutet, dass die Datenbank einen gemischten Status mit einigen erfolgreichen und einigen fehlgeschlagenen Updates aufweist.
- Ihre Vorgänge mit
@check
können zu inkonsistenteren Ergebnissen führen, wenn in Ihrer@check
-Logik die Ergebnisse von Lese- und/oder Schreibvorgängen in einem vorherigen Schritt verwendet werden. - Das an den Clientcode zurückgegebene Ergebnis enthält eine komplexere Mischung aus Erfolgs- und Fehlerantworten, die verarbeitet werden müssen.
Anweisungen für Data Connect-Mutationen
Zusätzlich zu den Anweisungen, die Sie zum Definieren von Typen und Tabellen verwenden, bietet Data Connect die Anweisungen @auth
, @check
, @redact
und @transaction
, um das Verhalten von Vorgängen zu erweitern.
Anweisung | Gilt für | Beschreibung |
---|---|---|
@auth |
Abfragen und Mutationen | Definiert die Autorisierungsrichtlinie für eine Abfrage oder Mutation. Weitere Informationen finden Sie im Leitfaden zur Autorisierung und Attestierung. |
@check |
query -Felder in mehrstufigen Vorgängen |
Prüft, ob die angegebenen Felder in den Abfrageergebnissen vorhanden sind. Mit einem CEL-Ausdruck (Common Expression Language) werden Feldwerte getestet. Weitere Informationen finden Sie unter Vorgänge mit mehreren Schritten. |
@redact |
Abfragen | Entfernt einen Teil der Antwort des Kunden. Weitere Informationen finden Sie unter Vorgänge mit mehreren Schritten. |
@transaction |
Mutationen | Erzwingt, dass eine Mutation immer in einer Datenbanktransaktion ausgeführt wird. Weitere Informationen finden Sie unter Vorgänge mit mehreren Schritten. |
Nächste Schritte
Folgendes könnte Sie interessieren:
- Mutationen für Ihre Apps mit KI-Unterstützungstools generieren
- Mutationen gemäß der Autorisierungsanleitung autorisieren
- Mutations aus Ihrem Clientcode für Web, iOS, Android und Flutter aufrufen.
- Bulk-Datenvorgänge mit Mutationen ausführen