window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(function () { var w = window if (w.LeadBooster) { console.warn('LeadBooster already exists') } else { w.LeadBooster = { q: [], on: function (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: function (n) { this.q.push({ t: 't', n: n }) }, } } })() Polymorphism in Ruby and GraphQL - The Codest
The Codest
  • About us
  • Services
    • Software Development
      • Frontend Development
      • Backend Development
    • Staff Augmentation
      • Frontend Developers
      • Backend Developers
      • Data Engineers
      • Cloud Engineers
      • QA Engineers
      • Other
    • It Advisory
      • Audit & Consulting
  • Industries
    • Fintech & Banking
    • E-commerce
    • Adtech
    • Healthtech
    • Manufacturing
    • Logistics
    • Automotive
    • IOT
  • Value for
    • CEO
    • CTO
    • Delivery Manager
  • Our team
  • Case Studies
  • Know How
    • Blog
    • Meetups
    • Webinars
    • Resources
Careers Get in touch
  • About us
  • Services
    • Software Development
      • Frontend Development
      • Backend Development
    • Staff Augmentation
      • Frontend Developers
      • Backend Developers
      • Data Engineers
      • Cloud Engineers
      • QA Engineers
      • Other
    • It Advisory
      • Audit & Consulting
  • Value for
    • CEO
    • CTO
    • Delivery Manager
  • Our team
  • Case Studies
  • Know How
    • Blog
    • Meetups
    • Webinars
    • Resources
Careers Get in touch
Back arrow GO BACK
2022-01-13
Software Development

Polymorphism in Ruby and GraphQL

Lukasz Brzeszcz

In this article, I will present the use of polymorphism in GraphQL. Before I start, however, it is worth recalling what polymorphism and GraphQL are.

Polymorphism

Polymorphism is a key component of object-oriented programming. To simplify things, it is based on the fact that objects of different classes have access to the same interface, which means that we can expect from each of them the same functionality but not necessarily implemented in the same way. In Ruby developers can obtain polymorphism in three ways:

Inheritance

Inheritance consists in creating a parent class and child classes (i.e., inheriting from the parent class). Subclasses receive the functionality of the parent class and also allow you to change and add a functionality.

Example:

class Document
  attr_reader :name
end

class PDFDocument < Document
  def extension
    :pdf
  end
end

class ODTDocument < Document
  def extension
    :odt
  end
end

Modules

Modules in Ruby have many uses. One of them is mixins (read more about mixins in The Ultimate Breakdown: Ruby vs. Python). Mixins in Ruby can be used similarly to interfaces in other programming languages (e.g., in Java), for instance, you can define in them methods common to objects that will contain a given mixin. It is a good practice to include read-only methods in modules, so methods that will not modify the state of this object.

Example:

module Taxable
  def tax

     price * 0.23
  end
end

class Car
  include Taxable
 attr_reader :price
end

class Book
  include Taxable

 attr_reader :price
end

Duck typing

This is one of the key features of dynamically typed languages. The name comes from the famous test if it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck. The programmer does not have to be interested to which class the given object belongs. What is important are the methods that can be called on this object.

Using the classes defined in the example above:

class Car
  attr_reader :price

 def initialize(price)
    @price = price
   end
end

class Book
  attr_reader :price

 def initialize(price)
    @price = price
  end
end

car = Car.new(20.0)
book = Book.new(10.0)

[car, book].map(&:price

GrapQL

GraphQL is a relatively new query language for APIs. Its advantages include the fact that it has a very simple syntax and, in addition, the client decides what exactly they want to get as each customer gets exactly what they want and nothing else.

Sample query in GraphQL:

{
  allUsers {
     users {
        id
        login
        email

       }
     }
   }

Sample response:

{
  "allUsers": {
    "users": [
     {
        "id": 1,
        "login": "user1",
        "email": "[email protected]"
      },
      {
        "id": 2,
        "login": "user2",
        "email": "[email protected]"
      },
    ]
  }
}

This is probably all we need to know at the moment. So, let’s get to the point.

Presentation of the problem

To best understand the problem and its solution, let’s create an example. It would be good if the example was both original and fairly down-to-earth. One that each of us can encounter someday. How about… animals? Yes! Great idea!

ruby and grapql polymorphism - animals

Suppose we have a backend application written in Ruby on Rails. It is already adapted to handle the above scheme. Let’s also assume that we already have GraphQL configured. We want to enable the client to make an inquiry within the following structure:

{
 allZoos : {
    zoo: {
      name
      city
      animals: {
        ...
      }
    }
  }
}

What should be put instead of the three dots in order to obtain the missing information – we will find out later.

Implementation

Below I will present the steps needed to achieve the goal.

Adding a query to QueryType

First, you need to define what exactly the query allZoos means. To do this, we need to visit the fileapp/graphql/types/query_type.rb and define the query:

   module Types
      class QueryType < Types::BaseObject
       field :all_zoos, [Types::ZooType], null: false

       def all_zoos
          Zoo.all
       end
    end
 end

The query is already defined. Now it’s time to define the return types.

Definition of types

The first type required will be ZooType. Let’s define it in the file app/graphql/types/ zoo_type.rb:

module Types
  class ZooType < Types::BaseObject
    field :name, String, null: false
    field :city, String, null: false
    field :animals, [Types::AnimalType], null: false
  end
end

Now it’s time to define the type AnimalType:

module Types
  class AnimalType < Types::BaseUnion
   possible_types ElephantType, CatType, DogType

     def self.resolve_type(obj, ctx)
       if obj.is_a?(Elephant)
          ElephantType
       elsif obj.is_a?(Cat)
         CatType
       elsif obj.is_a?(Dog)
        DogType
      end
    end
  end
end

What do we see in the code above?

  1. The AnimalType inherits from Types::BaseUnion.
  2. We have to list all types that can make up a given union.
    3.We override the function self.resolve_object(obj, ctx),which must return the type of a given object.

The next step is to define the types of animals. However, we know that some fields are common to all animals. Let’s include them in type AnimalInterface:

module Types
  module AnimalInterface
    include Types::BaseInterface

    field :name, String, null: false
    field :age, Integer, null: false
  end
end

Having this interface, we can proceed to define the types of specific animals:

module Types
  class ElephantType < Types::BaseObject
    implements Types::AnimalInterface

    field :trunk_length, Float, null: false
  end
end

module Types
  class CatType < Types::BaseObject
   implements Types::AnimalInterface

   field :hair_type, String, null: false
  end
end

module Types
  class DogType < Types::BaseObject
    implements Types::AnimalInterface

     field :breed, String, null: false
  end
end

That’s it! Ready! One last question: how can we use what we have done from the client’s side?

Building the query

{
 allZoos : {
   zoo: {
      name
      city
      animals: {
        __typename

        ... on ElephantType {
          name
          age
          trunkLength
        }

         ... on CatType {
          name
          age
          hairType
         }
         ... on DogType {
          name
          age
          breed
         }
       }
     }
   }
 }

We can use an additional __typename field here, which will return the exact type of a given element (e.g., CatType). What will a sample answer look like?

{
  "allZoos": [

   {
      "name": "Natura Artis Magistra",
      "city": "Amsterdam",
      "animals": [
        {
          "__typename": "ElephantType"
          "name": "Franco",
          "age": 28,
          "trunkLength": 9.27
         },
         {
         "__typename": "DogType"
         "name": "Jack",
         "age": 9,
         "breed": "Jack Russell Terrier"
        },
      ]
    }
  ]
} 

Analysis

One drawback of this approach is apparent. In the query, we must enter the name and age in each type, even though we know that all animals have these fields. This is not bothersome when the collection contains completely different objects. In this case, however, the animals share almost all fields. Can it be improved somehow?

Of course! We make the first change in the file app/graphql/types/zoo_type.rb:

module Types
  class ZooType < Types::BaseObject
    field :name, String, null: false
    field :city, String, null: false
    field :animals, [Types::AnimalInterface], null: false
  end
end

We no longer need the union we have defined before. We change Types::AnimalType to Types::AnimalInterface.

The next step is to add a function that returns a type from Types :: AnimalInterface and also add a list of orphan_types, so types that are never directly used:

module Types
  module AnimalInterface
    include Types::BaseInterface

   field :name, String, null: false
   field :age, Integer, null: false

   definition_methods do
      def resolve_type(obj, ctx)
        if obj.is_a?(Elephant)
          ElephantType
        elsif obj.is_a?(Cat)
          CatType
        elsif obj.is_a?(Dog)
          DogType
        end
      end
    end
    orphan_types Types::ElephantType, Types::CatType, Types::DogType
  end
end

Thanks to this simple procedure, the query has a less complex form:

{
  allZoos : {
   zoo: {
      name
      city
      animals: {
        __typename
        name
        age

       ... on ElephantType {
          trunkLength

       }
       ... on CatType {
          hairType

       }
       ... on DogType {
          breed

        }
      }
    }
  }
}

Summary

GraphQL is a really great solution. If you don’t know it yet, try it. Trust me, it’s worth it. It does a great job in solving problems appearing in, for example, REST APIs. As I showed above, polymorphism is not a real obstacle for it. I presented two methods to tackle it.
Reminder:

  • If you operate on a list of objects with a common base or a common interface – use the interfaces,
  • If you operate on a list of objects with a different structure, use a different interface – use the union

Read More

GraphQL Ruby. What about performance?

Rails and Other Means of Transport

Rails Development with TMUX, Vim, Fzf + Ripgrep

Related articles

Software Development

Build Future-Proof Web Apps: Insights from The Codest’s Expert Team

Discover how The Codest excels in creating scalable, interactive web applications with cutting-edge technologies, delivering seamless user experiences across all platforms. Learn how our expertise drives digital transformation and business...

THECODEST
Software Development

Top 10 Latvia-Based Software Development Companies

Learn about Latvia's top software development companies and their innovative solutions in our latest article. Discover how these tech leaders can help elevate your business.

thecodest
Enterprise & Scaleups Solutions

Java Software Development Essentials: A Guide to Outsourcing Successfully

Explore this essential guide on successfully outsourcing Java software development to enhance efficiency, access expertise, and drive project success with The Codest.

thecodest
Software Development

The Ultimate Guide to Outsourcing in Poland

The surge in outsourcing in Poland is driven by economic, educational, and technological advancements, fostering IT growth and a business-friendly climate.

TheCodest
Enterprise & Scaleups Solutions

The Complete Guide to IT Audit Tools and Techniques

IT audits ensure secure, efficient, and compliant systems. Learn more about their importance by reading the full article.

The Codest
Jakub Jakubowicz CTO & Co-Founder

Subscribe to our knowledge base and stay up to date on the expertise from the IT sector.

    About us

    The Codest – International software development company with tech hubs in Poland.

    United Kingdom - Headquarters

    • Office 303B, 182-184 High Street North E6 2JA
      London, England

    Poland - Local Tech Hubs

    • Fabryczna Office Park, Aleja
      Pokoju 18, 31-564 Kraków
    • Brain Embassy, Konstruktorska
      11, 02-673 Warsaw, Poland

      The Codest

    • Home
    • About us
    • Services
    • Case Studies
    • Know How
    • Careers
    • Dictionary

      Services

    • It Advisory
    • Software Development
    • Backend Development
    • Frontend Development
    • Staff Augmentation
    • Backend Developers
    • Cloud Engineers
    • Data Engineers
    • Other
    • QA Engineers

      Resources

    • Facts and Myths about Cooperating with External Software Development Partner
    • From the USA to Europe: Why do American startups decide to relocate to Europe
    • Tech Offshore Development Hubs Comparison: Tech Offshore Europe (Poland), ASEAN (Philippines), Eurasia (Turkey)
    • What are the top CTOs and CIOs Challenges?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Website terms of use

    Copyright © 2025 by The Codest. All rights reserved.

    en_USEnglish
    de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian jaJapanese ko_KRKorean es_ESSpanish nl_NLDutch etEstonian elGreek en_USEnglish