Firebase Data Connect позволяет создавать коннекторы для ваших экземпляров PostgreSQL, управляемых с помощью Google Cloud SQL. Эти коннекторы представляют собой комбинации запросов и мутаций для использования ваших данных из вашей схемы.
В руководстве по началу работы представлена схема приложения для обзора фильмов для PostgreSQL.
В этом руководстве также были представлены как развертываемые, так и специальные административные операции, включая мутации.
- Развертываемые мутации — это те, которые вы реализуете для вызова из клиентских приложений в коннекторе с конечными точками API, которые вы определяете. Data Connect интегрирует аутентификацию и авторизацию в эти мутации и генерирует клиентские SDK на основе вашего API.
- Административные мутации ad hoc запускаются из привилегированных сред для заполнения и управления таблицами. Вы можете создавать и выполнять их в консоли Firebase , из привилегированных сред с помощью Firebase Admin SDK и в локальных средах разработки с помощью нашего расширения Data Connect VS Code.
В этом руководстве более подробно рассматриваются развертываемые мутации .
Особенности мутаций Data Connect
Data Connect позволяет выполнять базовые мутации всеми способами, которые можно ожидать от базы данных PostgreSQL:
- Выполнение операций CRUD
- Управляйте многошаговыми операциями с транзакциями
Но с помощью расширений Data Connect для GraphQL вы можете реализовать расширенные мутации для более быстрых и эффективных приложений:
- Используйте ключевые скаляры, возвращаемые многими операциями, для упрощения повторяющихся операций с записями.
- Используйте значения сервера для заполнения данных с помощью операций, предоставляемых сервером.
- Выполняйте запросы в ходе многошаговых операций мутации для поиска данных, экономя строки кода и количество обращений к серверу.
Используйте сгенерированные поля для внедрения мутаций
Ваши операции Data Connect расширят набор полей, автоматически сгенерированных Data Connect на основе типов и отношений типов в вашей схеме. Эти поля генерируются локальными инструментами всякий раз, когда вы редактируете свою схему.
Сгенерированные поля можно использовать для реализации мутаций: от создания, обновления и удаления отдельных записей в отдельных таблицах до более сложных обновлений нескольких таблиц. Предположим, что ваша схема содержит тип Movie
и связанный с ним тип Actor
. Data Connect генерирует поля movie_insert
, movie_update
, movie_delete
и другие.
Мутация с
поле movie_insert
Поле | Используйте это поле для создания одного фильма. mutation CreateMovie($data: Movie_Data!) { movie_insert(data: $data) { key } } |
Мутация с
поле movie_update
Поле | Используйте это поле для обновления отдельного фильма по его ключу. mutation UpdateMovie($myKey: Movie_Key!, $data: Movie_Data!) { movie_update(key: $myKey, data: $data) { key } } |
Мутация с
поле movie_delete
Поле | Используйте это поле для удаления отдельного фильма по его ключу. mutation DeleteMovie($myKey: Movie_Key!) { movie_delete(key: $myKey) { key } } |
Основные элементы мутации
Мутации Data Connect — это мутации GraphQL с расширениями Data Connect . Как и в случае с обычной мутацией GraphQL, вы можете определить имя операции и список переменных GraphQL.
Data Connect расширяет запросы GraphQL с помощью настраиваемых директив, таких как @auth
и @transaction
.
Итак, следующая мутация имеет:
- Определение типа
mutation
- Имя операции
SignUp
(мутации) - Аргумент операции с одной переменной
$username
- Одна директива,
@auth
- Одно поле
user_insert
.
mutation SignUp($username: String!) @auth(level: USER) {
user_insert(data: {
id_expr: "auth.uid"
username: $username
})
}
Каждый аргумент мутации требует объявления типа: встроенного, например String
, или пользовательского, определяемого схемой типа, например Movie
.
Напишите основные мутации
Вы можете начать писать мутации для создания, обновления и удаления отдельных записей из вашей базы данных.
Создавать
Давайте сделаем базовые творения.
# 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
})
}
Или неожиданность.
# 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"
})
}
Выполнять обновления
Вот обновления. Продюсеры и режиссеры, конечно, надеются, что эти средние рейтинги будут в тренде.
Поле movie_update
содержит ожидаемый аргумент id
для идентификации записи и поле data
, которое можно использовать для установки значений в этом обновлении.
mutation UpdateMovie(
$id: UUID!,
$genre: String!,
$rating: Int!,
$description: String!
) {
movie_update(id: $id,
data: {
genre: $genre
rating: $rating
description: $description
})
}
Для выполнения нескольких обновлений используйте поле 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
})
}
Используйте операции увеличения, уменьшения, добавления и добавления в начало с помощью _update
Хотя в мутациях _update
и _updateMany
можно явно задавать значения в data:
часто бывает разумнее применить такой оператор, как инкремент, для обновления значений.
Чтобы изменить предыдущий пример обновления, предположим, что вы хотите увеличить рейтинг определенного фильма. Вы можете использовать синтаксис rating_update
с оператором inc
.
mutation UpdateMovie(
$id: UUID!,
$ratingIncrement: Int!
) {
movie_update(id: $id, data: {
rating_update: {
inc: $ratingIncrement
}
})
}
Data Connect поддерживает следующих операторов для полевых обновлений:
-
inc
для увеличения типов данныхInt
,Int64
,Float
,Date
иTimestamp
-
dec
для уменьшения типов данныхInt
,Int64
,Float
,Date
иTimestamp
Для списков вы также можете обновить их отдельными значениями или списками значений, используя:
-
add
для добавления элементов, если они еще не присутствуют в типах списков, за исключением векторных списков -
remove
, чтобы удалить все элементы, если они есть, из типов списков, за исключением векторных списков -
append
для добавления элементов к типам списков, за исключением векторных списков -
prepend
для добавления элементов в начало списков типов, за исключением списков векторов
Выполнять удаления
Конечно, вы можете удалить данные о фильмах. Защитники фильмов, безусловно, захотят, чтобы физические фильмы сохранялись как можно дольше.
# Delete by key
mutation DeleteMovie($id: UUID!) {
movie_delete(id: $id)
}
Здесь вы можете использовать _deleteMany
.
# Multiple deletes
mutation DeleteUnpopularMovies($minRating: Int!) {
movie_deleteMany(where: { rating: { le: $minRating } })
}
Напишите мутации в отношениях
Посмотрите, как использовать неявную мутацию _upsert
в отношении.
# Create or update a one to one relation
mutation MovieMetadataUpsert($movieId: UUID!, $director: String!) {
movieMetadata_upsert(
data: { movie: { id: $movieId }, director: $director }
)
}
Разработать схемы эффективных мутаций
Data Connect предоставляет две важные функции, которые позволяют вам писать более эффективные мутации и экономить циклические операции.
Ключевые скаляры — это краткие идентификаторы объектов, которые Data Connect автоматически собирает из ключевых полей в ваших схемах. Ключевые скаляры — это эффективность, позволяющая вам находить в одном вызове информацию об идентичности и структуре ваших данных. Они особенно полезны, когда вы хотите выполнять последовательные действия с новыми записями и вам нужен уникальный идентификатор для передачи в предстоящие операции, а также когда вы хотите получить доступ к реляционным ключам для выполнения дополнительных более сложных операций.
Используя серверные значения , вы можете эффективно позволить серверу динамически заполнять поля в ваших таблицах, используя сохраненные или легко вычисляемые значения в соответствии с определенными серверными выражениями CEL в аргументе expr
. Например, вы можете определить поле с временной меткой, применяемой при доступе к полю, используя время, сохраненное в запросе операции, updatedAt: Timestamp! @default(expr: "request.time")
.
Напишите расширенные мутации: позвольте Data Connect предоставлять значения, используя синтаксис field_expr
Как обсуждалось в разделе «Ключевые скаляры и серверные значения» , вы можете разработать свою схему таким образом, чтобы сервер заполнял значения для общих полей, таких как id
и даты, в ответ на клиентские запросы.
Кроме того, вы можете использовать данные, такие как идентификаторы пользователей, отправленные в объектах request
Data Connect из клиентских приложений.
При реализации мутаций используйте синтаксис field_expr
для запуска обновлений, сгенерированных сервером, или доступа к данным из запросов. Например, чтобы передать uid
авторизации, сохраненный в запросе, в операцию _upsert
, передайте "auth.uid"
в поле 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 })
}
Или, в знакомых приложениях для списков дел при создании нового списка дел, вы можете передать id_expr
для обучения сервера автоматически генерировать UUID для списка.
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,
})
}
Более подробную информацию см. в описании скаляров _Expr
в справочнике по скалярам .
Написание расширенных мутаций: многошаговые операции
Существует множество ситуаций, в которых вам может понадобиться включить несколько полей записи (например, вставки) в одну мутацию. Вы также можете захотеть прочитать свою базу данных во время выполнения мутации, чтобы просмотреть и проверить существующие данные перед выполнением, например, вставок или обновлений. Эти опции экономят операции кругового обхода и, следовательно, затраты.
Data Connect позволяет вам выполнять многошаговую логику в ваших мутациях, поддерживая:
Несколько полей записи
Несколько полей чтения в ваших мутациях (с использованием ключевого слова
query
field).Директива
@transaction
, которая обеспечивает поддержку транзакций, знакомую по реляционным базам данных.Директива
@check
, которая позволяет оценивать содержимое прочтений с использованием выражений CEL и на основе результатов такой оценки:- Продолжайте создавать, обновлять и удалять объекты, определенные мутацией.
- Перейти к возврату результатов поля запроса
- Используйте возвращаемые сообщения для выполнения соответствующей логики в клиентском коде.
Директива
@redact
, которая позволяет исключить результаты поля запроса из результатов проводного протокола.Привязка
response
CEL, которая хранит накопленные результаты всех мутаций и запросов, выполненных в сложной многошаговой операции. Вы можете получить доступ к привязкеresponse
:- В директивах
@check
, через аргументexpr:
- Со значениями сервера, используя синтаксис
field_expr
- В директивах
Директива @transaction
Поддержка многошаговых мутаций включает обработку ошибок с использованием транзакций.
Директива @transaction
обеспечивает, чтобы мутация — как с одним полем записи (например, _insert
или _update
), так и с несколькими полями записи — всегда выполнялась в транзакции базы данных.
Мутации без
@transaction
выполняют каждое корневое поле одно за другим в последовательности. Операция выявляет любые ошибки как частичные ошибки поля, но не последствия последующих выполнений.Мутации с
@transaction
гарантированно либо полностью успешны, либо полностью провалены. Если какое-либо из полей в транзакции не выполняется, вся транзакция откатывается.
Директивы @check
и @redact
Директива @check
проверяет, присутствуют ли указанные поля в результатах запроса. Выражение Common Expression Language (CEL) используется для проверки значений полей. Поведение директивы по умолчанию — проверять и отклонять узлы, значение которых равно null
или []
(пустые списки).
Директива @redact
редактирует часть ответа клиента. Редактированные поля все еще оцениваются на предмет побочных эффектов (включая изменения данных и @check
), а результаты все еще доступны для последующих шагов в выражениях CEL.
Используйте @check
, @check(message:)
и @redact
Основное применение @check
и @redact
— поиск связанных данных для принятия решения о том, следует ли авторизовать определенные операции, используя поиск в логике, но скрывая его от клиентов. Ваш запрос может возвращать полезные сообщения для правильной обработки в клиентском коде.
Для иллюстрации следующее поле запроса проверяет, имеет ли запрашивающая сторона соответствующую роль «администратор» для просмотра пользователей, которые могут редактировать фильм.
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
}
}
}
Дополнительную информацию о директивах @check
и @redact
при проверке авторизации см. в обсуждении поиска данных авторизации .
Используйте @check
для проверки ключей
Некоторые поля мутации, такие как _update
, могут быть пустыми, если запись с указанным ключом не существует. Аналогично, поиск может возвращать null или пустой список. Это не считается ошибками и, следовательно, не вызовет откатов.
Чтобы защититься от такого результата, проверьте, можно ли найти ключи, используя директиву @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")
}
Используйте связывание response
для создания цепочки многошаговых мутаций
Основной подход к созданию связанных записей, например, нового Movie
и связанной с ним записи MovieMetadata
, заключается в следующем:
- Вызов мутации
_insert
дляMovie
- Сохраните возвращенный ключ созданного фильма.
- Затем вызовите вторую мутацию
_insert
для создания записиMovieMetadata
.
Но с помощью Data Connect вы можете обработать этот распространенный случай за одну многошаговую операцию, обратившись к результатам первой _insert
во второй _insert
.
Создание успешного приложения для обзора фильмов — это большая работа. Давайте отследим наш список дел с помощью нового примера.
Используйте response
для установки полей со значениями сервера
В следующем списке дел мутация:
- Привязка
response
представляет собой частичный объект ответа на данный момент, который включает все поля мутации верхнего уровня до текущего. - Результаты первоначальной операции
todoList_insert
, которая возвращает полеid
(ключа), доступны позже вresponse.todoList_insert.id
, поэтому мы можем немедленно вставить новый элемент списка дел.
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,
})
}
Используйте response
для проверки полей с помощью @check
response
также доступен в @check(expr: "...")
, поэтому вы можете использовать его для построения еще более сложной логики на стороне сервера. В сочетании с шагами query { … }
в мутациях вы можете достичь гораздо большего без дополнительных клиент-серверных циклов.
Обратите внимание: в следующем примере @check
уже имеет доступ к response.query
, поскольку @check
всегда запускается после шага, к которому он прикреплен.
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,
})
}
Более подробную информацию о привязке response
см. в справочнике CEL .
Понимание прерванных операций с помощью @transaction
и query @check
Многошаговые мутации могут привести к ошибкам:
- Операции с базой данных могут завершиться неудачей.
- Логика запроса
@check
может завершить работу.
Data Connect рекомендует использовать директиву @transaction
с вашими многошаговыми мутациями. Это приводит к более согласованной базе данных и результатам мутаций, которые легче обрабатывать в клиентском коде:
- При первой ошибке или неудачном выполнении
@check
операция будет прекращена, поэтому нет необходимости управлять выполнением каких-либо последующих полей или оценкой CEL. - Откаты выполняются в ответ на ошибки базы данных или логику
@check
, обеспечивая согласованное состояние базы данных. - Ошибка отката всегда возвращается в клиентский код.
Могут быть некоторые варианты использования, когда вы решите не использовать @transaction
: вы можете выбрать конечную согласованность, если, например, вам нужна более высокая пропускная способность, масштабируемость или доступность. Однако вам нужно управлять своей базой данных и клиентским кодом, чтобы обеспечить результаты:
- Если одно поле выходит из строя из-за операций с базой данных, последующие поля продолжат выполняться. Однако неудачные
@check
все равно завершают всю операцию. - Откаты не выполняются, что означает смешанное состояние базы данных с некоторыми успешными обновлениями и некоторыми неудачными обновлениями.
- Ваши операции с
@check
могут давать более непоследовательные результаты, если ваша логика@check
использует результаты чтения и/или записи на предыдущем шаге. - Результат, возвращаемый клиентскому коду, будет содержать более сложную смесь ответов об успехе и неудаче, которые необходимо обработать.
Директивы для мутаций Data Connect
В дополнение к директивам, которые вы используете при определении типов и таблиц, Data Connect предоставляет директивы @auth
, @check
, @redact
и @transaction
для расширения поведения операций.
Директива | Применимо к | Описание |
---|---|---|
@auth | Запросы и мутации | Определяет политику авторизации для запроса или мутации. См. руководство по авторизации и аттестации . |
@check | поля query в многошаговых операциях | Проверяет, что указанные поля присутствуют в результатах запроса. Выражение Common Expression Language (CEL) используется для проверки значений полей. См. Многошаговые операции . |
@redact | Запросы | Редактирует часть ответа от клиента. См. Многошаговые операции . |
@transaction | Мутации | Обеспечивает, чтобы мутация всегда выполнялась в транзакции базы данных. См. Многошаговые операции . |
Следующие шаги
Вас может заинтересовать:
- Создание мутаций для ваших приложений с использованием инструментов помощи ИИ
- Авторизация ваших мутаций согласно руководству по авторизации
- Вызов мутаций из клиентского кода для веб-приложений , iOS , Android и Flutter .
- Выполнение массовых операций с данными с мутациями