Embedded associations in Rails using JSON fields

Learn how to improve the performance and scalability of your Rails applications by using embedded associations with JSON fields. Discover the benefits and limitations of this technique, and learn how to implement it in your own projects.

Arnaud Lachaume
Arnaud Lachaume LinkedIn
• 3 min read
Embedded associations in Rails using JSON fields

When it comes to associations in Rails we are all familiar with the traditional directives such as has_many, belongs_to etc.

But sometimes creating a whole table for an associated model feels like overkill because that associated model will only be used in the context of its parent. What you would like is: be able to embed that association within the parent the same way it can be done with NoSQL databases.

The above becomes even more relevant when the association is actually an ordered list of objects which is always saved as a whole.

Fortunately Rails supports storing attributes as JSON and offers a convenient attributes API which can be leveraged to create nested associations.

The Rails attributes API

The attributes API was shipped in Rails 5.

It allows you to define custom types for database fields such as:

The example above uses a prebuilt type but custom types can also be defined for more complex, object-oriented serialization/deserialization.

Let's see how to do this with a concrete example.

Book and Chapters (first version)

Let's assume we want to create Book model and associate to each book a list of chapters. I'll be assuming we're using Postgres as a database. The same can be done with MySQL.

The Book model

Let's start by creating the migration:

Then let's setup the Book ActiveRecord model:

The model uses the attribute directive to specify that the chapters will be of type Chapter::ListType.

This is the only thing we need to do in the parent model. All the nesting logic will actually be done in the Chapter model.

The Chapter model

The Chapter model will be implemented as a plain ActiveModel::Model. There is no need for ActiveRecord as the list of chapters will be saved on the parent model.

There are three things we need to define on this model:

  • Serializable attributes: required to know what to save in database
  • Equality operator: required to detect changes on the list of chapters
  • Type interface: how to serialize/deserialize our set of chapters

The Chapter model looks like this:

That's all we need to do to define a simple nested association.

Testing our new association

Let's open a Rails console and test our new association. The chapters attribute behaves exactly like any other ActiveRecord attribute.

Alright it works but this implementation is a bit of a one-off. Let's see how we can generalise the implementation.

Book and Chapters (refactored)

It feels a bit overkill to define a nested class called ListType on all the models we wish to embed. From one model to another the only parameter that will change is the actual model class.

Let's extract that nested type class into a dedicated class and parameterize it:

Now let's remove the old code from the Chapter model:

Finally, let's change the attribute declaration in our Book model:

That's all we need. With that refactored approach you can reuse this nested association pattern on various models within your app. All you need to do on associated models is define the attributes and equality methods.

About us

Keypup's SaaS solution allows engineering teams and all software development stakeholders to gain a better understanding of their engineering efforts by combining real-time insights from their development and project management platforms. The solution integrates multiple data sources into a unified database along with a user-friendly dashboard and insights builder interface. Keypup users can customize tried-and-true templates or create their own reports, insights and dashboards to get a full picture of their development operations at a glance, tailored to their specific needs.


Code snippets hosted with ā¤ by GitHub Banner designed by starline / Freepik

ā€

Ready to Transform Your Analytics?

Join teams already using AI to make data-driven decisions faster than ever.

Most Recent Articles

Achieving ISO27001 & SOC2 Type II: Continuous SDLC Audit with Keypup MCP

Achieving ISO27001 & SOC2 Type II: Continuous SDLC Audit with Keypup MCP

Learn how to achieve and maintain ISO27001 and SOC2 Type II certifications for your software development organization. Discover the specific SDLC requirements, audit processes, and how Keypup's MCP Server provides continuous compliance monitoring, automated evidence collection, and real-time audit trails—saving months of preparation time and reducing certification costs by up to 60%.

Stephane Ibos
Measuring AI Impact on Software Development: How to Track ROI and Efficiency with Data-Driven Insights

Measuring AI Impact on Software Development: How to Track ROI and Efficiency with Data-Driven Insights

AI coding assistants promise productivity gains, but are they delivering? Learn how to measure the real impact of AI tools on your SDLC—from code quality to cycle time to developer satisfaction. Discover which metrics matter for ROI analysis and how Keypup's MCP Server correlates AI adoption with engineering outcomes and financial performance.

Thomas Williams
Technical Debt Isn't Just Code: Measuring the Hidden Costs in Your SDLC

Technical Debt Isn't Just Code: Measuring the Hidden Costs in Your SDLC

Discover how technical debt extends far beyond messy code into your entire software development lifecycle. Learn to identify, measure, and quantify process debt, review debt, testing debt, and documentation debt using Keypup's MCP Server for AI-powered SDLC analysis and cost calculation.

Arnaud Lachaume