Firebase Data Connect umożliwia tworzenie łączników do instancji PostgreSQL zarządzanych za pomocą Google Cloud SQL. Te konektory to kombinacje zapytań i mutacji, które umożliwiają korzystanie z danych ze schematu.
W przewodniku dla początkujących przedstawiliśmy schemat aplikacji do oceniania filmów w PostgreSQL.
W tym przewodniku przedstawiliśmy też operacje administracyjne, które można wdrożyć i wykonać ad hoc, w tym mutacje.
- Mutacje, które można wdrożyć, to te, które implementujesz, aby wywoływać je z aplikacji klienckich w złączu, z zdefiniowanymi przez Ciebie punktami końcowymi interfejsu API. Data Connect integruje uwierzytelnianie i autoryzację z tymi mutacjami oraz generuje pakiety SDK klientaData Connect na podstawie Twojego interfejsu API.
- Mutacje administracyjne ad hoc są uruchamiane w środowiskach z uprawnieniami do wypełniania tabel i zarządzania nimi. Możesz je tworzyć i wykonywać w konsoli Firebase, w środowiskach z uprawnieniami za pomocą Firebase Admin SDK oraz w lokalnych środowiskach programistycznych za pomocą naszego rozszerzenia Data Connect do VS Code.
W tym przewodniku znajdziesz szczegółowe informacje o mutacjach, które można wdrożyć.
Cechy mutacji Data Connect
Data Connect umożliwia wykonywanie podstawowych mutacji na wszystkie sposoby, jakich można oczekiwać w przypadku bazy danych PostgreSQL:
- Wykonywanie operacji CRUD
- Zarządzanie wieloetapowymi operacjami za pomocą transakcji
Dzięki rozszerzeniom Data Connect do GraphQL możesz jednak wdrażać zaawansowane mutacje, aby tworzyć szybsze i wydajniejsze aplikacje:
- Używaj skalarów kluczy zwracanych przez wiele operacji, aby uprościć powtarzające się operacje na rekordach.
- Używaj wartości serwera, aby wypełniać dane operacjami dostarczanymi przez serwer.
- Wykonywanie zapytań w trakcie wieloetapowych operacji mutacji w celu wyszukiwania danych, co pozwala zaoszczędzić wiersze kodu i podróże do serwera.
Używanie wygenerowanych pól do wdrażania mutacji
Operacje Data Connect rozszerzą zestaw pól automatycznie wygenerowanych Data Connect na podstawie typów i relacji między typami w schemacie. Pola te są generowane przez narzędzia lokalne za każdym razem, gdy edytujesz schemat.
Wygenerowanych pól możesz używać do wdrażania mutacji, od tworzenia, aktualizowania i usuwania poszczególnych rekordów w pojedynczych tabelach po bardziej złożone aktualizacje w wielu tabelach.Załóżmy, że schemat zawiera typ Movie
i powiązany z nim typ Actor
.
Data Connect generuje pola movie_insert
, movie_update
, movie_delete
i inne.
Mutacja z polem
movie_insert
Pole |
Użyj tego pola, aby utworzyć jeden film. mutation CreateMovie($data: Movie_Data!) { movie_insert(data: $data) { key } } |
Mutacja z polem
movie_update
Pole |
Użyj tego pola, aby zaktualizować pojedynczy film według klucza. mutation UpdateMovie($myKey: Movie_Key!, $data: Movie_Data!) { movie_update(key: $myKey, data: $data) { key } } |
Mutacja z polem
movie_delete
Pole |
Użyj tego pola, aby usunąć pojedynczy film według klucza. mutation DeleteMovie($myKey: Movie_Key!) { movie_delete(key: $myKey) { key } } |
Najważniejsze elementy mutacji
Mutacje Data Connect to mutacje GraphQL z Data Connectrozszerzeniami. Podobnie jak w przypadku zwykłej mutacji GraphQL możesz zdefiniować nazwę operacji i listę zmiennych GraphQL.
Data Connect rozszerza zapytania GraphQL o niestandardowe dyrektywy, takie jak @auth
i @transaction
.
W związku z tym ta mutacja ma:
- Definicja typu
mutation
- Nazwa
SignUp
operacji (zmiany) - Argument operacji z jedną zmienną
$username
- jedna dyrektywa,
@auth
- Pojedyncze pole
user_insert
.
mutation SignUp($username: String!) @auth(level: USER) {
user_insert(data: {
id_expr: "auth.uid"
username: $username
})
}
Każdy argument mutacji wymaga deklaracji typu, wbudowanego, np. String
, lub niestandardowego, zdefiniowanego w schemacie, np. Movie
.
Zapisz podstawowe mutacje
Możesz zacząć pisać mutacje, aby tworzyć, aktualizować i usuwać poszczególne rekordy z bazy danych.
Utwórz
Zacznijmy od podstawowych kreacji.
# 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
})
}
lub 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"
})
}
Przeprowadzanie aktualizacji
Oto aktualności. Producenci i reżyserzy z pewnością mają nadzieję, że te średnie oceny są zgodne z trendami.
Pole movie_update
zawiera oczekiwany argument id
do identyfikowania rekordu i pole data
, którego możesz użyć do ustawiania wartości w tej aktualizacji.
mutation UpdateMovie(
$id: UUID!,
$genre: String!,
$rating: Int!,
$description: String!
) {
movie_update(id: $id,
data: {
genre: $genre
rating: $rating
description: $description
})
}
Aby wprowadzić kilka zmian, użyj pola 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
})
}
Używanie operacji zwiększania, zmniejszania, dołączania i dodawania na początku z elementem _update
W przypadku mutacji _update
i _updateMany
możesz jawnie ustawiać wartości w data:
, ale często bardziej opłaca się zastosować operator taki jak increment, aby zaktualizować wartości.
Aby zmodyfikować wcześniejszy przykład aktualizacji, załóżmy, że chcesz zwiększyć ocenę konkretnego filmu. Możesz używać składni rating_update
z operatorem inc
.
mutation UpdateMovie(
$id: UUID!,
$ratingIncrement: Int!
) {
movie_update(id: $id, data: {
rating_update: {
inc: $ratingIncrement
}
})
}
Data Connect obsługuje te operatory w przypadku aktualizacji pól:
inc
, aby zwiększyć typy danychInt
,Int64
,Float
,Date
iTimestamp
dec
, aby zmniejszyć wartości typów danychInt
,Int64
,Float
,Date
iTimestamp
.
W przypadku list możesz też aktualizować poszczególne wartości lub listy wartości, korzystając z tych opcji:
add
, aby dołączyć elementy, jeśli nie ma ich jeszcze na listach(z wyjątkiem list wektorowych);remove
– aby usunąć wszystkie elementy z list, z wyjątkiem list wektorowych.append
– dołączanie elementów do typów list, z wyjątkiem list wektorowych;prepend
– dodawanie elementów na początku list, z wyjątkiem list wektorów.
Usuwanie
Możesz oczywiście usunąć dane filmu. Osoby zajmujące się konserwacją filmów z pewnością będą chciały, aby fizyczne kopie filmów były przechowywane jak najdłużej.
# Delete by key
mutation DeleteMovie($id: UUID!) {
movie_delete(id: $id)
}
Możesz tu użyć _deleteMany
.
# Multiple deletes
mutation DeleteUnpopularMovies($minRating: Int!) {
movie_deleteMany(where: { rating: { le: $minRating } })
}
Zapisywanie mutacji w relacjach
Zobacz, jak używać niejawnej mutacji _upsert
w relacji.
# Create or update a one to one relation
mutation MovieMetadataUpsert($movieId: UUID!, $director: String!) {
movieMetadata_upsert(
data: { movie: { id: $movieId }, director: $director }
)
}
Projektowanie schematów pod kątem wydajnych mutacji
Data Connect udostępnia 2 ważne funkcje, które pozwalają pisać bardziej wydajne mutacje i oszczędzać operacje typu round-trip.
Kluczowe skalary to zwięzłe identyfikatory obiektów, które Data Connect automatycznie tworzy na podstawie kluczowych pól w Twoich schematach. Kluczowe skalary dotyczą wydajności, ponieważ umożliwiają uzyskanie w ramach jednego wywołania informacji o tożsamości i strukturze danych. Są one szczególnie przydatne, gdy chcesz wykonywać sekwencyjne działania na nowych rekordach i potrzebujesz unikalnego identyfikatora do przekazywania do kolejnych operacji, a także gdy chcesz uzyskać dostęp do kluczy relacyjnych, aby wykonywać dodatkowe, bardziej złożone operacje.
Korzystając z wartości serwera, możesz skutecznie zezwolić serwerowi na dynamiczne wypełnianie pól w tabelach za pomocą przechowywanych lub łatwo obliczalnych wartości zgodnie z określonymi wyrażeniami CEL po stronie serwera w argumencie expr
. Możesz na przykład zdefiniować pole z sygnaturą czasową, która jest stosowana, gdy uzyskuje się do niego dostęp przy użyciu czasu przechowywanego w żądaniu operacji, updatedAt: Timestamp!
@default(expr: "request.time")
.
Pisanie zaawansowanych mutacji: pozwól Data Connect dostarczać wartości za pomocą składni field_expr
Jak wspomnieliśmy w sekcji kluczowe wartości skalarne i wartości serwera, możesz zaprojektować schemat tak, aby serwer wypełniał wartości w przypadku typowych pól, takich jak id
i daty, w odpowiedzi na żądania klientów.
Możesz też korzystać z danych, takich jak identyfikatory użytkowników, przesyłanych w obiektach Data Connect request
z aplikacji klienckich.
Podczas wdrażania mutacji używaj składni field_expr
, aby wywoływać aktualizacje generowane przez serwer lub uzyskiwać dostęp do danych z żądań. Na przykład, aby przekazać autoryzację uid
przechowywaną w żądaniu do operacji _upsert
, przekaż "auth.uid"
w polu 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 })
}
W znanej aplikacji do tworzenia list zadań podczas tworzenia nowej listy możesz przekazać znak id_expr
, aby poinstruować serwer, aby automatycznie wygenerował identyfikator UUID dla listy.
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,
})
}
Więcej informacji znajdziesz w _Expr
skalarach w dokumentacji skalarów.
Pisanie zaawansowanych mutacji: operacje wieloetapowe
W jednej mutacji możesz uwzględnić wiele pól zapisu (np. wstawień). Możesz też chcieć odczytać bazę danych podczas wykonywania mutacji, aby wyszukać i zweryfikować istniejące dane przed wykonaniem np. wstawień lub aktualizacji. Te opcje pozwalają zaoszczędzić operacje w obie strony, a tym samym koszty.
Data Connect umożliwia wykonywanie wieloetapowej logiki w mutacjach przez obsługę:
Wiele pól do pisania
Wiele pól odczytu w mutacjach (z użyciem słowa kluczowego pola
query
).@transaction
Dyrektywa, która zapewnia obsługę transakcji znaną z relacyjnych baz danych.@check
dyrektywa, która umożliwia ocenę zawartości odczytów za pomocą wyrażeń CEL i na podstawie wyników tej oceny:- Przeprowadzanie operacji tworzenia, aktualizowania i usuwania zdefiniowanych przez mutację
- Przejdź do zwracania wyników pola zapytania
- Używaj zwróconych wiadomości do wykonywania odpowiedniej logiki w kodzie klienta.
@redact
Dyrektywa, która umożliwia pomijanie wyników pól zapytań w wynikach protokołu przewodowego.Powiązanie CEL
response
, które przechowuje zgromadzone wyniki wszystkich mutacji i zapytań wykonanych w ramach złożonej, wieloetapowej operacji. Możesz uzyskać dostęp do wiązaniaresponse
:- W
@check
dyrektywach za pomocą argumentuexpr:
- Wartości serwera, używając składni
field_expr
- W
Dyrektywa @transaction
Obsługa mutacji wieloetapowych obejmuje obsługę błędów za pomocą transakcji.
Dyrektywa @transaction
wymusza, aby mutacja z jednym polem zapisu (np. _insert
lub _update
) lub z wieloma polami zapisu zawsze była wykonywana w transakcji bazy danych.
Mutacje bez
@transaction
wykonują każde pole główne po kolei. Operacja zgłasza błędy jako błędy częściowe, ale nie informuje o skutkach kolejnych wykonań.Mutacje oznaczone symbolem
@transaction
gwarantują pełny sukces lub pełną porażkę. Jeśli którekolwiek z pól w transakcji nie zostanie przetworzone, cała transakcja zostanie wycofana.
Dyrektywy @check
i @redact
Dyrektywa @check
sprawdza, czy w wynikach zapytania znajdują się określone pola. Do testowania wartości pól używane jest wyrażenie w języku Common Expression Language (CEL). Domyślne działanie dyrektywy polega na sprawdzaniu i odrzucaniu węzłów, których wartość to null
lub []
(puste listy).
Dyrektywa @redact
usuwa część odpowiedzi klienta. Zaciemnione pola są nadal oceniane pod kątem efektów ubocznych (w tym zmian danych i @check
), a wyniki są nadal dostępne w późniejszych krokach wyrażeń CEL.
Użyj właściwości @check
, @check(message:)
i @redact
.
Głównym zastosowaniem funkcji @check
i @redact
jest wyszukiwanie powiązanych danych w celu podjęcia decyzji, czy zezwolić na określone operacje. Wyszukiwanie jest używane w logice, ale jest ukryte przed klientami. Zapytanie może zwracać przydatne komunikaty, które umożliwiają prawidłową obsługę w kodzie klienta.
Na przykład to pole zapytania sprawdza, czy osoba wysyłająca żądanie ma odpowiednią rolę „administratora”, aby wyświetlić użytkowników, którzy mogą edytować 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
}
}
}
Więcej informacji o dyrektywach @check
i @redact
w sprawdzaniu autoryzacji znajdziesz w omówieniu wyszukiwania danych autoryzacyjnych.
Używanie urządzenia @check
do weryfikacji kluczy
Niektóre pola mutacji, np. _update
, mogą nie działać, jeśli rekord z określonym kluczem nie istnieje. Podobnie wyszukiwania mogą zwracać wartość null lub pustą listę. Nie są one uznawane za błędy, więc nie powodują wycofania zmian.
Aby temu zapobiec, sprawdź, czy klucze można znaleźć za pomocą dyrektywy @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")
}
Używaj powiązania response
do łączenia mutacji wieloetapowych
Podstawowe podejście do tworzenia powiązanych rekordów, np. nowego rekordu Movie
i powiązanego wpisu MovieMetadata
, polega na wykonaniu tych czynności:
- Wywołanie mutacji
_insert
dlaMovie
- Przechowywanie zwróconego klucza utworzonego filmu
- Następnie wywołaj drugą mutację
_insert
, aby utworzyć rekordMovieMetadata
.
Dzięki Data Connect możesz jednak obsłużyć ten typowy przypadek w ramach jednej wieloetapowej operacji, uzyskując dostęp do wyników pierwszego _insert
w drugim _insert
.
Stworzenie aplikacji do recenzowania filmów, która odniesie sukces, wymaga dużo pracy. Śledźmy listę zadań do wykonania na nowym przykładzie.
Używanie response
do ustawiania pól z wartościami serwera
W tej mutacji listy zadań:
- Wiązanie
response
reprezentuje dotychczasowy obiekt częściowej odpowiedzi, który zawiera wszystkie pola mutacji najwyższego poziomu przed bieżącym polem. - Wyniki początkowej operacji
todoList_insert
, która zwraca poleid
(klucz), są dostępne później w operacjiresponse.todoList_insert.id
, dzięki czemu możemy od razu wstawić nowe zadanie do wykonania.
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,
})
}
Użyj polecenia response
, aby zweryfikować pola za pomocą funkcji @check
response
jest też dostępny w @check(expr: "...")
, więc możesz go używać do tworzenia jeszcze bardziej złożonych mechanizmów logicznych po stronie serwera. W połączeniu z query { … }
krokami w mutacjach możesz osiągnąć znacznie więcej bez dodatkowych podróży w obie strony między klientem a serwerem.
W przykładzie poniżej zwróć uwagę, że @check
ma już dostęp do response.query
, ponieważ @check
zawsze jest uruchamiany po kroku, do którego jest dołączony.
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,
})
}
Więcej informacji o powiązaniu response
znajdziesz w dokumentacji referencyjnej CEL.
Zrozumienie przerwanych operacji za pomocą symboli @transaction
i query @check
W przypadku mutacji wieloetapowych mogą wystąpić błędy:
- Operacje na bazie danych mogą się nie powieść.
- logika zapytania
@check
może zakończyć operacje.
Data Connect zaleca używanie dyrektywy @transaction
w przypadku mutacji wieloetapowych. Dzięki temu baza danych jest bardziej spójna, a wyniki mutacji są łatwiejsze do obsługi w kodzie klienta:
- Operacja zostanie zakończona przy pierwszym błędzie lub nieudanym
@check
, więc nie musisz zarządzać wykonywaniem kolejnych pól ani oceną CEL. - Cofanie zmian jest wykonywane w odpowiedzi na błędy bazy danych lub
@check
logikę, co zapewnia spójny stan bazy danych. - Błąd wycofania jest zawsze zwracany do kodu klienta.
W niektórych przypadkach możesz nie używać @transaction
. Możesz na przykład wybrać spójność ostateczną, jeśli potrzebujesz większej przepustowości, skalowalności lub dostępności. Musisz jednak zarządzać bazą danych i kodem klienta, aby umożliwić uzyskanie wyników:
- Jeśli jedno pole nie zostanie przetworzone z powodu operacji na bazie danych, kolejne pola będą nadal wykonywane. Jednak nieudane
@check
nadal przerywają całą operację. - Wycofywanie zmian nie jest wykonywane, co oznacza, że baza danych może mieć stan mieszany, w którym niektóre aktualizacje zostały przeprowadzone, a niektóre nie.
- Operacje z użyciem
@check
mogą dawać bardziej niespójne wyniki, jeśli logika@check
korzysta z wyników odczytu lub zapisu z poprzedniego kroku. - Wynik zwrócony do kodu klienta będzie zawierał bardziej złożoną kombinację odpowiedzi o powodzeniu i niepowodzeniu, które należy obsłużyć.
Dyrektywy dotyczące mutacji Data Connect
Oprócz dyrektyw używanych do definiowania typów i tabel Data Connect udostępnia dyrektywy @auth
, @check
, @redact
i @transaction
, które rozszerzają działanie operacji.
Dyrektywa | Dotyczy | Opis |
---|---|---|
@auth |
Zapytania i mutacje | Definiuje zasadę autoryzacji dla zapytania lub mutacji. Zapoznaj się z przewodnikiem po autoryzacji i atestowaniu. |
@check |
query pól w operacjach wieloetapowych |
Sprawdza, czy w wynikach zapytania znajdują się określone pola. Do testowania wartości pól używa się wyrażenia w języku Common Expression Language (CEL). Zobacz Operacje wieloetapowe. |
@redact |
Zapytania | Usuwa część odpowiedzi klienta. Zobacz Operacje wieloetapowe. |
@transaction |
Mutacje | Wymusza, aby mutacja zawsze była wykonywana w transakcji bazy danych. Zobacz Operacje wieloetapowe. |
Dalsze kroki
Może Cię zainteresować:
- Generowanie mutacji w aplikacjach za pomocą narzędzi opartych na AI
- Autoryzowanie mutacji zgodnie z przewodnikiem po autoryzacji
- Wywoływanie mutacji z kodu klienta w przypadku internetu, iOS, Androida i Fluttera.
- Wykonywanie operacji na danych zbiorczych za pomocą mutacji