Files
ent/doc/md/tutorial-todo-gql-tx-mutation.md
Dan Kortschak e02622a064 docs: various fixes to the graphql tutorial (#2965)
* doc/md: fix resolver diff and isolation example

* doc/md: fix updateTodo input field

* doc/md: add missing extension and fix paths

Without entgql.WithSchemaGenerator entc.go fails to run.

* doc/md: add file titles

Note that the where:TodoWhereInput parameter is pre-filled in the
generated ent.graphql file.

* doc/md: add context to conjunction/negation example

Only add for the first example to provide a lead to readers.
2022-09-27 10:02:16 +03:00

2.8 KiB
Executable File

id, title, sidebar_label
id title sidebar_label
tutorial-todo-gql-tx-mutation Transactional Mutations Transactional Mutations

In this section, we continue the GraphQL example by explaining how to set our GraphQL mutations to be transactional. That means, to automatically wrap our GraphQL mutations with a database transaction and either commit at the end, or rollback the transaction in case of a GraphQL error.

Clone the code (optional)

The code for this tutorial is available under github.com/a8m/ent-graphql-example, and tagged (using Git) in each step. If you want to skip the basic setup and start with the initial version of the GraphQL server, you can clone the repository and checkout v0.1.0 as follows:

git clone git@github.com:a8m/ent-graphql-example.git
cd ent-graphql-example 
go run ./cmd/todo/

Usage

The GraphQL extensions provides a handler named entgql.Transactioner that executes each GraphQL mutation in a transaction. The injected client for the resolver is a transactional ent.Client. Hence, GraphQL resolvers that uses ent.Client won't need to be changed. In order to add it to our todo list application we follow these steps:

1. Edit the cmd/todo/main.go and add to the GraphQL server initialization the entgql.Transactioner handler as follows:

srv := handler.NewDefaultServer(todo.NewSchema(client))
+srv.Use(entgql.Transactioner{TxOpener: client})

2. Then, in the GraphQL mutations, use the client from context as follows:

}
+func (mutationResolver) CreateTodo(ctx context.Context, input ent.CreateTodoInput) (*ent.Todo, error) {
+	client := ent.FromContext(ctx)
+	return client.Todo.Create().SetInput(input).Save(ctx)
-func (r *mutationResolver) CreateTodo(ctx context.Context, input ent.CreateTodoInput) (*ent.Todo, error) {
-	return r.client.Todo.Create().SetInput(input).Save(ctx)
}

Isolation Levels

If you'd like to tweak the transaction's isolation level, you can do so by implementing your own TxOpener. For example:

srv.Use(entgql.Transactioner{
	TxOpener: entgql.TxOpenerFunc(func(ctx context.Context) (context.Context, driver.Tx, error) {
		tx, err := client.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelRepeatableRead})
		if err != nil {
			return nil, nil, err
		}
		ctx = ent.NewTxContext(ctx, tx)
		ctx = ent.NewContext(ctx, tx.Client())
		return ctx, tx, nil
	}),
})

Great! With a few lines of code, our application now supports automatic transactional mutations. Please continue to the next section where we explain how to extend the Ent code generator and generate GraphQL input types for our GraphQL mutations.