Firebase SQL Connect te permite crear conectores para tus instancias de PostgreSQL administradas con Google Cloud SQL. Estos conectores son combinaciones de consultas y mutaciones para usar tus datos desde tu esquema.
En la guía de introducción, se presentó un esquema de app de opinión sobre películas para PostgreSQL.
En esa guía, también se presentaron operaciones administrativas implementables y ad hoc, incluidas las mutaciones.
- Las mutaciones implementables son las que implementas para llamar desde apps cliente en un conector, con extremos de API que defines. SQL Connect integra la autenticación y la autorización en estas mutaciones, y genera SDKs de cliente basados en tu API.
- Las mutaciones administrativas ad hoc se ejecutan desde entornos con privilegios para propagar y administrar tablas. Puedes crearlas y ejecutarlas en la Firebase consola, desde entornos con privilegios con la Firebase Admin SDK, y en entornos de desarrollo locales con nuestra extensión de SQL Connect para VS Code.
En esta guía, se analiza en detalle las mutaciones implementables.
Funciones de las mutaciones de SQL Connect
SQL Connect te permite realizar mutaciones básicas de todas las formas que esperarías dada una base de datos de PostgreSQL:
- Realiza operaciones CRUD.
- Administra operaciones de varios pasos con transacciones.
Sin embargo, con las extensiones de SQL Connect a GraphQL, puedes implementar mutaciones avanzadas para apps más rápidas y eficientes:
- Usa escalares clave que muestran muchas operaciones para simplificar las operaciones repetidas en los registros.
- Usa valores del servidor para propagar datos con operaciones proporcionadas por el servidor.
- Realiza consultas en el transcurso de operaciones de mutación de varios pasos para buscar datos, guardar líneas de código y viajes de ida y vuelta al servidor.
Usa campos generados para implementar mutaciones
Tus SQL Connect operaciones extenderán un conjunto de campos generados automáticamente por SQL Connect en función de los tipos y las relaciones de tipos en tu esquema. Estas herramientas locales generan estos campos cada vez que editas tu esquema.
Puedes usar campos generados para implementar mutaciones, desde crear, actualizar y borrar registros individuales en tablas únicas hasta actualizaciones más complejas de varias tablas.Supongamos que tu esquema contiene un tipo Movie y un tipo Actor asociado.
SQL Connect genera campos movie_insert,
movie_update, movie_delete y mucho más.
Mutación con el
movie_insert campo
|
El campo |
Usa este campo para crear una sola película. mutation CreateMovie($data: Movie_Data!) { movie_insert(data: $data) { key } } |
Mutación con el
movie_update campo
|
El campo |
Usa este campo para actualizar una sola película por su clave. mutation UpdateMovie($myKey: Movie_Key!, $data: Movie_Data!) { movie_update(key: $myKey, data: $data) { key } } |
Mutación con el
movie_delete campo
|
El campo |
Usa este campo para borrar una sola película por su clave. mutation DeleteMovie($myKey: Movie_Key!) { movie_delete(key: $myKey) { key } } |
Elementos esenciales de una mutación
SQL Connect mutaciones son mutaciones de GraphQL con SQL Connect extensiones. Al igual que con una mutación de GraphQL normal, puedes definir un nombre de operación y una lista de variables de GraphQL.
SQL Connect extiende las consultas de GraphQL con directivas personalizadas como
@auth y @transaction.
Por lo tanto, la siguiente mutación tiene lo siguiente:
- Una definición de tipo
mutation - Un nombre de operación (mutación)
SignUp - Un solo argumento de operación
$username - Una sola directiva,
@auth - Un solo campo
user_insert
mutation SignUp($username: String!) @auth(level: USER) {
user_insert(data: {
id_expr: "auth.uid"
username: $username
})
}
Cada argumento de mutación requiere una declaración de tipo, un tipo integrado como String o un tipo personalizado definido por el esquema como Movie.
Escribe mutaciones básicas
Puedes comenzar a escribir mutaciones para crear, actualizar y borrar registros individuales de tu base de datos.
Crear
Hagamos creaciones básicas.
# 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
})
}
O una inserción o actualización.
# 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"
})
}
Realiza actualizaciones
Aquí tienes actualizaciones. Los productores y directores seguramente esperan que esas calificaciones promedio estén en tendencia.
El campo movie_update contiene un argumento id esperado para identificar un registro y un campo data que puedes usar para establecer valores en esta actualización.
mutation UpdateMovie(
$id: UUID!,
$genre: String!,
$rating: Int!,
$description: String!
) {
movie_update(id: $id,
data: {
genre: $genre
rating: $rating
description: $description
})
}
Para realizar varias actualizaciones, usa el campo 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
})
}
Usa operaciones de incremento, decremento, anexión y anteposición con _update
Si bien en las mutaciones _update y _updateMany puedes establecer valores de forma explícita en
data:, a menudo es más conveniente aplicar un operador como el incremento para actualizar
valores.
Para modificar el ejemplo de actualización anterior, supongamos que deseas aumentar la calificación de una película en particular. Puedes usar la sintaxis rating_update con el operador inc.
mutation UpdateMovie(
$id: UUID!,
$ratingIncrement: Int!
) {
movie_update(id: $id, data: {
rating_update: {
inc: $ratingIncrement
}
})
}
SQL Connect admite los siguientes operadores para las actualizaciones de campos:
incpara aumentar los tipos de datosInt,Int64,Float,DateyTimestampdecpara disminuir los tipos de datosInt,Int64,Float,DateyTimestamp
Para las listas, también puedes actualizar con valores individuales o listas de valores con lo siguiente:
addpara anexar elementos si aún no están presentes en los tipos de lista, excepto las listas de vectoresremovepara quitar todos los elementos, si están presentes, de los tipos de lista, excepto las listas de vectoresappendpara anexar elementos a los tipos de lista, excepto las listas de vectoresprependpara anteponer elementos a los tipos de lista, excepto las listas de vectores
Realiza eliminaciones
Por supuesto, puedes borrar datos de películas. Los conservacionistas de películas seguramente querrán que las películas físicas se mantengan durante el mayor tiempo posible.
# Delete by key
mutation DeleteMovie($id: UUID!) {
movie_delete(id: $id)
}
Aquí puedes usar _deleteMany.
# Multiple deletes
mutation DeleteUnpopularMovies($minRating: Int!) {
movie_deleteMany(where: { rating: { le: $minRating } })
}
Escribe mutaciones en relaciones
Observa cómo usar la mutación _upsert implícita en una relación.
# Create or update a one to one relation
mutation MovieMetadataUpsert($movieId: UUID!, $director: String!) {
movieMetadata_upsert(
data: { movie: { id: $movieId }, director: $director }
)
}
Diseña esquemas para mutaciones eficientes
SQL Connect proporciona dos funciones importantes que te permiten escribir mutaciones más eficientes y guardar operaciones de ida y vuelta.
Los escalares clave son identificadores de objetos concisos que SQL Connect ensambla automáticamente a partir de campos clave en tus esquemas. Los escalares clave son sobre la eficiencia, lo que te permite encontrar en una sola llamada información sobre la identidad y la estructura de tus datos. Son especialmente útiles cuando deseas realizar acciones secuenciales en registros nuevos y necesitas un identificador único para pasar a las próximas operaciones, y también cuando deseas acceder a claves relacionales para realizar operaciones adicionales más complejas.
Con los valores del servidor, puedes permitir que el servidor propague de forma eficaz los
campos de tus tablas de forma dinámica con valores almacenados o fácilmente computables según
expresiones CEL particulares del servidor en el argumento expr. Por ejemplo, puedes definir un campo con una marca de tiempo aplicada cuando se accede al campo con
la hora almacenada en una solicitud de operación, updatedAt: Timestamp!
@default(expr: "request.time").
Escribe mutaciones avanzadas: permite que SQL Connect proporcione valores con la sintaxis field_expr
Como se explicó en los escalares clave y los valores del servidor,
puedes diseñar tu esquema de modo que el servidor propague valores para campos comunes
como ids y fechas en respuesta a las solicitudes del cliente.
Además, puedes usar datos, como IDs de usuario, que se envían en
SQL Connect request objetos desde apps cliente.
Cuando implementes mutaciones, usa la sintaxis field_expr para activar actualizaciones generadas por el servidor o acceder a datos de solicitudes. Por ejemplo, para pasar
la autorización uid almacenada en una solicitud a una operación _upsert, pasa
"auth.uid" en el campo 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 })
}
O bien, en una app de lista de tareas pendientes familiar, cuando crees una nueva lista de tareas pendientes, puedes pasar id_expr para indicarle al servidor que genere automáticamente un UUID para la lista.
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,
})
}
Para obtener más información, consulta los _Expr escalares en la
referencia de escalares.
Escribe mutaciones avanzadas: operaciones de varios pasos
Hay muchas situaciones en las que es posible que desees incluir varios campos de escritura (como inserciones) en una mutación. Es posible que también desees leer tu base de datos durante la ejecución de una mutación para buscar y verificar los datos existentes antes de realizar, por ejemplo, inserciones o actualizaciones. Estas opciones guardan operaciones de ida y vuelta y, por lo tanto, costos.
SQL Connect te permite realizar lógica de varios pasos en tus mutaciones, ya que admite lo siguiente:
Varios campos de escritura
Varios campos de lectura en tus mutaciones (con la palabra clave del campo
query)La directiva
@transaction, que proporciona compatibilidad con transacciones familiares de bases de datos relacionalesLa directiva
@check, que te permite evaluar el contenido de las lecturas con expresiones CEL y, según los resultados de dicha evaluación, hacer lo siguiente:- Continuar con las creaciones, actualizaciones y eliminaciones definidas por una mutación
- Continuar para mostrar los resultados de un campo de consulta
- Usar mensajes devueltos para realizar la lógica adecuada en tu código de cliente
La directiva
@redact, que te permite omitir los resultados del campo de consulta de los resultados del protocolo de transmisiónLa vinculación
responsede CEL, que almacena los resultados acumulados de todas las mutaciones y consultas realizadas en una operación compleja de varios pasos Puedes acceder a la vinculaciónresponsede la siguiente manera:- En las directivas
@check, a través del argumentoexpr: - Con valores del servidor, con la sintaxis
field_expr
- En las directivas
La directiva @transaction
La compatibilidad con mutaciones de varios pasos incluye el manejo de errores con transacciones.
La directiva @transaction exige que una mutación, ya sea con un solo campo de escritura (por ejemplo, _insert o _update) o con varios campos de escritura, siempre se ejecute en una transacción de base de datos.
Las mutaciones sin
@transactionejecutan cada campo raíz uno tras otro en secuencia. La operación muestra los errores como errores de campo parciales, pero no los impactos de las ejecuciones posteriores.Se garantiza que las mutaciones con
@transactionse completarán o fallarán por completo. Si falla alguno de los campos dentro de la transacción, se revierte toda la transacción.
Las directivas @check y @redact
La directiva @check verifica que los campos especificados estén presentes en los resultados de la consulta. Se usa una expresión de Common Expression Language (CEL) para probar los valores de los campos. El comportamiento predeterminado de la directiva es verificar y rechazar los nodos cuyo valor es null o [] (listas vacías).
La directiva @redact redacta una parte de la respuesta del cliente. Los campos redactados aún se evalúan para detectar efectos secundarios (incluidos los cambios de datos y @check), y los resultados aún están disponibles para los pasos posteriores en las expresiones CEL.
Usa @check, @check(message:) y @redact
Un uso importante de @check y @redact es buscar datos relacionados para decidir si se deben autorizar ciertas operaciones, usar la búsqueda en la lógica, pero ocultarla a los clientes. Tu consulta puede mostrar mensajes útiles para el manejo correcto en el código de cliente.
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
}
}
}
Para obtener más información sobre las directivas @check y @redact en las verificaciones de autorización,
consulta el análisis de la búsqueda de datos de autorización.
Usa @check para validar claves
Es posible que algunos campos de mutación, como _update, no funcionen si no existe un registro con una clave especificada. Del mismo modo, las búsquedas pueden mostrar valores nulos o una lista vacía. Estos no se consideran errores y, por lo tanto, no activarán reversiones.
Para evitar este resultado, prueba si se pueden encontrar claves con la directiva @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")
}
Usa la vinculación response para encadenar mutaciones de varios pasos
El enfoque básico para crear registros relacionados, por ejemplo, un Movie nuevo y
una entrada MovieMetadata asociada, es el siguiente:
- Llama a una mutación
_insertparaMovie. - Almacena la clave devuelta de la película creada.
- Luego, llama a una segunda mutación
_insertpara crear el registroMovieMetadata.
Sin embargo, con SQL Connect, puedes controlar este caso común en una sola
operación de varios pasos si accedes a los resultados de la primera _insert en la
segunda _insert.
Crear una app de opinión sobre películas exitosa requiere mucho trabajo. Hagamos un seguimiento de nuestra lista de tareas pendientes con un ejemplo nuevo.
Usa response para establecer campos con valores del servidor
En la siguiente mutación de lista de tareas pendientes:
- La vinculación
responserepresenta el objeto de respuesta parcial hasta el momento, que incluye todos los campos de mutación de nivel superior antes del actual. - Se accede más tarde a los resultados de la operación inicial
todoList_insert, que muestra elid(clave), enresponse.todoList_insert.idpara que podamos insertar de inmediato un nuevo elemento de tarea pendiente.
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,
})
}
Usa response para validar campos con @check
response también está disponible en @check(expr: "..."), por lo que puedes usarlo para
compilar una lógica del servidor aún más complicada. En combinación con los pasos query { … } en las mutaciones, puedes lograr mucho más sin ningún viaje de ida y vuelta adicional entre el cliente y el servidor.
En el siguiente ejemplo, ten en cuenta que @check ya tiene acceso a response.query porque un @check siempre se ejecuta después del paso al que está conectado.
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,
})
}
Para obtener más información sobre la vinculación response, consulta la
referencia de CEL.
Comprende las operaciones interrumpidas con @transaction y query @check
Las mutaciones de varios pasos pueden generar errores:
- Es posible que fallen las operaciones de la base de datos.
- Es posible que la lógica de query
@checkfinalice las operaciones.
SQL Connect recomienda que uses la directiva @transaction con
tus mutaciones de varios pasos. Esto da como resultado una base de datos más coherente y resultados de mutación que son más fáciles de controlar en el código de cliente:
- En el primer error o
@checkfallido, la operación finalizará, por lo que no es necesario administrar la ejecución de ningún campo posterior ni la evaluación de CEL. - Las reversiones se realizan en respuesta a errores de la base de datos o a la lógica de
@check, lo que genera un estado coherente de la base de datos. - Siempre se muestra un error de reversión al código de cliente.
Es posible que haya algunos casos de uso en los que decidas no usar @transaction: puedes optar por la coherencia eventual si, por ejemplo, necesitas una mayor capacidad de procesamiento, escalabilidad o disponibilidad. Sin embargo, debes administrar tu base de datos y tu código de cliente para permitir los resultados:
- Si un campo falla debido a las operaciones de la base de datos, los campos posteriores seguirán ejecutándose. Sin embargo, los
@checkfallidos aún finalizan toda la operación. - No se realizan reversiones, lo que significa un estado de base de datos mixto con algunas actualizaciones exitosas y otras fallidas.
- Tus operaciones con
@checkpueden dar resultados más incoherentes si tu lógica@checkusa los resultados de lecturas o escrituras en un paso anterior. - El resultado que se muestra al código de cliente contendrá una combinación más compleja de respuestas de éxito y falla que se deben controlar.
Directivas para mutaciones SQL Connect
Además de las directivas que usas para definir tipos y tablas,
SQL Connect proporciona las directivas @auth, @check, @redact y
@transaction para aumentar el comportamiento de las operaciones.
| Directiva | Se aplica a | Descripción |
|---|---|---|
@auth |
Consultas y mutaciones | Define la política de autorización para una consulta o mutación. Consulta la guía de autorización y certificación. |
@check |
Campos query en operaciones de varios pasos |
Verifica que los campos especificados estén presentes en los resultados de la consulta. Se usa una expresión de Common Expression Language (CEL) para probar los valores de los campos. Consulta Operaciones de varios pasos. |
@redact |
Consultas | Redacta una parte de la respuesta del cliente. Consulta Operaciones de varios pasos. |
@transaction |
Mutaciones | Exige que una mutación siempre se ejecute en una transacción de base de datos. Consulta Operaciones de varios pasos. |
Próximos pasos
Tal vez te interese lo siguiente:
- Generar mutaciones para tus apps con herramientas de asistencia de IA
- Autorizar tus mutaciones según la guía de autorización
- Llamar a mutaciones desde tu código de cliente para la Web, iOS, Android y Flutter.
- Realizar operaciones de datos masivas con mutaciones