---
id: graphql
title: GraphQL Integration
---

The `ent` framework provides an integration with GraphQL through the [99designs/gqlgen](https://github.com/99designs/gqlgen)
library using the [external templates](templates.md) option (i.e. it can be extended to support other libraries).

## Quick Introduction

In order to enable the [`entgql`](https://github.com/facebookincubator/ent-contrib/tree/master/entgql) extension to your
project, you need to use the `entc` (ent codegen) package as described [here](code-gen.md#use-entc-as-a-package).
Follow these 3 steps to enable it to your project:

1\. Create a new Go file named `ent/entc.go`, and paste the following content:

```go
// +build ignore

package main

import (
	"log"

	"github.com/facebook/ent/entc"
	"github.com/facebook/ent/entc/gen"
	"github.com/facebookincubator/ent-contrib/entgql"
)

func main() {
	err := entc.Generate("./schema", &gen.Config{
		Templates: entgql.AllTemplates,
	})
	if err != nil {
		log.Fatalf("running ent codegen: %v", err)
	}
}
```

2\. Edit the `ent/generate.go` file to execute the `ent/entc.go` file:

```go
package ent

//go:generate go run entc.go
```

Note that `ent/entc.go` is ignored using a build tag, and it's executed by the `go generate` command
through the `generate.go` file. The full example can be found in the [ent-contrib repository](https://github.com/facebookincubator/ent-contrib/blob/master/entgql/internal/todo).


3\. Run codegen for your ent project:

```console
go generate ./...
```

After running codegen, the following add-ons will be added to your project.

## Node API

A new file named `ent/node.go` was created that implements the [Relay Node interface](https://relay.dev/docs/en/graphql-server-specification.html#object-identification).

In order to use the new generated `ent.Noder` interface in the [GraphQL resolver](https://gqlgen.com/reference/resolvers/),
add the `Node` method to the query resolver, and look at the [configuration](#gql-configuration) section to understand
how to use it.

If you are using the [Universal IDs](migrate.md#universal-ids) option in the schema migration, the NodeType is derived
from the id value and can be used as follows:

```go
func (r *queryResolver) Node(ctx context.Context, id int) (ent.Noder, error) {
	return r.client.Noder(ctx, id)
}
```

However, if you use a custom format for the global unique identifiers, you can control the NodeType as follows:

```go
func (r *queryResolver) Node(ctx context.Context, guid string) (ent.Noder, error) {
	typ, id := parseGUID(guid)
	return r.client.Noder(ctx, id, ent.WithNodeType(typ))
}
```


## GQL Configuration

Here's a configuration example for a todo app as exists in [ent-contrib/entgql/todo](https://github.com/facebookincubator/ent-contrib/tree/master/entgql/internal/todo).

```yaml
schema:
  - todo.graphql

resolver:
  # Tell gqlgen to generate resolvers next to the schema file.
  layout: follow-schema
  dir: .

# gqlgen will search for any type names in the schema in the generated
# ent package. If they match it will use them, otherwise it will new ones.
autobind:
  - github.com/facebookincubator/ent-contrib/entgql/internal/todo/ent

models:
  ID:
    model:
      - github.com/99designs/gqlgen/graphql.IntID
  Node:
    model:
      # ent.Noder is the new interface generated by the Node template.
      - github.com/facebookincubator/ent-contrib/entgql/internal/todo/ent.Noder
```

## Pagination

The pagination template adds a pagination support according to the _Relay Cursor Connections Spec_. More info
about the Relay Spec can be found in its [website](https://relay.dev/graphql/connections.htm).

## Connection Ordering

The ordering option allows us to apply an ordering on the edges returned from a connection.

### Usage Notes

- The generated types will be `autobind`ed to GraphQL types if a naming convention is preserved (see example below).
- Ordering can only be defined on ent fields (no edges).
- Ordering fields should normally be [indexed](schema-indexes.md) to avoid full table DB scan.
- Pagination queries can be sorted by a single field (no order by ... then by ... semantics).

### Example

Let's go over the steps needed in order to add ordering to an existing GraphQL type.
The code example is based on a todo-app that can be found in [ent-contrib/entql/todo](https://github.com/facebookincubator/ent-contrib/tree/master/entgql/internal/todo).

### Defining order fields in ent/schema

Ordering can be defined on any comparable field of ent by annotating it with `entgql.Annotation`.
Note that the given `OrderField` name must match its enum value in graphql schema.
```go
func (Todo) Fields() []ent.Field {
    return []ent.Field{
	    field.Time("created_at").
			Default(time.Now).
			Immutable().
			Annotations(
				entgql.OrderField("CREATED_AT"),
			),
		field.Enum("status").
			NamedValues(
				"InProgress", "IN_PROGRESS",
				"Completed", "COMPLETED",
			).
			Annotations(
				entgql.OrderField("STATUS"),
			),
		field.Int("priority").
			Default(0).
			Annotations(
				entgql.OrderField("PRIORITY"),
			),
		field.Text("text").
			NotEmpty().
			Annotations(
				entgql.OrderField("TEXT"),
			),
    }
}
```
That's all the schema changes required, make sure to run `go generate` to apply them.

### Define ordering types in GraphQL schema

Next we need to define the ordering types in graphql schema:
```graphql
enum OrderDirection {
  ASC
  DESC
}

enum TodoOrderField {
  CREATED_AT
  PRIORITY
  STATUS
  TEXT
}

input TodoOrder {
  direction: OrderDirection!
  field: TodoOrderField
}
```
Note that the naming must take the form of `<T>OrderField` / `<T>Order` for `autobind`ing to the generated ent types.
Alternatively [@goModel](https://gqlgen.com/config/#inline-config-with-directives) directive can be used for manual type binding.

### Adding orderBy argument to the pagination query
```graphql
type Query {
  todos(
    after: Cursor
    first: Int
    before: Cursor
    last: Int
    orderBy: TodoOrder
  ): TodoConnection
}
```
That's all for the GraphQL schema changes, let's run `gqlgen` code generation.

### Update the underlying resolver

Head over to the Todo resolver and update it to pass `orderBy` argument to `.Paginate()` call:

```go
func (r *queryResolver) Todos(ctx context.Context, after *ent.Cursor, first *int, before *ent.Cursor, last *int, orderBy *ent.TodoOrder) (*ent.TodoConnection, error) {
	return r.client.Todo.Query().
		Paginate(ctx, after, first, before, last,
			ent.WithTodoOrder(orderBy),
		)
}
```

### Use in GraphQL

```text
query {
    todos(first: 3, orderBy: {direction: DESC, field: NAME}) {
        edges {
            node {
                name
            }
        }
    }
}
```

## Fields Collection

The collection template adds support for automatic fields collection from GraphQL requests using eager-loading
the ent-edges. In order to configure this option to specific edges, use the `entgql.Annotation` as follows:

```go
func (Todo) Edges() []ent.Edge {
    return []ent.Edge{
        edge.To("children", Todo.Type).
            Annotations(entgql.Bind()).
            From("parent").
            // Bind implies the edge name in graphql schema is
            // equivalent to the name used in ent schema.
            Annotations(entgql.Bind()).
            Unique(),
        edge.From("owner", User.Type).
            Ref("tasks").
            // Map edge names as defined in graphql schema.
            Annotations(entgql.MapsTo("taskOwner")),
    }
}
```

Then, in the resolver, use it as follows:

```go
func (r *todoResolver) Parent(ctx context.Context, t *ent.Todo) (*ent.Todo, error) {
	parent, err := t.Edges.ParentOrErr()
	return parent, ent.MaskNotFound(err)
}

func (r *todoResolver) Children(ctx context.Context, obj *ent.Todo) ([]*ent.Todo, error) {
	return obj.Edges.ChildrenOrErr()
}
```

More info about fields-collection can be found in [Relay website](https://spec.graphql.org/June2018/#sec-Field-Collection).

## Enum Implementation

The enum template implements the MarshalGQL/UnmarshalGQL methods for enums generated by ent.

## Transactional Mutations

The `entgql.Transactioner` handler executes each GraphQL mutation in a transaction. The injected client for the resolver
is a [transactional `ent.Client`](transactions.md#transactional-client).
Hence, code that uses `ent.Client` won't need to be changed. In order to use it, follow these steps:


1\. In the GraphQL server initialization, use the `entgql.Transactioner` handler as follows:

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

2\. And then, in the GraphQL mutations, use the client from context as follows:
```go
func (mutationResolver) CreateTodo(ctx context.Context, todo TodoInput) (*ent.Todo, error) {
	client := ent.FromContext(ctx)
	return client.Todo.
		Create().
		SetStatus(todo.Status).
		SetNillablePriority(todo.Priority).
		SetText(todo.Text).
		SetNillableParentID(todo.Parent).
		Save(ctx)
}
```

---

Please note that this documentation is under development. All code parts reside in [ent-contrib/entgql](https://github.com/facebookincubator/ent-contrib/tree/master/entgql),
and an example of a todo-app can be found in [ent-contrib/entql/todo](https://github.com/facebookincubator/ent-contrib/tree/master/entgql/internal/todo).
