Saltar al contenido principal

Entradas de Mutación

[Traducción Beta No Oficial]

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

En esta sección, continuamos con el ejemplo de GraphQL explicando cómo extender el generador de código Ent usando plantillas Go y generar objetos de tipo input para nuestras mutaciones GraphQL que pueden aplicarse directamente en mutaciones Ent.

Clonar el código (opcional)

El código de este tutorial está disponible en github.com/a8m/ent-graphql-example, con etiquetas (usando Git) en cada paso. Si deseas saltar la configuración básica y comenzar con la versión inicial del servidor GraphQL, puedes clonar el repositorio y ejecutar el programa así:

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

Tipos de Mutación

Ent soporta generar tipos de mutación. Un tipo de mutación puede aceptarse como entrada para mutaciones GraphQL, siendo manejado y verificado por Ent. Indiquemos a Ent que nuestro tipo GraphQL Todo soporta operaciones de creación y actualización:

ent/schema/todo.go
func (Todo) Annotations() []schema.Annotation {
return []schema.Annotation{
entgql.QueryField(),
entgql.Mutations(entgql.MutationCreate(), entgql.MutationUpdate()),
}
}

Luego, ejecuta la generación de código:

go generate .

Observarás que Ent generó para ti 2 tipos: ent.CreateTodoInput y ent.UpdateTodoInput.

Mutaciones

Tras generar nuestras entradas de mutación, podemos conectarlas a las mutaciones GraphQL:

todo.graphql
type Mutation {
createTodo(input: CreateTodoInput!): Todo!
updateTodo(id: ID!, input: UpdateTodoInput!): Todo!
}

Al ejecutar la generación de código obtendremos las mutaciones reales, y lo único que quedará después será vincular los resolvers a Ent.

go generate .
todo.resolvers.go
// CreateTodo is the resolver for the createTodo field.
func (r *mutationResolver) CreateTodo(ctx context.Context, input ent.CreateTodoInput) (*ent.Todo, error) {
return r.client.Todo.Create().SetInput(input).Save(ctx)
}

// UpdateTodo is the resolver for the updateTodo field.
func (r *mutationResolver) UpdateTodo(ctx context.Context, id int, input ent.UpdateTodoInput) (*ent.Todo, error) {
return r.client.Todo.UpdateOneID(id).SetInput(input).Save(ctx)
}

Probar el Resolver CreateTodo

Comencemos creando 2 elementos todo ejecutando la mutación createTodo dos veces.

Mutación

mutation CreateTodo {
createTodo(input: {text: "Create GraphQL Example", status: IN_PROGRESS, priority: 2}) {
id
text
createdAt
priority
parent {
id
}
}
}

Salida

{
"data": {
"createTodo": {
"id": "1",
"text": "Create GraphQL Example",
"createdAt": "2021-04-19T10:49:52+03:00",
"priority": 2,
"parent": null
}
}
}

Mutación

mutation CreateTodo {
createTodo(input: {text: "Create Tracing Example", status: IN_PROGRESS, priority: 2}) {
id
text
createdAt
priority
parent {
id
}
}
}

Salida

{
"data": {
"createTodo": {
"id": "2",
"text": "Create Tracing Example",
"createdAt": "2021-04-19T10:50:01+03:00",
"priority": 2,
"parent": null
}
}
}

Probar el Resolver UpdateTodo

Lo único que queda es probar el resolver UpdateTodo. Usémoslo para actualizar el parent del segundo elemento todo a 1.

mutation UpdateTodo {
updateTodo(id: 2, input: {parentID: 1}) {
id
text
createdAt
priority
parent {
id
text
}
}
}

Salida

{
"data": {
"updateTodo": {
"id": "2",
"text": "Create Tracing Example",
"createdAt": "2021-04-19T10:50:01+03:00",
"priority": 1,
"parent": {
"id": "1",
"text": "Create GraphQL Example"
}
}
}
}

Crear aristas con mutaciones

Para crear las aristas de un nodo en la misma mutación, puedes extender la entrada de mutación GQL con campos de aristas:

extended.graphql
extend input CreateTodoInput {
createChildren: [CreateTodoInput!]
}

Luego, ejecuta nuevamente la generación de código:

go generate .

GQLGen generará el resolver para el campo createChildren, permitiéndote usarlo en tu resolver:

