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 позволяет вам реализовывать многошаговую логику в ваших мутациях, поддерживая:
Несколько полей записи
Несколько полей чтения в ваших мутациях (с использованием ключевого слова
queryfield).Директива
@transaction, которая обеспечивает поддержку транзакций, знакомую по реляционным базам данных.Директива
@check, которая позволяет оценивать содержимое прочтений с использованием выражений CEL и на основе результатов такой оценки:- Продолжайте создавать, обновлять и удалять объекты, определенные мутацией.
- Продолжайте возвращать результаты поля запроса
- Используйте возвращаемые сообщения для реализации соответствующей логики в клиентском коде.
Директива
@redact, которая позволяет исключить результаты поля запроса из результатов проводного протокола.Привязка
responseCEL, в которой хранятся накопленные результаты всех мутаций и запросов, выполненных в рамках сложной многоэтапной операции. Вы можете получить доступ к привязке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 .
- Выполнение массовых операций с данными с мутациями