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 .
- Выполнение массовых операций с данными с мутациями