extended.resolvers.go
// CreateChildren is the resolver for the createChildren field.
func (r *createTodoInputResolver) CreateChildren(ctx context.Context, obj *ent.CreateTodoInput, data []*ent.CreateTodoInput) error {
panic(fmt.Errorf("not implemented: CreateChildren - createChildren"))
}

Ahora necesitamos implementar la lógica para crear los hijos:

extended.resolvers.go
// CreateChildren is the resolver for the createChildren field.
func (r *createTodoInputResolver) CreateChildren(ctx context.Context, obj *ent.CreateTodoInput, data []*ent.CreateTodoInput) error {
// NOTE: We need to use the Ent client from the context.
// To ensure we create all of the children in the same transaction.
// See: Transactional Mutations for more information.
c := ent.FromContext(ctx)
builders := make([]*ent.TodoCreate, len(data))
for i := range data {
builders[i] = c.Todo.Create().SetInput(*data[i])
}
todos, err := c.Todo.CreateBulk(builders...).Save(ctx)
if err != nil {
return err
}
ids := make([]int, len(todos))
for i := range todos {
ids[i] = todos[i].ID
}
obj.ChildIDs = append(obj.ChildIDs, ids...)
return nil
}

Cambia las siguientes líneas para usar el cliente transaccional:

todo.resolvers.go
// CreateTodo is the resolver for the createTodo field.
func (r *mutationResolver) CreateTodo(ctx context.Context, input ent.CreateTodoInput) (*ent.Todo, error) {
return ent.FromContext(ctx).Todo.Create().SetInput(input).Save(ctx)
}

// UpdateTodo is the resolver for the updateTodo field.
func (r *mutationResolver) UpdateTodo(ctx context.Context, id int, input ent.UpdateTodoInput) (*ent.Todo, error) {
return ent.FromContext(ctx).Todo.UpdateOneID(id).SetInput(input).Save(ctx)
}

Prueba la mutación con los hijos:

Mutación

mutation {
createTodo(input: {
text: "parent", status:IN_PROGRESS,
createChildren: [
{ text: "children1", status: IN_PROGRESS },
{ text: "children2", status: COMPLETED }
]
}) {
id
text
children {
id
text
status
}
}
}

Salida

{
"data": {
"createTodo": {
"id": "3",
"text": "parent",
"children": [
{
"id": "1",
"text": "children1",
"status": "IN_PROGRESS"
},
{
"id": "2",
"text": "children2",
"status": "COMPLETED"
}
]
}
}
}

Si habilitas el cliente debug, verás que los hijos se crean en la misma transacción:

2022/12/14 00:27:41 driver.Tx(7e04b00b-7941-41c5-9aee-41c8c2d85312): started
2022/12/14 00:27:41 Tx(7e04b00b-7941-41c5-9aee-41c8c2d85312).Query: query=INSERT INTO `todos` (`created_at`, `priority`, `status`, `text`) VALUES (?, ?, ?, ?), (?, ?, ?, ?) RETURNING `id` args=[2022-12-14 00:27:41.046344 +0700 +07 m=+5.283557793 0 IN_PROGRESS children1 2022-12-14 00:27:41.046345 +0700 +07 m=+5.283558626 0 COMPLETED children2]
2022/12/14 00:27:41 Tx(7e04b00b-7941-41c5-9aee-41c8c2d85312).Query: query=INSERT INTO `todos` (`text`, `created_at`, `status`, `priority`) VALUES (?, ?, ?, ?) RETURNING `id` args=[parent 2022-12-14 00:27:41.047455 +0700 +07 m=+5.284669251 IN_PROGRESS 0]
2022/12/14 00:27:41 Tx(7e04b00b-7941-41c5-9aee-41c8c2d85312).Exec: query=UPDATE `todos` SET `todo_parent` = ? WHERE `id` IN (?, ?) AND `todo_parent` IS NULL args=[3 1 2]
2022/12/14 00:27:41 Tx(7e04b00b-7941-41c5-9aee-41c8c2d85312).Query: query=SELECT DISTINCT `todos`.`id`, `todos`.`text`, `todos`.`created_at`, `todos`.`status`, `todos`.`priority` FROM `todos` WHERE `todo_parent` = ? args=[3]
2022/12/14 00:27:41 Tx(7e04b00b-7941-41c5-9aee-41c8c2d85312): committed