<br />

Firebase Data Connectlets you create connectors for your PostgreSQL instances managed with Google Cloud SQL. These connectors are combinations of a queries and mutations for using your data from your schema.
| **Note:**Follow the complete series on building Data Connect schemas and connectors, which covers:
|
| - [Schema development](https://firebase.google.com/docs/data-connect/schemas-guide)
| - [Query development](https://firebase.google.com/docs/data-connect/queries-guide)
| - Mutation development (this guide)

The[Get started guide](https://firebase.google.com/docs/data-connect/quickstart)introduced a movie review app schema for PostgreSQL.

That guide also introduced both deployable and ad hoc administrative operations, including mutations.

- **Deployable mutations** are those you implement to call from client apps in a connector, with API endpoints you define.Data Connectintegrates authentication and authorization into these mutations, and generates client SDKs based on your API.
- **Ad hoc administrative mutations** are run from privileged environments to populate and manage tables. You can create and execute them in theFirebaseconsole, from privileged environments using theFirebaseAdmin SDK, and in local development environments using our Data Connect VS Code extension.

This guide takes a deeper look at**deployable mutations**.

## Features ofData Connectmutations

Data Connectlets you to perform basic mutations in the all the ways you'd expect given a PostgreSQL database:

- Perform CRUD operations
- Manage multi-step operations with transactions

But withData Connect's extensions to GraphQL, you can implement advanced mutations for faster, more efficient apps:

- Use**key scalars**returned by many operations to simplify repeated operations on records
- Use**server values**to populate data with operations provided by the server
- Perform queries in the course of a multi-step mutation operations to look up data, saving lines of code and round trips to the server.

| **Note:** Before using this guide, review the movie review schema.  
|
| #### Movie review app schema
|
| ```graphql
| # Movies
| type Movie
|   @table {
|   id: UUID! @default(expr: "uuidV4()")
|   title: String!
|   releaseYear: Int
|   genre: String
|   rating: Int
|   description: String
|   tags: [String]
| }
|
| # Movie Metadata
| # Movie - MovieMetadata is a one-to-one relationship
| type MovieMetadata
|   @table {
|   movie: Movie! @ref
|   director: String
| }
|
| // ... existing code ...
| extend type Movie {
|   movieMetadata: MovieMetadata
|   movieMetadatas_on_movie: MovieMetadata
| }
|
| # Actors
| # Suppose an actor can participate in multiple movies and movies can have multiple actors
| # Movie - Actors (or vice versa) is a many to many relationship
| type Actor @table {
|   id: UUID! @default(expr: "uuidV4()")
|   name: String!
| }
|
| # Join table for many-to-many relationship for movies and actors
| # The 'key' param signifies the primary keys of this table
| # In this case, the keys are [movieId, actorId], the foreign key fields of the reference fields [movie, actor]
| type MovieActor @table {
|   movie: Movie!
|   actor: Actor!
|   role: String! # "main" or "supporting"
|   # optional other fields
| }
|
| type User @table {
|   # `@default(expr: "auth.uid")` sets it to Firebase Auth UID during insert and upsert.
|   id: String! @default(expr: "auth.uid")
|   username: String!
| }
|
| # Reviews is a join table tween User and Movie.
| # It has a composite primary keys `userUid` and `movieId`.
| # A user can leave reviews for many movies. A movie can have reviews from many users.
| # User  <-> Review is a one-to-many relationship
| # Movie <-> Review is a one-to-many relationship
| # Movie <-> User is a many-to-many relationship
| type Review @table {
|   user: User!
|   movie: Movie!
|   rating: Int
|   reviewText: String
|   reviewDate: Date! @default(expr: "request.time")
|     
| ```

## Use generated fields to implement mutations

YourData Connectoperations will extend a set of fields automatically generatedData Connectbased on the types and type relationships in your schema. These fields are generated by local tooling whenever you edit your schema.
| **Tip:** To discover the generated fields while building operations, use the query editor in theFirebaseconsole, or our Visual Studio Code extension.
You can use generated fields to implement mutations, from creating, updating, and deleting individual records in single tables, to more complex multi-table updates.

Assume your schema contains a`Movie`type and an associated`Actor`type.Data Connectgenerates`movie_insert`,`movie_update`,`movie_delete`fields, and more.
**Note:** These mutation fields return and let you pass a*key scalar* type, here a`Movie_Key`, to identify records. These are created based on your[schema](https://firebase.google.com/docs/data-connect/mutations-guide#key_scalars_and_server_values).  

### Mutation with the
`movie_insert`field

|--------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|
| The`movie_insert`field represents a mutation to create a single record in the`Movie`table. | Use this field to create a single movie. ```graphql mutation CreateMovie($data: Movie_Data!) { movie_insert(data: $data) { key } } ``` |

### Mutation with the
`movie_update`field

|--------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| The`movie_update`field represents a mutation to update a single record in the`Movie`table. | Use this field to update a single movie by its key. ```graphql mutation UpdateMovie($myKey: Movie_Key!, $data: Movie_Data!) { movie_update(key: $myKey, data: $data) { key } } ``` |

### Mutation with the
`movie_delete`field

|--------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------|
| The`movie_delete`field represents a mutation to delete a single record in the`Movie`table. | Use this field to delete a single movie by its key. ```graphql mutation DeleteMovie($myKey: Movie_Key!) { movie_delete(key: $myKey) { key } } ``` |

| **Tip:** The schema definition language also lets you explicitly control how names are generated for fields using[`singular`and`plural`arguments for the`@table`directive](https://firebase.google.com/docs/reference/data-connect/gql/directive#customizations_2).

## Essential elements of a mutation

Data Connect mutations are GraphQL mutations withData Connectextensions. Just as with a regular GraphQL mutation, you can define an operation name and a list of GraphQL variables.

Data Connect extends GraphQL queries with customized**directives** like`@auth`and`@transaction`.
| **Tip:** You can use AI assistance to help you create and test GraphQL mutations in theFirebaseconsole and in our local tooling. Learn more at[Use AI assistance for scheams, queries and mutations](https://firebase.google.com/docs/data-connect/ai-assistance).

So the following mutation has:

- A`mutation`type definition
- A`SignUp`operation (mutation) name
- A single variable`$username`operation argument
- A single directive,`@auth`
- A single field`user_insert`.

    mutation SignUp($username: String!) @auth(level: USER) {
      user_insert(data: {
        id_expr: "auth.uid"
        username: $username
      })
    }

Every mutation argument requires a type declaration, a built-in like`String`, or a custom, schema-defined type like`Movie`.
| **Note:** The example shows how to sign up an account as yourself. Anyone that logs in can create a user account for themselves.

## Write basic mutations

You can start writing mutations to create, update and delete individual records from your database.
| **Tip:** You can use Gemini inFirebaseto help you create and run GraphQL mutations in theFirebaseconsole. Learn more at[Use AI assistance for queries and mutations](https://firebase.google.com/docs/data-connect/ai-assistance).

### Create

Let's do basic creates.  

    # 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
      })
    }

Or an 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"
      })
    }

### Perform updates

Here are updates. Producers and directors certainly hope that those average ratings are on trend.

The`movie_update`field contains an expected`id`argument to identify a record and a`data`field you can use to set values in this update.  

    mutation UpdateMovie(
      $id: UUID!,
      $genre: String!,
      $rating: Int!,
      $description: String!
    ) {
      movie_update(id: $id,
        data: {
          genre: $genre
          rating: $rating
          description: $description
        })
    }

To perform multiple updates, use the`movie_updateMany`field.  

    # Multiple updates (increase all ratings of a genre)
    mutation IncreaseRatingForGenre($genre: String!, $rating: Int!) {
      movie_updateMany(
        where: { genre: { eq: $genre } },
        data:
          {
            rating: $rating
          })
    }

#### Use increment, decrement, append, and prepend operations with`_update`

While in`_update`and`_updateMany`mutations you can explicitly set values in`data:`, it often makes more sense to apply an operator like increment to update values.

To modify the earlier update example, assume you want to increment the rating of a particular movie. You can use`rating_update`syntax with the`inc`operator.  

    mutation UpdateMovie(
      $id: UUID!,
      $ratingIncrement: Int!
    ) {
      movie_update(id: $id, data: {
        rating_update: {
          inc: $ratingIncrement
        }
      })
    }

Data Connectsupports the following operators for field updates:

- `inc`to increment`Int`,`Int64`,`Float`,`Date`and`Timestamp`data types
- `dec`to decrement`Int`,`Int64`,`Float`,`Date`and`Timestamp`data types

| **Note:** For these operations, if an existing numeric is`null`, then it is treated as`0`. If an existing`Date`or`Timestamp`is`null`, then it is treated as the current date or time.

For lists, you can also update with individual values or lists of values using:

- `add`to append item(s) if they are not already present to list types, except Vector lists
- `remove`to remove all items, if present, from list types, except Vector lists
- `append`to append item(s) to list types, except Vector lists
- `prepend`to prepend item(s) to list types, except Vector lists

| **Note:** For these operations, if the existing list field is`null`, it is treated as an empty list.
| **Tip:** For details of the interface, see the[`_Update`and`_ListUpdate`reference documentation entries](https://firebase.google.com/docs/reference/data-connect/gql/input_object).

### Perform deletes

You can of course delete movie data. Film preservationists will certainly want the physical films to be maintained for as long as possible.  

    # Delete by key
    mutation DeleteMovie($id: UUID!) {
      movie_delete(id: $id)
    }

Here you can use`_deleteMany`.  

    # Multiple deletes
    mutation DeleteUnpopularMovies($minRating: Int!) {
      movie_deleteMany(where: { rating: { le: $minRating } })
    }

### Write mutations on relations

Observe how to use the implicit`_upsert`mutation on a relation.  

    # Create or update a one to one relation
    mutation MovieMetadataUpsert($movieId: UUID!, $director: String!) {
      movieMetadata_upsert(
        data: { movie: { id: $movieId }, director: $director }
      )
    }

| **Tip:** The complete set of GraphQL language extensions forData Connectis[documented in the language reference guide](https://firebase.google.com/docs/reference/data-connect).

## Design schemas for efficient mutations

Data Connectprovides two important features that allow you to write more efficient mutations and save round-trip operations.

**Key scalars** are concise object identifiers thatData Connectautomatically assembles from key fields in your schemas. Key scalars are about efficiency, allowing you to find in a single call information about the identity and structure of your data. They are especially useful when you want to perform sequential actions on new records and need a unique identifier to pass to upcoming operations, and also when you want to access relational keys to perform additional more complex operations.

Using**server values** , you can effectively let the server dynamically populate fields in your tables using stored or readily-computable values according to particular server-side CEL expressions in the`expr`argument. For example, you can define a field with a timestamp applied when the field is accessed using the time stored in an operation request,`updatedAt: Timestamp!
@default(expr: "request.time")`.

## Write advanced mutations: letData Connectsupply values using`field_expr`syntax

As discussed in[key scalars and server values](https://firebase.google.com/docs/data-connect/mutations-guide#key_scalars_and_server_values), you can design your schema so that the server populates values for common fields like`id`s and dates in response to client requests.

In addition, you can make use of data, such as user IDs, sent inData Connect`request`objects from client apps.

When you implement mutations, use`field_expr`syntax to trigger server-generated updates or access data from requests. For example, to pass the authorization`uid`stored in a request to an`_upsert`operation, pass`"auth.uid"`in the`userId_expr`field.  

    # 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 })
    }

Or, in a familiar to-do list app, when creating a new to-do list, you could pass`id_expr`to instruct the server to auto-generate a UUID for the list.  

    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,
      })
    }

