📣 Requestly API Client – Free Forever & Open Source. A powerful alternative to Postman. Try now ->

GraphQL with Ruby: Building Modern APIs

Rashmi Saini
Learn how to build GraphQL APIs with Ruby using graphql-ruby, covering schema design, queries, mutations, and testing with Requestly for efficient debugging.

GraphQL with Ruby combines the effectiveness of the Ruby language with the flexibility of GraphQL, enabling developers to build efficient, type-safe APIs. The graphql-ruby gem provides a robust foundation for integrating GraphQL into Rails applications, offering strong typing, a single endpoint, and precise data fetching.

This eliminates over-fetching and under-fetching issues common in REST APIs, improving performance and developer experience.

This article explores building GraphQL APIs with Ruby using the graphql-ruby gem, covering schema design, queries, mutations, and resolver patterns.

Installing and Setting Up graphql-ruby

To begin using GraphQL in a Ruby on Rails application, start by adding the graphql gem to your project.

1. Add the gem to your Gemfile

Include the following line:

gem 'graphql'

Then install it using:

bundle install

2. Generate the GraphQL structure

Run the Rails generator to scaffold the necessary files:

rails generate graphql:install

This creates:

  • A schema file at app/graphql/schema.rb
  • Base type classes under app/graphql/types/
  • A controller app/controllers/graphql_controller.rb
  • Routes for GraphQL execution and GraphiQL IDE access​

3. Verify the routes

Ensure your config/routes.rb includes:

post '/graphql', to: 'graphql#execute'if Rails.env.development?
mount GraphiQL::Rails::Engine, at: '/graphiql', graphql_path: '/graphql'end

This mounts the GraphiQL interface in development for easy query testing​

4. Test the setup

Start the Rails server:

rails server

Navigate to http://localhost:3000/graphiql and run a test query:

{
__schema {
queryType {
name
}
}
}

A successful response confirms the GraphQL endpoint is active and properly configured​

For standalone Ruby applications (non-Rails), require the gem and define the schema programmatically, then integrate with Rack to handle HTTP requests. This setup enables rapid development and debugging of GraphQL APIs using Ruby’s clean syntax and rich ecosystem.

Defining GraphQL Types in Ruby

In graphql-ruby, types define the structure of data available in the API and are created by extending GraphQL::Schema::Object. Each type corresponds to a model or data entity, such as User, Post, or Product, and exposes fields that clients can query.​

Creating an Object Type

Define a type by creating a class under app/graphql/types/. For example, a User type:

class Types::User < GraphQL::Schema::Object
description "A user of the application"
field :id, ID, null: false
field :name, String, null: false
field :email, String, null: true
field :created_at, GraphQL::Types::ISO8601DateTime, null: falseend

The field method declares each field, specifying its name, type, and whether it can return null.​

Field Naming Convention

Use underscored names in Ruby (e.g., created_at), which are automatically converted to camelCase (createdAt) in the GraphQL schema, following standard GraphQL naming conventions.

Supporting Different Type Kinds

graphql-ruby supports multiple type definitions:

  • Object Types: Represent entities with fields (Types::User)
  • Interface Types: Define common fields shared across types (e.g., Node for Relay)
  • Union Types: Represent a value that could be one of several object types
  • Enum Types: Define a set of allowed values (e.g., PostStatus)
  • Input Object Types: Used for complex arguments in mutations
  • Scalar Types: Built-in types like String, Int, Boolean, or custom scalars

Using Base Classes for Reuse

Create a base class (e.g., Types::BaseObject) to share common configurations like default null behavior or interfaces:

class Types::BaseObject < GraphQL::Schema::Object
include GraphQL::Types::Relay::Node
field_class Types::BaseFieldend

Then inherit from Types::BaseObject in all other object types for consistency.

These type definitions form the foundation of the GraphQL schema, enabling precise, self-documenting APIs that integrate seamlessly with frontends and tooling.

Building Queries

In graphql-ruby, queries are defined as a special type that serves as the entry point for read operations. This type, typically named Query, inherits from GraphQL::Schema::Object and exposes fields that return data without modifying server state.​

Defining the Root Query Type

Create app/graphql/types/query.rb to define the top-level query fields:

class Types::Query < GraphQL::Schema::Object
description "The query root of this schema"
field :user, Types::User, null: true do
description "Find a user by ID"
argument :id, ID, required: true
end
field :users, [Types::User], null: false do
description "List all users"
endend

Each field declaration specifies the return type, nullability, and any arguments the field accepts.​

Implementing Resolver Methods

Resolver logic is written as instance methods in the query class. For example:

def user(id:)
User.find_by(id: id)enddef users
User.allend

These methods are automatically called when the corresponding field is queried, with arguments passed as keyword parameters.​

Connecting to the Schema

Ensure the schema in app/graphql/schema.rb references the query type:

class MySchema < GraphQL::Schema
query Types::Queryend

This links the query type to the schema, making its fields available for execution.​

Testing Queries in GraphiQL

