--- 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/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" "entgo.io/ent/entc" "entgo.io/ent/entc/gen" "entgo.io/contrib/entgql" ) func main() { ex, err := entgql.NewExtension() if err != nil { log.Fatalf("creating entgql extension: %v", err) } if err := entc.Generate("./schema", &gen.Config{}, entc.Extensions(ex)); 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 -mod=mod 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/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/graphql/objectidentification.htm). 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/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: - entgo.io/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. - entgo.io/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/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 `OrderField` / `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 ```graphql query { todos(first: 3, orderBy: {direction: DESC, field: TEXT}) { edges { node { text } } } } ``` ## Fields Collection The collection template adds support for automatic [GraphQL fields collection](https://spec.graphql.org/June2018/#sec-Field-Collection) for ent-edges using eager-loading. That means, if a query asks for nodes and their edges, entgql will add automatically [`With`](eager-load.md#api) steps to the root query, and as a result, the client will execute constant number of queries to the database - and it works recursively. For example, given this GraphQL query: ```graphql query { users(first: 100) { edges { node { photos { link } posts { content comments { content } } } } } } ``` The client will execute 1 query for getting the users, 1 for getting the photos, and another 2 for getting the posts, and their comments (4 in total). This logic works both for root queries/resolvers and for the node(s) API. ### Schema configuration 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")), } } ``` ### Usage and Configuration The GraphQL extension generates also edge-resolvers for the nodes under the `edge.go` file as follows: ```go func (t *Todo) Children(ctx context.Context) ([]*Todo, error) { result, err := t.Edges.ChildrenOrErr() if IsNotLoaded(err) { result, err = t.QueryChildren().All(ctx) } return result, err } ``` However, if you need to explicitly write these resolvers by hand, you can add the [`forceResolver`](https://gqlgen.com/master/config#inline-config-with-directives) option to your GraphQL schema: ```graphql type Todo implements Node { id: ID! children: [Todo]! @goField(forceResolver: true) } ``` Then, you can implement it on your type resolver. ```go func (r *todoResolver) Children(ctx context.Context, obj *ent.Todo) ([]*ent.Todo, error) { // Do something here. return obj.Edges.ChildrenOrErr() } ``` ## 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\. 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) } ``` ## Examples The [ent/contrib](https://github.com/ent/contrib) contains several examples at the moment: 1. A complete GraphQL server with a simple [Todo App](https://github.com/ent/contrib/tree/master/entgql/internal/todo) with numeric ID field 2. The same [Todo App](https://github.com/ent/contrib/tree/master/entgql/internal/todouuid) in 1, but with UUID type for the ID field 3. The same [Todo App](https://github.com/ent/contrib/tree/master/entgql/internal/todopulid) in 1 and 2, but with a prefixed [ULID](https://github.com/ulid/spec) or `PULID` as the ID field. This example supports the Relay Node API by prefixing IDs with the entity type rather than employing the ID space partitioning in [Universal IDs](migrate.md#universal-ids). --- Please note that this documentation is under development. All code parts reside in [ent/contrib/entgql](https://github.com/ent/contrib/tree/master/entgql), and an example of a todo-app can be found in [ent/contrib/entgql/todo](https://github.com/ent/contrib/tree/master/entgql/internal/todo).