For more information, see the`_Expr`scalars in the[scalars reference](https://firebase.google.com/docs/reference/data-connect/gql/scalar).

## Write advanced mutations: multi-step operations

There are many situations in which you might want to include multiple write fields (like inserts) in one mutation. You might also want to read your database during execution of a mutation to lookup and verify existing data before performing, for example, inserts or updates. These options save round trip operations and hence costs.

Data Connectlets you perform multi-step logic in your mutations by supporting:

- Multiple write fields

- Multiple read fields in your mutations (using the`query`field keyword).

- The[`@transaction`directive](https://firebase.google.com/docs/data-connect/mutations-guide#query-mutation-directives), which provides transaction support familiar from relational databases.

- The[`@check`directive](https://firebase.google.com/docs/data-connect/mutations-guide#query-mutation-directives), which lets you evaluate the contents of reads using CEL expressions, and based on the results of such evaluation:

  - Proceed with creates, updates and deletes defined by a mutation
  - Proceed to return the results of a query field
  - Use returned messages to perform appropriate logic in your client code
- The[`@redact`directive](https://firebase.google.com/docs/data-connect/mutations-guide#query-mutation-directives), which lets you omit query field results from wire protocol results.

- The CEL`response`binding, which stores the accumulated results of all mutations and queries performed in a complex, multi-step operation. You can access the`response`binding:

  - In`@check`directives, through the`expr:`argument
  - With server values, using`field_expr`syntax

### The`@transaction`directive

Support for multi-step mutations includes error handling using transactions.

The`@transaction`directive enforces that a mutation - with either a single write field (for example,`_insert`or`_update`) or with multiple write fields - always run in a database transaction.

- Mutations without`@transaction`execute each root field one after another in sequence. The operation surfaces any errors as partial field errors, but not the impacts of the subsequent executions.

- Mutations with`@transaction`are guaranteed to either fully succeed or fully fail. If any of the fields within the transaction fails, the entire transaction is rolled back.

### The`@check`and`@redact`directives

The`@check`directive verifies that specified fields are present in query results. A Common Expression Language (CEL) expression is used to test field values. The default behavior of the directive is to check for and reject nodes whose value is`null`or`[]`(empty lists).

The`@redact`directive redacts a part of the response from the client. Redacted fields are still evaluated for side effects (including data changes and`@check`) and the results are still available to later steps in CEL expressions.

#### Use`@check`,`@check(message:)`and`@redact`

A major use for`@check`and`@redact`is looking up related data to decide whether certain operations should be authorized, using the lookup in logic but hiding it from clients. Your query can return useful messages for correct handling in client code.

For illustration, the following query field checks whether a requestor has an appropriate "admin" role to view users who can edit a movie.  

    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
        }
      }
    }

To learn more about`@check`and`@redact`directives in authorization checks, refer to the[discussion of authorization data lookup](https://firebase.google.com/docs/data-connect/authorization-and-security#authz-data-lookup).

#### Use`@check`to validate keys

Some mutation fields, such as`_update`, may no-op if a record with a specified key does not exist. Similarly, lookups may return null or an empty list. These are not considered errors and therefore won't trigger rollbacks.

To guard against this result, test whether keys can be found using the`@check`directive.  

    # 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")
    }

| **Tip:** See the[directives reference for the`@check`and`@redact`directives](https://firebase.google.com/docs/reference/data-connect/gql/directive)and the[CEL expression reference](https://firebase.google.com/docs/data-connect/cel-reference).

### Use the`response`binding to chain multi-step mutations

The basic approach to creating related records, for example a new`Movie`and an associated`MovieMetadata`entry, is to:

1. Call an`_insert`mutation for`Movie`
2. Store the returned key of the created movie
3. Then, call a second`_insert`mutation to create the`MovieMetadata`record.

But withData Connect, you can handle this common case in a single multi-step operation by accessing the*results* of the first`_insert`in the second`_insert`.

Making a successful movie review app is a lot of work. Let's track our to-do list with a new example.

#### Use`response`to set fields with server values

In the following to-do list mutation:

- The`response`binding represents the partial response object so far, which includes all top-level mutation fields before the current one.
- The results of the initial`todoList_insert`operation, which returns the`id`(key) field, are accessed later in`response.todoList_insert.id`so we can immediately insert a new to-do item.

    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,
      })
    }

