A critical model of ours utilizes STI, an incredibly useful tool in the right circumstances. Rails supports this gracefully; however, the JSONAPI Resources gem which implements our API required some hair-pulling.

Create routes, resources, and controllers for each subclass

You might try to define a JSONAPI classes for the parent only. While this may partially work, we found it best to define them for each STI subclass. This strategy allows for clean OOP-style behavior adjustments via overriding rather than a conditional. Plus, the large set of attributes required to create each type will likely vary.

As a result, you'll have these routes:

POST /fruit
POST /apples
POST /bananas

You'll have these resources:

# Parent
class Api::V1::FruitResource < JSONAPI::Resource; end

# STI Subclasses
class Api::V1::AppleResource < Api::V1::FruitResource; end
class Api::V1::BananaResource < Api::V1::FruitResource; end

You'll have these controllers:

# Parent
class Api::V1::FruitController < JSONAPI::ResourceController; end

# STI Subclasses
class Api::V1::ApplesController < Api::V1::FruitController; end
class Api::V1::BananasController < Api::V1::FruitController; end

Using the correct subclass in Resources

The main gotcha we encountered is that JR does not correctly determine the subclass model names. Unfortunately, you'll need to explicitly define them. You'd think this would work:

class Api::V1::AppleResource < Api::V1::FruitResource
  model_name 'Apple'
end

It does not. Instead, directly override the class method:

class Api::V1::AppleResource < Api::V1::FruitResource
  def self._model_class
    Apple
  end
end

Inherited configuration

JR does not inherit configuration. For example, if you are using a custom primary_key for your whole Fruit model, each subclass needs to define it.

class Api::V1::FruitResource < JSONAPI::Resource
  primary_key :alphanumeric_id
end

class Api::V1::AppleResource < Api::V1::FruitResource
  primary_key :alphanumeric_id
end
More blog posts