<commit_before>---
layout: guide
search: true
section: Schema
title: Class-based API
desc: Define your GraphQL schema with Ruby classes (1.8.x alpha releases)
experimental: true
index: 10
---

In GraphQL `1.8`+, you can use Ruby classes to build your schema. You can __mix__ class-style and `.define`-style type definitions in a schema.

You can get an overview of this new feature:

- [Rationale & Goals](#rationale--goals)
- [Compatibility & Migration Overview](#compatibility--migration-overview)
- [Roadmap](#roadmap)

And learn about the APIs:

- [Schema class](#schema-class)
- [Common type configurations](#common-type-configurations) (shared by all the following types)
- [Object classes](#object-classes)
- [Interface classes](#interface-classes)
- [Union classes](#union-classes)
- [Enum classes](#enum-classes)
- [Input Object classes](#input-object-classes)
- [Scalar classes](#scalar-classes)
- [Customizing definitions](#customizing-definitions)


## Rationale & Goals

This new API aims to improve the "getting started" experience and the schema customization experience by replacing GraphQL-Ruby-specific DSLs with familiar Ruby semantics (classes and methods).

Additionally, this new API must be cross-compatible with the current schema definition API so that it can be adopted bit-by-bit.

## Compatibility & Migration overview

Parts of your schema can be converted one-by-one, so you can convert definitions gradually.

### Classes

In general, each `.define { ... }` block will be converted to a class.

- Instead of a `GraphQL::{X}Type`, classes inherit from `GraphQL::Schema:{X}`. For example, instead of `GraphQL::ObjectType.define { ... }`, a definition is made by extending `GraphQL::Schema::Object`
- Any class hierarchy is supported; It's recommended to create a base class for your application, then extend the base class for each of your types (like `ApplicationController` in Rails, see [Customizing Definitions](#customizing-defintions)).

See sections below for specific information about each schema definition class.

### Type Instances

The previous `GraphQL::{X}Type` objects are still used under the hood. Each of the new `GraphQL::Schema::{X}` classes implements a few methods:

- `.to_graphql`: creates a new instance of `GraphQL::{X}Type`
- `.graphql_definition`: returns a cached instance of `GraphQL::{X}Type`

If you have custom code which breaks on new-style definitions, try calling `.graphql_definition` to get the underlying type object.

As described below, `.to_graphql` can be overridden to customize the type system.

## Roadmap

Here is a working plan for rolling out this feature:

- ongoing:
  - ☐ Receive feedback from GraphQL schema owners about the new API (usability & goals)
- graphql 1.8:
  - ☑ Build a schema definition API based on classes instead of singletons
  - ☑ Migrate a few components of GitHub's GraphQL schema to this new API
  - ☐ Build advanced class-based features:
    - ☐ Custom `Context` classes
    - ☐ Custom introspection types
    - ☐ Custom directives
    - ☐ Custom `Schema#execute` method
  - ☐ Migrate all of GitHub's GraphQL schema to this new API
- graphql 1.9:
  - ☐ Update all GraphQL-Ruby docs to reflect this new API
- graphql 1.10:
  - ☐ Begin sunsetting `.define`: isolate it in its own module
  - ☐ Remove `.define`

## Schema class

Your GraphQL schema is a class that extends {{ "GraphQL::Schema" | api_doc }}. Its configuration options are similar to `.define`-based options, but if you find something that doesn't work, please {% open_an_issue "Class-based schema issue","(Please share some example code and the error you found)" %}.

```ruby
class MyAppSchema < GraphQL::Schema
  max_complexity 400
  query Types::Query
  use GraphQL::Batch

  # Define hooks as class methods:
  def self.resolve_type(type, obj, ctx)
    # ...
  end

  def self.object_from_id(node_id, ctx)
    # ...
  end

  def self.id_from_object(object, type, ctx)
    # ...
  end
end
```

## Common Type Configurations

Some configurations are used for _all_ types described below:

- `graphql_name` overrides the type name. (The default value is the Ruby constant name, without any namespaces)
- `description` provides a description for GraphQL introspection.

For example:

```ruby
class Types::TodoList < GraphQL::Schema::Object # or Scalar, Enum, Union, whatever
  graphql_name "List" # Overrides the default of "TodoList"
  description "Things to do (may have already been done)"
end
```

(Implemented in {{ "GraphQL::Schema::Member" | api_doc }}).

## Object classes

Classes extending {{ "GraphQL::Schema::Object" | api_doc }} describe [Object types](http://graphql.org/learn/schema/#object-types-and-fields) and customize their behavior.

Object fields can be created with the `field(...)` class method, which accepts the similar arguments as the previous `field(...)` method.

```ruby
# first, somewhere, a base class:
class Types::BaseObject < GraphQL::Schema::Object
end

# then...
class Types::TodoList < BaseObject
  field :name, String, "The unique name of this list", null: false
  # Related Object:
  field :owner, Types::User, "The creator of this list", null: false
  # List field:
  field :viewers, [Types::User], "Users who can see this list", null: false
  # Connection:
  field :items, Types::TodoItem.connection_type, "Tasks on this list", null: false do
    argument :status, TodoStatus, "Restrict items to this status", null: true
  end
end
```

### New return type & argument type specification

The second argument to `field(...)` is the return type. This can be:

- A GraphQL type object built with `.define { ... }`
- A GraphQL type class which you defined
- A Ruby built-in such as `Integer`, `Float` or `String`
- A string which corresponds to a GraphQL built-in, such as `"ID"`, `"Boolean"`, or `"Int"`
- An _array_ of any of the above, which denotes a list type. Inner list types are always made non-null.

Nullability is expressed with the required `null:` keyword:

- `null: true` means that the field _may_ return null
- `null: false` means the field is non-nullable; it may not return null. If the implementation returns `nil`, GraphQL-Ruby will return an error to the client.

Here are some examples:

```ruby
field :name, String, null: true # String
field :id, "ID", null: false # ID!
field :scores, [Integer], null: false, # [Int!]!
field :teammates, [Types::User], null: false, # [User!]!
```

### Connection types

There is no `connection(...)` method. Instead, connection fields are inferred from the type name.

If the type name ends in `Connection`, the field is treated as a connection field.

This default may be overridden by passing a `connection: true` or `connection: false` keyword.

### Resolve function compatibility

If you define a type with a class, you can use existing GraphQL-Ruby resolve functions with that class, for example:

```ruby
# Using a Proc literal or #call-able
field :something, ... resolve: ->(obj, args, ctx) { ... }
# Using a predefined field
field :doSomething, field: Mutations::DoSomething.field
# Using a GraphQL::Function
field :something, function: Functions::Something.new
```

When using these resolution implementations, they will be called with the same `(obj, args, ctx)` parameters as before.

### Resolution with methods

If you implement a field by defining a method, you should expect some automatic transformations:

- GraphQL arguments will be converted to Ruby keyword arguments.
- If the field name is `camelCased`, the method name should be `underscore_cased`.
- If any argument names are `camelCased`, they will be passed to the method as `underscore_cased` Ruby keyword args.

Inside the method, you can access some instance variables:

- `@context` is the query context (formerly `ctx` to resolve functions)
- `@object` is the underlying application object (formerly `obj` to resolve functions)

## Interface classes

Interfaces extend `GraphQL::Schema::Interface`. First, make a base class:

```ruby
class BaseInterface < GraphQL::Schema::Interface
  # optional, see below for customizing field definitions
  field_class MyCustomField
end
```

Then, extend that for each interface:

```ruby
class RetailItemType < BaseInterface
  description "Something that can be bought"
  field :price, PriceType, "How much this item costs", null: false

  # Optional: if this method is defined, it overrides `Schema.resolve_type`
  def self.resolve_type(object, context)
    context.schema.types[object.class.name]
  end
end
```

Interface classes are never instantiated. At runtime, only their `.resolve_type` methods are called (if they're defined).

## Union classes

Unions extend `GraphQL::Schema::Union`. First, make a base class:

```ruby
class BaseUnion < GraphQL::Schema::Union
end
```

Then, extend that one for each union in your schema:

```ruby
class CommentSubjectType < BaseUnion
  description "Objects which may be commented on"
  possible_types PostType, ImageType

  # Optional: if this method is defined, it will override `Schema.resolve_type`
  def self.resolve_type(object, context)
    if object.is_a?(BlogPost)
      PostType
    else
      ImageType
    end
  end
end
```

Union classes are never instantiated; At runtime, only their `.resolve_type` methods may be called (if defined).

## Enum classes

<!-- TODO -->

## Input object classes

Input objects extend `GraphQL::Schema::InputObject`. First, make a base class:

```ruby
class BaseInputObject < GraphQL::Schema::InputObject
end
```

Then extend it for your input objects:<commit_msg>Add enum docs, document Implementation module<commit_after>---
layout: guide
search: true
section: Schema
title: Class-based API
desc: Define your GraphQL schema with Ruby classes (1.8.x alpha releases)
experimental: true
index: 10
---

In GraphQL `1.8`+, you can use Ruby classes to build your schema. You can __mix__ class-style and `.define`-style type definitions in a schema.

You can get an overview of this new feature:

- [Rationale & Goals](#rationale--goals)
- [Compatibility & Migration Overview](#compatibility--migration-overview)
- [Roadmap](#roadmap)

And learn about the APIs:

- [Schema class](#schema-class)
- [Common type configurations](#common-type-configurations) (shared by all the following types)
- [Object classes](#object-classes)
- [Interface classes](#interface-classes)
- [Union classes](#union-classes)
- [Enum classes](#enum-classes)
- [Input Object classes](#input-object-classes)
- [Scalar classes](#scalar-classes)
- [Customizing definitions](#customizing-definitions)


## Rationale & Goals

This new API aims to improve the "getting started" experience and the schema customization experience by replacing GraphQL-Ruby-specific DSLs with familiar Ruby semantics (classes and methods).

Additionally, this new API must be cross-compatible with the current schema definition API so that it can be adopted bit-by-bit.

## Compatibility & Migration overview

Parts of your schema can be converted one-by-one, so you can convert definitions gradually.

### Classes

In general, each `.define { ... }` block will be converted to a class.

- Instead of a `GraphQL::{X}Type`, classes inherit from `GraphQL::Schema:{X}`. For example, instead of `GraphQL::ObjectType.define { ... }`, a definition is made by extending `GraphQL::Schema::Object`
- Any class hierarchy is supported; It's recommended to create a base class for your application, then extend the base class for each of your types (like `ApplicationController` in Rails, see [Customizing Definitions](#customizing-defintions)).

See sections below for specific information about each schema definition class.

### Type Instances

The previous `GraphQL::{X}Type` objects are still used under the hood. Each of the new `GraphQL::Schema::{X}` classes implements a few methods:

- `.to_graphql`: creates a new instance of `GraphQL::{X}Type`
- `.graphql_definition`: returns a cached instance of `GraphQL::{X}Type`

If you have custom code which breaks on new-style definitions, try calling `.graphql_definition` to get the underlying type object.

As described below, `.to_graphql` can be overridden to customize the type system.

## Roadmap

Here is a working plan for rolling out this feature:

- ongoing:
  - ☐ Receive feedback from GraphQL schema owners about the new API (usability & goals)
- graphql 1.8:
  - ☑ Build a schema definition API based on classes instead of singletons
  - ☑ Migrate a few components of GitHub's GraphQL schema to this new API
  - ☐ Build advanced class-based features:
    - ☐ Custom `Context` classes
    - ☐ Custom introspection types
    - ☐ Custom directives
    - ☐ Custom `Schema#execute` method
  - ☐ Migrate all of GitHub's GraphQL schema to this new API
- graphql 1.9:
  - ☐ Update all GraphQL-Ruby docs to reflect this new API
- graphql 1.10:
  - ☐ Begin sunsetting `.define`: isolate it in its own module
  - ☐ Remove `.define`

## Schema class

Your GraphQL schema is a class that extends {{ "GraphQL::Schema" | api_doc }}. Its configuration options are similar to `.define`-based options, but if you find something that doesn't work, please {% open_an_issue "Class-based schema issue","(Please share some example code and the error you found)" %}.

```ruby
class MyAppSchema < GraphQL::Schema
  max_complexity 400
  query Types::Query
  use GraphQL::Batch

  # Define hooks as class methods:
  def self.resolve_type(type, obj, ctx)
    # ...
  end

  def self.object_from_id(node_id, ctx)
    # ...
  end

  def self.id_from_object(object, type, ctx)
    # ...
  end
end
```

## Common Type Configurations

Some configurations are used for _all_ types described below:

- `graphql_name` overrides the type name. (The default value is the Ruby constant name, without any namespaces)
- `description` provides a description for GraphQL introspection.

For example:

```ruby
class Types::TodoList < GraphQL::Schema::Object # or Scalar, Enum, Union, whatever
  graphql_name "List" # Overrides the default of "TodoList"
  description "Things to do (may have already been done)"
end
```

(Implemented in {{ "GraphQL::Schema::Member" | api_doc }}).

## Object classes

Classes extending {{ "GraphQL::Schema::Object" | api_doc }} describe [Object types](http://graphql.org/learn/schema/#object-types-and-fields) and customize their behavior.

Object fields can be created with the `field(...)` class method, which accepts the similar arguments as the previous `field(...)` method.

```ruby
# first, somewhere, a base class:
class Types::BaseObject < GraphQL::Schema::Object
end

# then...
class Types::TodoList < BaseObject
  field :name, String, "The unique name of this list", null: false
  # Related Object:
  field :owner, Types::User, "The creator of this list", null: false
  # List field:
  field :viewers, [Types::User], "Users who can see this list", null: false
  # Connection:
  field :items, Types::TodoItem.connection_type, "Tasks on this list", null: false do
    argument :status, TodoStatus, "Restrict items to this status", null: true
  end
end
```

### New return type & argument type specification

The second argument to `field(...)` is the return type. This can be:

- A GraphQL type object built with `.define { ... }`
- A GraphQL type class which you defined
- A Ruby built-in such as `Integer`, `Float` or `String`
- A string which corresponds to a GraphQL built-in, such as `"ID"`, `"Boolean"`, or `"Int"`
- An _array_ of any of the above, which denotes a list type. Inner list types are always made non-null.

Nullability is expressed with the required `null:` keyword:

- `null: true` means that the field _may_ return null
- `null: false` means the field is non-nullable; it may not return null. If the implementation returns `nil`, GraphQL-Ruby will return an error to the client.

Here are some examples:

```ruby
field :name, String, null: true # String
field :id, "ID", null: false # ID!
field :scores, [Integer], null: false, # [Int!]!
field :teammates, [Types::User], null: false, # [User!]!
```

### Connection types

There is no `connection(...)` method. Instead, connection fields are inferred from the type name.

If the type name ends in `Connection`, the field is treated as a connection field.

This default may be overridden by passing a `connection: true` or `connection: false` keyword.

### Resolve function compatibility

If you define a type with a class, you can use existing GraphQL-Ruby resolve functions with that class, for example:

```ruby
# Using a Proc literal or #call-able
field :something, ... resolve: ->(obj, args, ctx) { ... }
# Using a predefined field
field :doSomething, field: Mutations::DoSomething.field
# Using a GraphQL::Function
field :something, function: Functions::Something.new
```

When using these resolution implementations, they will be called with the same `(obj, args, ctx)` parameters as before.

### Resolution with methods

If you implement a field by defining a method, you should expect some automatic transformations:

- GraphQL arguments will be converted to Ruby keyword arguments.
- If the field name is `camelCased`, the method name should be `underscore_cased`.
- If any argument names are `camelCased`, they will be passed to the method as `underscore_cased` Ruby keyword args.

Inside the method, you can access some instance variables:

- `@context` is the query context (formerly `ctx` to resolve functions)
- `@object` is the underlying application object (formerly `obj` to resolve functions)

## Interface classes

Interfaces extend `GraphQL::Schema::Interface`. First, make a base class:

```ruby
class BaseInterface < GraphQL::Schema::Interface
  # optional, see below for customizing field definitions
  field_class MyCustomField
end
```

Then, extend that for each interface:

```ruby
class RetailItemType < BaseInterface
  description "Something that can be bought"
  field :price, PriceType, "How much this item costs", null: false

  # Optional: if this method is defined, it overrides `Schema.resolve_type`
  def self.resolve_type(object, context)
    context.schema.types[object.class.name]
  end

  module Implementation
    # optional, see below
  end
end
```

Interface classes are never instantiated. At runtime, only their `.resolve_type` methods are called (if they're defined).

### Implementation modules

An interface may contain a module named `Implementation`. If it does, that module will be included into any `Object` class which implements the interface. For example, this `Implementation` module contains the `#price` method:

```ruby
class RetailItemType < BaseInterface
  field :price, PriceType, null: false

  module Implementation
    def price
      Price.new(price_in_cents)
    end
  end
end
```

When the interface is implemented by an `Object`:

```ruby
class CarType < BaseObject
  implements RetailItemType
end
```

Then the object gets a few things from the interface:

- Any `field` definitions from the interface (which may be overridden by the `Object`)
- The `Implementation` module is `include`-d into the object, so it gets any methods from that module (which may be overridden by the `Object`)

Specifically, in the example above, `CarType` would get a field named `price` and a `#price` method which implements that field.

## Union classes

Unions extend `GraphQL::Schema::Union`. First, make a base class:

```ruby
class BaseUnion < GraphQL::Schema::Union
end
```

Then, extend that one for each union in your schema:

```ruby
class CommentSubjectType < BaseUnion
  description "Objects which may be commented on"
  possible_types PostType, ImageType

  # Optional: if this method is defined, it will override `Schema.resolve_type`
  def self.resolve_type(object, context)
    if object.is_a?(BlogPost)
      PostType
    else
      ImageType
    end
  end
end
```

Union classes are never instantiated; At runtime, only their `.resolve_type` methods may be called (if defined).

## Enum classes

Enums extend `GraphQL::Schema::Enum`. First, make a base class:

```ruby
class BaseEnum < GraphQL::Schema::Enum
end
```

Then, extend that class to define enums:

```ruby
class CategoryType < BaseEnum
  description "Things that a blog post can be about"
  value "SPORTS", "Various sports ball things"
  value "SOFTWARE", "Programming and stuff", value: "CODING"
end
```

The `value(...)` API is identical to the previous API.

Enum classes are never instantiated and their methods are never called.


## Input object classes

Input objects extend `GraphQL::Schema::InputObject`. First, make a base class:

```ruby
class BaseInputObject < GraphQL::Schema::InputObject
end
```

Then extend it for your input objects:
{'fraction_non_alphanumeric': 0.09947110485710127, 'fraction_numerical': 0.0015527196855742637, 'mean_word_length': 4.423684210526316, 'pattern_counts': {'":': 0, '<': 26, '<?xml version=': 0, '>': 6, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5915229', 'n_tokens_mistral': 5948, 'n_tokens_neox': 5669, 'n_words': 2581}