Firebase SQL Connect permite criar conectores para instâncias do PostgreSQL gerenciadas com o Google Cloud SQL. Esses conectores são combinações de consultas e mutações para usar os dados do seu esquema.
O guia de introdução apresentou um esquema de app de resenha de filmes para PostgreSQL.
Esse guia também apresentou operações administrativas implantáveis e ad hoc, incluindo mutações.
- Mutações implantáveis são aquelas que você implementa para chamar de apps cliente em um conector, com endpoints de API definidos. SQL Connect integra autenticação e autorização a essas mutações e gera SDKs do cliente com base na sua API.
- Mutações administrativas ad hoc são executadas em ambientes privilegiados para preencher e gerenciar tabelas. É possível criar e executar essas mutações no Firebase console, em ambientes privilegiados usando o Firebase Admin SDK, e em ambientes de desenvolvimento local usando nossa extensão do SQL Connect para VS Code.
Este guia aborda as mutações implantáveis.
Recursos de SQL Connect mutações
SQL Connect permite realizar mutações básicas de todas as maneiras esperadas em um banco de dados PostgreSQL:
- Realizar operações CRUD
- Gerenciar operações de várias etapas com transações
No entanto, com as extensões do SQL Connect para GraphQL, é possível implementar mutações avançadas para apps mais rápidos e eficientes:
- Use escalares de chave retornados por muitas operações para simplificar operações repetidas em registros
- Use valores do servidor para preencher dados com operações fornecidas pelo servidor
- Realize consultas no curso de operações de mutação de várias etapas para pesquisar dados, salvando linhas de código e viagens de ida e volta ao servidor.
Usar campos gerados para implementar mutações
As operações do SQL Connect vão estender um conjunto de campos gerados automaticamente pelo SQL Connect com base nos tipos e nas relações de tipo no seu esquema. Esses campos são gerados por ferramentas locais sempre que você edita o esquema.
É possível usar campos gerados para implementar mutações, desde a criação, atualização e exclusão de registros individuais em tabelas únicas até atualizações mais complexas de várias tabelas.Considere que o esquema contenha um tipo Movie e um tipo Actor associado.
SQL Connect gera movie_insert,
movie_update, campos movie_delete e muito mais.
Mutação com o
movie_insert campo
|
O campo |
Use esse campo para criar um único filme. mutation CreateMovie($data: Movie_Data!) { movie_insert(data: $data) { key } } |
Mutação com o
movie_update campo
|
O campo |
Use esse campo para atualizar um único filme pela chave. mutation UpdateMovie($myKey: Movie_Key!, $data: Movie_Data!) { movie_update(key: $myKey, data: $data) { key } } |
Mutação com o
movie_delete campo
|
O campo |
Use esse campo para excluir um único filme pela chave. mutation DeleteMovie($myKey: Movie_Key!) { movie_delete(key: $myKey) { key } } |
Elementos essenciais de uma mutação
SQL Connect mutações são mutações GraphQL com SQL Connect extensões. Assim como em uma mutação GraphQL normal, é possível definir um nome de operação e uma lista de variáveis GraphQL.
SQL Connect estende as consultas GraphQL com diretivas personalizadas, como
@auth e @transaction.
Portanto, a mutação a seguir tem:
- Uma definição de tipo
mutation - Um nome de operação (mutação)
SignUp - Um único argumento de operação
$username - Uma única diretiva,
@auth - Um único campo
user_insert.
mutation SignUp($username: String!) @auth(level: USER) {
user_insert(data: {
id_expr: "auth.uid"
username: $username
})
}
Cada argumento de mutação requer uma declaração de tipo, um tipo integrado como String ou um tipo personalizado definido pelo esquema, como Movie.
Escrever mutações básicas
É possível começar a escrever mutações para criar, atualizar e excluir registros individuais do seu banco de dados.
Criar
Vamos fazer criações 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
})
}
Ou uma operação 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"
})
}
Realizar atualizações
Confira as atualizações. Produtores e diretores certamente esperam que essas classificações médias estejam na moda.
O campo movie_update contém um argumento id esperado para identificar um registro e um campo data que pode ser usado para definir valores nessa atualização.
mutation UpdateMovie(
$id: UUID!,
$genre: String!,
$rating: Int!,
$description: String!
) {
movie_update(id: $id,
data: {
genre: $genre
rating: $rating
description: $description
})
}
Para realizar várias atualizações, use o 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
})
}
Usar operações de incremento, decremento, anexação e prefixação com _update
Embora nas mutações _update e _updateMany seja possível definir valores explicitamente em
data:, muitas vezes é mais sensato aplicar um operador como incremento para atualizar
valores.
Para modificar o exemplo de atualização anterior, considere que você quer incrementar a classificação de um filme específico. É possível usar a sintaxe rating_update com o operador inc.
mutation UpdateMovie(
$id: UUID!,
$ratingIncrement: Int!
) {
movie_update(id: $id, data: {
rating_update: {
inc: $ratingIncrement
}
})
}
SQL Connect oferece suporte aos seguintes operadores para atualizações de campo:
incpara incrementar tipos de dadosInt,Int64,Float,DateeTimestampdecpara decrementar tipos de dadosInt,Int64,Float,DateeTimestamp
Para listas, também é possível atualizar com valores individuais ou listas de valores usando:
addpara anexar itens, se eles ainda não estiverem presentes em tipos de lista, exceto listas de vetoresremovepara remover todos os itens, se presentes, de tipos de lista, exceto listas de vetoresappendpara anexar itens a tipos de lista, exceto listas de vetoresprependpara prefixar itens a tipos de lista, exceto listas de vetores
Realizar exclusões
É claro que é possível excluir dados de filmes. Os preservacionistas de filmes certamente querem que os filmes físicos sejam mantidos pelo maior tempo possível.
# Delete by key
mutation DeleteMovie($id: UUID!) {
movie_delete(id: $id)
}
Aqui, é possível usar _deleteMany.
# Multiple deletes
mutation DeleteUnpopularMovies($minRating: Int!) {
movie_deleteMany(where: { rating: { le: $minRating } })
}
Escrever mutações em relações
Observe como usar a mutação _upsert implícita em uma relação.
# Create or update a one to one relation
mutation MovieMetadataUpsert($movieId: UUID!, $director: String!) {
movieMetadata_upsert(
data: { movie: { id: $movieId }, director: $director }
)
}
Projetar esquemas para mutações eficientes
SQL Connect oferece dois recursos importantes que permitem escrever mutações mais eficientes e salvar operações de ida e volta.
Escalares de chave são identificadores de objetos concisos que o SQL Connect monta automaticamente a partir de campos de chave nos seus esquemas. Os escalares de chave são sobre eficiência, permitindo encontrar em uma única chamada informações sobre a identidade e a estrutura dos seus dados. Eles são especialmente úteis quando você quer realizar ações sequenciais em novos registros e precisa de um identificador exclusivo para transmitir às próximas operações, e também quando você quer acessar chaves relacionais para realizar outras operações mais complexas.
Usando valores do servidor, é possível permitir que o servidor preencha dinamicamente
campos nas suas tabelas usando valores armazenados ou facilmente calculáveis de acordo com
expressões CEL específicas do lado do servidor no argumento expr. Por exemplo, é possível definir um campo com um carimbo de data/hora aplicado quando o campo é acessado usando
o horário armazenado em uma solicitação de operação, updatedAt: Timestamp!
@default(expr: "request.time").
Escrever mutações avançadas: permitir que SQL Connect forneça valores usando a sintaxe field_expr
Conforme discutido em escalares de chave e valores do servidor,
é possível projetar o esquema para que o servidor preencha valores para campos comuns,
como ids e datas, em resposta a solicitações do cliente.
Além disso, é possível usar dados, como IDs de usuário, enviados em
SQL Connect request objetos de apps cliente.
Ao implementar mutações, use a sintaxe field_expr para acionar atualizações geradas pelo servidor ou acessar dados de solicitações. Por exemplo, para transmitir
a autorização uid armazenada em uma solicitação para uma operação _upsert, transmita
"auth.uid" no 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 })
}
Ou, em um app de lista de tarefas familiar, ao criar uma nova lista de tarefas, é possível transmitir id_expr para instruir o servidor a gerar automaticamente um UUID para a 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 mais informações, consulte os _Expr escalares na
referência de escalares.
Escrever mutações avançadas: operações de várias etapas
Há muitas situações em que você pode querer incluir vários campos de gravação (como inserções) em uma mutação. Também é possível ler o banco de dados durante a execução de uma mutação para pesquisar e verificar os dados existentes antes de realizar, por exemplo, inserções ou atualizações. Essas opções salvam operações de ida e volta e, portanto, custos.
SQL Connect permite realizar lógica de várias etapas nas mutações, oferecendo suporte a:
Vários campos de gravação
Vários campos de leitura nas mutações (usando a palavra-chave do campo
query).A diretiva
@transaction, que oferece suporte a transações familiares de bancos de dados relacionais.A diretiva
@check, que permite avaliar o conteúdo das leituras usando expressões CEL e, com base nos resultados dessa avaliação:- Prossiga com criações, atualizações e exclusões definidas por uma mutação
- Prossiga para retornar os resultados de um campo de consulta
- Use mensagens retornadas para realizar a lógica apropriada no código do cliente
A diretiva
@redact, que permite omitir resultados de campos de consulta dos resultados do protocolo de rede.A vinculação
responsedo CEL, que armazena os resultados acumulados de todas as mutações e consultas realizadas em uma operação complexa de várias etapas. É possível acessar a vinculaçãoresponse:- Em diretivas
@check, pelo argumentoexpr: - Com valores do servidor, usando a sintaxe
field_expr
- Em diretivas
A diretiva @transaction
O suporte a mutações de várias etapas inclui tratamento de erros usando transações.
A diretiva @transaction impõe que uma mutação, com um único campo de gravação (por exemplo, _insert ou _update) ou com vários campos de gravação, sempre seja executada em uma transação de banco de dados.
Mutações sem
@transactionexecutam cada campo raiz um após o outro em sequência. A operação mostra erros como erros de campo parciais, mas não os impactos das execuções subsequentes.As mutações com
@transactiontêm garantia de sucesso ou falha total. Se algum dos campos na transação falhar, toda a transação será revertida.
As diretivas @check e @redact
A diretiva @check verifica se os campos especificados estão presentes nos resultados da consulta. Uma expressão Common Expression Language (CEL) é usada para testar valores de campo. O comportamento padrão da diretiva é verificar e rejeitar nós cujo valor seja null ou [] (listas vazias).
A diretiva @redact edita uma parte da resposta do cliente. Os campos editados ainda são avaliados quanto a efeitos colaterais (incluindo mudanças de dados e @check) e os resultados ainda estão disponíveis para etapas posteriores em expressões CEL.
Usar @check, @check(message:) e @redact
Um dos principais usos de @check e @redact é pesquisar dados relacionados para decidir se determinadas operações devem ser autorizadas, usando a pesquisa na lógica, mas ocultando-a dos clientes. A consulta pode retornar mensagens úteis para o tratamento correto no código do 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 saber mais sobre as diretivas @check e @redact em verificações de autorização,
consulte a discussão sobre a pesquisa de dados de autorização.
Usar @check para validar chaves
Alguns campos de mutação, como _update, podem não funcionar se um registro com uma chave especificada não existir. Da mesma forma, as pesquisas podem retornar nulo ou uma lista vazia. Esses não são considerados erros e, portanto, não acionam rollbacks.
Para evitar esse resultado, teste se as chaves podem ser encontradas usando a diretiva @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")
}
Usar a vinculação response para encadear mutações de várias etapas
A abordagem básica para criar registros relacionados, por exemplo, um novo Movie e
uma entrada MovieMetadata associada, é:
- Chamar uma mutação
_insertparaMovie - Armazenar a chave retornada do filme criado
- Em seguida, chame uma segunda mutação
_insertpara criar o registroMovieMetadata.
No entanto, com SQL Connect, é possível processar esse caso comum em uma única
operação de várias etapas acessando os resultados da primeira _insert na
segunda _insert.
Criar um app de resenha de filmes bem-sucedido é muito trabalho. Vamos acompanhar nossa lista de tarefas com um novo exemplo.
Usar response para definir campos com valores do servidor
Na mutação da lista de tarefas a seguir:
- A vinculação
responserepresenta o objeto de resposta parcial até o momento, que inclui todos os campos de mutação de nível superior antes do atual. - Os resultados da operação inicial
todoList_insert, que retorna oid(chave), são acessados mais tarde emresponse.todoList_insert.idpara que possamos inserir imediatamente um novo item de tarefa.
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,
})
}
Usar response para validar campos usando @check
response também está disponível em @check(expr: "..."), para que seja possível usá-lo para
criar uma lógica do lado do servidor ainda mais complicada. Combinado com etapas query { … } em mutações, é possível conseguir muito mais sem viagens de ida e volta adicionais do cliente-servidor.
No exemplo a seguir, observe que @check já tem acesso a response.query porque um @check sempre é executado após a etapa a que está anexado.
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 mais informações sobre a vinculação response, consulte a
referência de CEL.
Entender operações interrompidas com @transaction e query @check
Mutações de várias etapas podem encontrar erros:
- As operações de banco de dados podem falhar.
- A lógica de
@checkde consulta pode encerrar operações.
SQL Connect recomenda que você use a diretiva @transaction com
suas mutações de várias etapas. Isso resulta em um banco de dados mais consistente e resultados de mutação mais fáceis de processar no código do cliente:
- No primeiro erro ou
@checkcom falha, a operação será encerrada. Portanto, não é necessário gerenciar a execução de campos subsequentes ou a avaliação de CEL. - Os rollbacks são realizados em resposta a erros de banco de dados ou lógica
@check, gerando um estado de banco de dados consistente. - Um erro de rollback sempre é retornado ao código do cliente.
Pode haver alguns casos de uso em que você opta por não usar @transaction: é possível optar pela consistência posterior se, por exemplo, precisar de maior capacidade de processamento, escalonabilidade ou disponibilidade. No entanto, é necessário gerenciar o banco de dados e o código do cliente para permitir os resultados:
- Se um campo falhar devido a operações de banco de dados, os campos subsequentes continuarão sendo executados. No entanto,
@checks com falha ainda encerram toda a operação. - Os rollbacks não são realizados, o que significa um estado de banco de dados misto com algumas atualizações bem-sucedidas e algumas com falha.
- Suas operações com
@checkpodem gerar resultados mais inconsistentes se a lógica@checkusar os resultados de leituras e/ou gravações em uma etapa anterior. - O resultado retornado ao código do cliente vai conter uma combinação mais complexa de respostas de sucesso e falha a serem processadas.
Diretivas para SQL Connect mutações
Além das diretivas usadas na definição de tipos e tabelas,
SQL Connect oferece as @auth, @check, @redact e
@transaction diretivas para aumentar o comportamento das operações.
| Diretiva | Aplicável a | Descrição |
|---|---|---|
@auth |
Consultas e mutações | Define a política de autorização para uma consulta ou mutação. Consulte o guia de autorização e atestado. |
@check |
Campos query em operações de várias etapas |
Verifica se os campos especificados estão presentes nos resultados da consulta. Uma expressão Common Expression Language (CEL) é usada para testar valores de campo. Consulte Operações de várias etapas. |
@redact |
Consultas | Edita uma parte da resposta do cliente. Consulte Operações de várias etapas. |
@transaction |
Mutações | Impõe que uma mutação sempre seja executada em uma transação de banco de dados. Consulte Operações de várias etapas. |
Próximas etapas
Talvez você se interesse por:
- Gerar mutações para seus apps usando ferramentas de assistência de IA
- Autorizar suas mutações de acordo com o guia de autorização
- Chamar mutações do código do cliente para Web, iOS, Android e Flutter.
- Realizar operações de dados em massa com mutações