Rails + Storing User Settings — 2016 Edition

Using Postgres 9.4+, Storext & jsonb with Rails

Mike Coutermarsh
Jun 12, 2016 · 3 min read
Image for post
Image for post

I wrote about this problem back in 2013. And conveniently, tech has gotten much better since then. So, it is time for an update 😀.

JSONB + Storext is currently my favorite method for storing user settings in a Rails app. It’s super flexible, like NoSQL, but behaves exactly like the standard Rails attributes that you already know and love.

(if you want to know more about why I think this is best, skip to the end, I explain there)

How to do it

What you’ll need

  • Postgres 9.4+ (is possible with lower versions, if you use hstore)
  • Rails 4.x+

1. Add the column

First, you’ll need to add a new jsonb column to your users table. This is where you’ll store all of your user’s settings. We use JSON so that we can fit all of our settings in a single column.

Alternative column naming ideas: preferences, notification_settings, email_settings

2. Install Storext

Your settings are stored as json in Postgres, but we want them to act like regular ActiveRecord columns. Storext sits on top of ActiveRecord::Store and adds some nice extra features (such as type coercion) to your json values.

3. Add your settings

Now the fun part. Defining your settings. These are done with Storext’s store_attributes method.

  # "settings" matches what we named the database column
store_attributes :settings do
subscribed_to_newsletter Boolean, default: false
time_zone String
theme String, default: 'dark'
send_highlights_browser_push Boolean, default: true
send_mention_email Boolean, default: true
end
end

All of your standard Rails validations (except for uniqueness) will work.

4. You’re done!

You’ll now be able to use your new settings just like you would any other ActiveRecord attribute. 🌟

More info

I love this solution because it’s super flexible. You can add and remove settings easily without tedious (or even downtime) migrations. They are also indexable and queryable if needed.

When I stumbled upon Storext, I got really excited. It gives us some really nice features, such as default values and type coercion. Coercion comes in incredibly useful for managing user settings. If a Rails form submits the value “1” for a checkbox instead of a true, Storext will store it as a boolean as expected. This behavior matches what Rails does internally when setting normal attributes.

Why not a separate table?

User settings tend to become a core piece of every application. If they are retrieved on every request, you’re now making an extra query with each request. Using a separate table makes sense when you have to have individual columns for each setting. But since we’re using jsonb, that’s not an issue. It’s just a single column.

Why jsonb instead of hstore?

Hstore is only for storing text. If you’re going to store boolean or integer values, you start having to coerce the values on read. This works, but is a little messy. The boolean/integer value you see in Rails is different than what’s stored in the database. If you connect this database to a different application that’s missing that application logic… well, you have to write the coercion again in that other application.

Note: If you’re going to be querying off of your settings values, be sure to add a gin index.

For more information, I recommend reading “Hstore vs. JSON — Which to Use in Postgres” by Craig Kerstiens.


If you have a question, respond to this post or reach out on Twitter @mscccc.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store