Directory Image
This website uses cookies to improve user experience. By using our website you consent to all cookies in accordance with our Privacy Policy.

How We Retrieve Tenant Data in a Multi-Tenant App with Detectify

Author: Ievgen Cherkashyn
by Ievgen Cherkashyn
Posted: Oct 20, 2016

We often build multi-tenant applications for Software as a Service (SaaS) providers. As we know, support for multi-tenancy is imperative for a SaaS application because a single instance of the app must manage data for multiple clients. The difference between a multi-tenant and a single-tenant app chiefly concerns the database tier, or layer. Multi-tenant and single-tenant refers to how client data is stored and accessed in a database.

But this article won’t talk about how to segregate client data in a database schema (or schemas) with Ruby gems, such as Apartment and Multitenancy. Instead, we’ll explain how to work around a different challenge – retrieving specific data depending on the tenant’s domain or subdomain name. We’ll present our own solution for this task, which you can use to build your own multi-tenant SaaS application.

Identifying the Tenant in a Multi-Tenant App

We often see URLs similar to these: tenant-one.webapp.com and tenant-two.webapp.com. For example, Slack allocates the rubygarage.slack.com subdomain for our RubyGarage team, and we must enter our subdomain – rubygarage – to sign in. This approach to building a multi-tenant web application can be called partial white-labeling (i.e. we don’t get our own full domain for Slack).

We commonly retrieve a tenant’s data (an entity) from Active Record using a subdomain name in our SaaS projects.

For example, we’re working on a project called Shopperations, a SaaS application designed for shopper marketers. Shopperations allows multiple tenants (companies) to separately work on their respective marketing projects. When a Shopperations user (a company representative) wants to sign in to the Shopperations application, they have to enter their subdomain.

So what happens when we enter a subdomain name and sign in? Put simply, the application determines who we are using this subdomain name and loads our data.

The responsible code grabs this subdomain and sends a request to the database. Since database schemas are set up correctly, only our data is retrieved and we are redirected to our unique page. It’s as simple as that!

How We Identified Tenants Before

In order to send a subdomain-specific request and receive appropriate data, we’ve typically employed a Ruby gem called Houser. But several months ago we decided to switch to a custom solution for a couple of reasons.

First, Houser can look for data only using subdomain names, whereas we needed to search for data using domain names as well. Second, we wanted to be able to ignore routes when sending a URL-based request to the database, and Houser didn’t let us do this.

Before building Detectify, we had to write our own code on top of Houser to implement these two features we just mentioned. But this implementation required two requests to the database. Since we wanted to be efficient – meaning we wanted to use a single request to retrieve data – and since we needed a more flexible solution, we decided to build Detectify.

What’s Detectify?

Detectify is a simple Ruby gem, similar to Houser, that helps you retrieve an Active Record entity from a database with a URL. The URL can be either a domain name or subdomain name.

It’s easy to install and set up Detectify to meet your specific needs. Below, you’ll find an explanation of what Detectify does and how exactly the gem does it. Keep in mind that Detectify is intended for use with Rack applications.

Detectify: How It Works

To use Detectify, you must first add the following line to your project’s Gemfile:

gem 'detectify'

You’ll also need to install Detectify with the standard command:

Install Detectify with the help of Bundler.

$ bundle install

Keep in mind that Detectify only supports applications built with Ruby 2.2.2 and Rails 4.2 or higher. There’s no need to install any other gems to run Detectify.

Now, let’s take a look at the short Middleware module that our developer created for Detectify.

module Detectify class Middleware def initialize(app) @app = app end def call(env) request = Rack::Request.new(env) detector = Detector.new(request.url) env['Detectify-Entity'] = detector.detect @app.call(env) end endend

Detectify’s workflow starts in the middleware.rb file. Our middleware receives a new Rack request and initializes the Detector (the main Detectify entity) with the URL returned by that Rack request. Next, Detectify sends a request to the next middleware, where the result of the initial request is actually used.

The detector.rb file is the second stop in Detectify’s workflow. The Detector class receives a URL and builds a request to the database using QueryBuilder.

require 'uri'module Detectify class Detector attr_reader :request_uri def initialize(request_url, query_builder = QueryBuilder::SQL) @request_uri = URI(request_url) @query_builder = query_builder end def detect @entity ||= Detectify.entity_class.where(*query).first unless ignore? end private def ignore? @request_uri.to_s[Regexp.union(*Detectify.config.ignore_urls)] end def query @query_builder.new(domain, subdomain).build end def domain request_uri.host end def subdomain chunks = request_uri.host.split('.') chunks[0...(1 - Detectify.config.tld_length - 2)].join end endend

For now, we have a single query builder for SQL-type databases. But the query builder implementation allows you to freely add query builders to Detectify for other types of databases.

Let’s also have a look at the QueryBuilder code.

The QueryBuilder module allows us to build a more advanced request to the database than what Active Record normally does. This is why we’ve added such functions as domain_clause and subdomain_clause to this module.

module Detectify module QueryBuilder class SQL

About the Author

Sviatoslav Andrushko is a content manager at RubyGarage. Sviat adores writing about web development, web design, testing, and other IT-related topics.

Rate this Article
Leave a Comment
Author Thumbnail
I Agree:
Comment 
Pictures
Author: Ievgen Cherkashyn

Ievgen Cherkashyn

Member since: Jun 28, 2016
Published articles: 55

Related Articles