After setup, test queries using the GraphiQL interface at /graphiql. For example:

{ users { id name email }}

####Or fetch a single user:####

{ user(id: "1") { name createdAt }}

The response will reflect the structure defined in the type and resolver logic.​

Queries in graphql-ruby enable clients to request exactly the data they need, reducing over-fetching and improving API efficiency. By combining strongly typed fields with Ruby-backed resolvers, developers can build flexible, maintainable APIs that scale with application needs

Implementing Mutations

Mutations in graphql-ruby allow clients to modify data on the server, such as creating, updating, or deleting records. They are defined separately from queries and must be explicitly declared in the schema to handle write operations.​

Defining the Root Mutation Type

Create app/graphql/types/mutation.rb to define top-level mutation fields:

class Types::Mutation < GraphQL::Schema::Object
description "The mutation root of this schema"
field :create_user, mutation: Mutations::CreateUser
field :update_user, mutation: Mutations::UpdateUser
field :delete_user, mutation: Mutations::DeleteUserend

Instead of defining logic directly, fields reference dedicated mutation classes, promoting modularity and reusability.​

Creating Mutation Classes

Generate a mutation class under app/graphql/mutations/. For example, CreateUser:

class Mutations::CreateUser < GraphQL::Schema::Mutation
description "Create a new user"
field :user, Types::User, null: false
field :errors, [String], null: false
argument :name, String, required: true
argument :email, String, required: true
def resolve(name:, email:)
user = User.new(name: name, email: email)
if user.save
{ user: user, errors: [] }
else
{ user: nil, errors: user.errors.full_messages }
end
endend

The resolve method contains the business logic and returns a hash matching the defined fields.​

Connecting Mutations to the Schema

Update app/graphql/schema.rb to include the mutation type:

class MySchema < GraphQL::Schema
query Types::Query
mutation Types::Mutationend

This enables the mutation fields to be accessible via the GraphQL endpoint.​

Testing Mutations in GraphiQL

Use the GraphiQL interface to test mutation operations. For example:

mutation {
createUser(name: "Alice", email: "[email protected]") {
user {
id
name
email
}
errors
}
}

The response will include the created user object and any validation errors, as defined in the mutation class.​

By organizing mutations into dedicated classes, graphql-ruby supports clean, testable, and secure data modification patterns. Each mutation encapsulates its own logic, arguments, and return structure, making APIs predictable and easier to maintain.

Enhancing GraphQL Ruby Testing and Debugging with Requestly

Requestly is a powerful browser-based tool that enhances the development experience for GraphQL Ruby APIs by enabling real-time interception, modification, and mocking of HTTP requests. It allows developers and QA teams to test resolver behavior, debug issues, and simulate edge cases without altering backend code or restarting the server.

Intercept and Inspect GraphQL Requests

Requestly captures all outgoing GraphQL requests from the frontend, displaying the full query or mutation payload, variables, and headers. This visibility helps verify that the correct data is being sent to the Ruby backend and allows inspection of how arguments are passed to resolvers.​

Mock Resolver Responses

You can create rules in Requestly to intercept specific queries (e.g., query { users { name email } }) and return a custom JSON response. This is useful for:

  • Simulating data before the backend is ready
  • Testing error states (e.g., errors: [{ message: “Unauthorized” }])
  • Validating frontend handling of null or malformed data​

Modify Requests for Testing Scenarios

Use Requestly to alter query variables or headers dynamically. For example:

  • Change the Authorization header to test authentication logic in resolvers
  • Modify input arguments in a mutation to trigger validation errors
  • Simulate different user roles or permissions during development​

Simulate Latency and Failures

Introduce artificial delays or force 5xx responses to test how the client handles slow or failed resolver executions. This improves resilience and ensures proper loading and error states are implemented in the UI.​

Streamline Team Collaboration

Save and share Requestly rules across teams, ensuring consistent testing environments. Frontend developers can use predefined mocks while backend teams focus on resolver logic, reducing dependency bottlenecks.​

By integrating Requestly into the development workflow, teams can accelerate testing, improve debugging accuracy, and deliver robust GraphQL Ruby APIs with greater confidence.

Conclusion

Building GraphQL APIs with Ruby using the graphql-ruby gem offers a structured, efficient, and developer-friendly approach to modern backend development. From defining types and constructing queries and mutations to implementing robust resolver logic, the framework integrates seamlessly with Rails and leverages Ruby’s elegance to create maintainable, type-safe APIs.​

By following best practices, such as organizing mutations into dedicated classes, using base types for consistency, and preventing N+1 queries with batch loading, teams can ensure scalability and performance. Tools like GraphiQL and Requestly further enhance the development experience by enabling real-time testing, mocking, and debugging without backend dependencies.​

With strong community support, comprehensive documentation, and integration capabilities across frontend and backend systems, graphql-ruby empowers developers to build production-ready GraphQL servers that are flexible, testable, and easy to evolve.

Written by
Rashmi Saini