| **Note:** Results of sub-steps are available in the same`@transaction`, so you still get the atomicity guarantees as you'd expect from a relational database.

#### Use`response`to validate fields using`@check`

`response`is available in`@check(expr: "...")`as well, so you can use it to build even more complicated server-side logic. Combined with`query { ... }`steps in mutations, you can achieve a lot more without any additional client-server roundtrips.

In the following example, note: that`@check`already has access to`response.query`because a`@check`always runs after the step it is attached to.
**Tip:** You can hide results of intermediate steps from the wire and clients using`@redact`. Redacted fields are still available in`response`for use in later steps.  

    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,
      })
    }

For more information about the`response`binding, see the[CEL reference](https://firebase.google.com/docs/data-connect/cel-reference#the_response_binding).

### Understand interrupted operations with`@transaction`and`query @check`

Multi-step mutations can encounter errors:

- Database operations may fail.
- query`@check`logic may terminate operations.

Data Connectrecommends that you use the`@transaction`directive with your multi-step mutations. This results in a more consistent database and mutation results that are easier to handle in client code:

- At the first error or failed`@check`, the operation will terminate, so there is no need to manage execution of any subsequent fields or evaluation of CEL.
- Rollbacks are performed in response to database errors or`@check`logic, yielding a consistent database state.
- A rollback error is always returned to client code.

There may be some use cases where you choose not to use`@transaction`: you might opt for eventual consistency if, for example, you need higher throughput, scalability or availability. However, you need to manage your database and your client code to allow for the results:

- If one field fails due to database operations, subsequent fields will continue to execute. However, failed`@check`s still terminate the entire operation.
- Rollbacks are not performed, meaning a mixed database state with some successful updates and some failed updates.
- Your operations with`@check`may give more inconsistent results if your`@check`logic uses the results of reads and/or writes in a previous step.
- The result returned to client code will contain a more complex mix of success and failure responses to be handled.

## Directives forData Connectmutations

In addition to the directives you use in defining types and tables,Data Connectprovides the`@auth`,`@check`,`@redact`and`@transaction`directives for augmenting the behavior of operations.

|   Directive    |             Applicable to              |                                                                                                                        Description                                                                                                                        |
|----------------|----------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `@auth`        | Queries and mutations                  | Defines the authorization policy for a query or mutation. See the[authorization and attestation guide](https://firebase.google.com/docs/data-connect/authorization-and-security#auth-directive).                                                          |
| `@check`       | `query`fields in multi-step operations | Verifies that specified fields are present in query results. A Common Expression Language (CEL) expression is used to test field values. See[Multi-step operations](https://firebase.google.com/docs/data-connect/mutations-guide#multi-step-operations). |
| `@redact`      | Queries                                | Redacts a part of the response from the client. See[Multi-step operations](https://firebase.google.com/docs/data-connect/mutations-guide#multi-step-operations).                                                                                          |
| `@transaction` | Mutations                              | Enforces that a mutation always run in a database transaction. See[Multi-step operations](https://firebase.google.com/docs/data-connect/mutations-guide#multi-step-operations).                                                                           |

| **Tip:** Complete reference documentation for these directives is available in the[directives reference guide](https://firebase.google.com/docs/reference/data-connect/gql/directive).

## Next steps

| **Note:**This guide is part of a complete series on building Data Connect schemas and connectors, which covers:
|
| - [Schema development](https://firebase.google.com/docs/data-connect/schemas-guide).
| - [Query development](https://firebase.google.com/docs/data-connect/queries-guide).
| - Mutation development (this guide).

You may be interested in:

- Generating mutations for your apps using[AI assistance tools](https://firebase.google.com/docs/data-connect/ai-assistance)
- Authorizing your mutations per the[authorization guide](https://firebase.google.com/docs/data-connect/authorization-and-security)
- Calling mutations from your client code for[web](https://firebase.google.com/docs/data-connect/web-sdk),[iOS](https://firebase.google.com/docs/data-connect/ios-sdk),[Android](https://firebase.google.com/docs/data-connect/android-sdk)and[Flutter](https://firebase.google.com/docs/data-connect/flutter-sdk).
- Performing[bulk data operations with mutations](https://firebase.google.com/docs/data-connect/data-seeding-bulk-operations)