Stay Ahead in Ruby!
From development processes to the most useful gems, get it all straight in your inbox. Join our Ruby blog today!

Skip to main content

Efficient JSON Serialization with Blueprinter for Ruby on Rails

JSON serialization with Blueprinter gem - cover image

Serializing data to JSON is an essential task for most Ruby on Rails applications, but it can quickly become a time-consuming and error-prone process as your application grows in complexity.

Serialization in Rails can be accomplished using a variety of techniques, including the as_json method on ActiveRecord models, custom serializers, or third-party gems like Blueprinter. The goal of serialization is to provide a consistent, well-structured format for transmitting data between different parts of a Rails application or between the application and external systems.

In this article, we’ll explore the key features and benefits of using Blueprinter gem for JSON serialization in Rails. We’ll dive into some real-world examples of how to use the gem to serialize complex data structures, and we’ll compare Blueprinter to other popular serialization libraries. By the end of this article, you’ll have a solid understanding of how Blueprinter can help you streamline your serialization workflow and write more maintainable code.

What is Blueprinter? #

Blueprinter is a serialization library for creating JSON APIs in Ruby on Rails applications. It allows developers to define the structure of the JSON response from the Rails API, providing a way to generate a schema for the API response. This gem can be particularly useful when dealing with complex JSON objects that require nested or conditional serialization.

With Blueprinter, you can define a blueprint(schema) that specifies how to serialize a particular model or any other data. A blueprint consists of a set of rules that define how the object should be serialized, including which attributes to include, how to format them, and any relationships with other objects.

Main advantages #

Blueprinter has several advantages that make it a popular choice for developers looking for a simple and flexible way to serialize JSON responses in Ruby on Rails applications. Here are some of the main advantages of Blueprinter:

  • Easy to use: Blueprinter is a simple and easy-to-use serialization library that requires minimal setup and configuration. The syntax for defining blueprints is straightforward and easy to understand, making it a great choice for developers who are new to serialization or who need to quickly implement a serialization solution.

  • Flexible: Blueprinter provides a high degree of flexibility when it comes to defining the structure of your JSON response. It allows you to define blueprints for any type of object or data structure, and it provides a wide range of customization options for specifying which attributes to include, how to format them, and how to handle relationships with other objects.

  • Lightweight: Blueprinter is a lightweight gem that doesn’t require a lot of dependencies or memory overhead. This makes it a great choice for applications that need to generate JSON responses quickly and efficiently.

  • Fast: Blueprinter is designed to be fast and efficient, with a focus on minimizing the overhead associated with serialization. It uses a simple and optimized codebase to ensure that JSON responses are generated quickly and with minimal impact on application performance.

Overall, Blueprinter is a solid choice for developers looking for a lightweight, flexible, and easy-to-use serialization library for their Ruby on Rails applications.

Disadvantages #

Here are some potential disadvantages of using the Blueprinter gem:

  • Lack of support for advanced features: Blueprinter is designed to be a lightweight and easy-to-use solution for generating JSON responses, it doesn’t offer the same level of advanced features as some other serialization libraries. For example, it doesn’t have built-in support for pagination or caching, which may be important in some use cases.

  • Limited ecosystem: Blueprinter has a growing community of users and contributors, but it’s not as widely used as some other serialization libraries like Active Model Serializers or Jbuilder. This means that you may not be able to find as many resources or examples for working with Blueprinter, and you may need to rely more on the gem’s documentation (which is actually pretty good) and community support.

  • Potential performance issues: While Blueprinter is generally fast and efficient at generating JSON responses, there are some situations where it may introduce performance issues. For example, if you’re working with large data sets or complex data models, Blueprinter may be slower than other serialization libraries.

It’s worth noting that these potential disadvantages may not be significant issues for every use case, and many developers have found Blueprinter to be a reliable and efficient solution for JSON serialization in Ruby on Rails applications.

Alternatives #

There are several alternatives to Blueprinter for Ruby on Rails, depending on your needs and preferences. Here are some popular ones:

  • JSONAPI-rb: It is a more verbose and explicit serialization library designed specifically for the JSON API standard, while Blueprinter is a more lightweight and flexible serialization library with better customization options and performance for smaller data sets.

  • Jbuilder: This is a built-in Rails gem that allows you to generate JSON responses using a DSL (domain-specific language) for building JSON objects. Jbuilder provides a simple and intuitive way to define the structure of your JSON response using Ruby code.

Each of these alternatives has its own strengths and weaknesses, and the best choice for your project will depend on your specific requirements and preferences.

Usage examples #

The process of adding Blueprinter to your Ruby on Rails application is the same as for any other gem.

Add the gem to your Gemfile: Open your application’s Gemfile and add the following line:

gem 'blueprinter'

Install the gem: Run the following command in your terminal to install the gem:

bundle install

Create a blueprint #

Create a new file in your app/blueprints directory with the following naming convention: {model_name}_blueprint.rb. For example, if you want to create a blueprint for a User model, you would create a file called user_blueprint.rb. Inside this file, you can define the structure of your JSON response using the blueprint DSL.

class UserBlueprint < Blueprinter::Base
    identifier :id
    fields :name, :email
end
Tip: Of course, you can create a different directory and different file names. The most important thing is that your class needs to be inherited from Blueprinter::Base.

Serialize your data #

Once you have defined your blueprint, you can use it to serialize your data by calling the render method on the blueprint class. For example, if you want to serialize a collection of User objects, you can do the following in your controller:

class UsersController < ApplicationController
    def index
        @users = User.all
        render json: UserBlueprint.render(@users)
    end
end

This will generate a JSON response that includes the id, name, and email fields for each user in the collection.

Click here to see the JSON result
[
  {
    "id": "53",
    "name": "John Doe",
    "email": "john.doe@mail.com"
  },
  {
    "id": "54",
    "name": "Kate Doe",
    "email": "kate.doe@mail.com"
  }
]

More complex examples #

Customizing field names #

Blueprinter allows you to customize the names of individual fields in your JSON response. For example, let’s say you want to rename the email field to user_email. You can define a blueprint like this:

class UserBlueprint < Blueprinter::Base
    identifier :id
    field :name
    field :email, as: :user_email
end

In this example, the UserBlueprint includes a field block for the name field that uses the default field name (i.e., the name of the field on the User model). The UserBlueprint also includes a field block for the email field that specifies an alternate name (user_email) using the as option.

Click here to see the JSON result
{
  "id": "53",
  "name": "John Doe",
  "user_email": "john.doe@mail.com"
}

Customizing field formatting #

Blueprinter allows you to customize the formatting of individual fields in your JSON response. For example, let’s say you want to format a created_at field in ISO 8601 format. You can define a blueprint like this:

class UserBlueprint < Blueprinter::Base
    identifier :id
    fields :name, :email,
    field :created_at do |user|
        user.created_at.iso8601
    end
end

In this example, the UserBlueprint includes a field block for the created_at field that specifies a block to be used to format the output. The block takes a single argument (the User object being serialized) and returns the formatted value. In this case, we are using the iso8601 method to format the created_at value in ISO 8601 format.

Click here to see the JSON result
{
  "id": "53",
  "name": "John Doe",
  "email": "john.doe@mail.com",
  "created_at": "2023-04-20T12:28:24Z"
}

Handling relationships #

Blueprinter allows you to handle relationships between models in your JSON response. For example, let’s say you have a User model that has many Post models. You can define a blueprint for the User model that includes the associated Post objects like this:

class UserBlueprint < Blueprinter::Base
    identifier :id
    fields :name, :email
    association :posts, blueprint: PostBlueprint
end

class PostBlueprint < Blueprinter::Base
    identifier :id
    fields :title, :body
end

In this example, the UserBlueprint includes an association block that specifies the posts association on the User model, and specifies that the PostBlueprint should be used to serialize the associated objects.

When you render a collection of User objects with this blueprint, the resulting JSON response will include an array of Post objects for each user.

Click here to see the JSON result
[
  {
  "id": "53",
  "name": "John Doe",
  "email": "john.doe@mail.com",
  "posts": [
    {
      "id": "12",
      "title": "First post",
      "body": "The text of the first post"
    },
    {
      "id": "13",
      "title": "Second post",
      "body": "The text of the second post"
    }
  ]
  },
  {
    "id": "54",
    "name": "Kate Doe",
    "email": "kate.doe@mail.com",
    "posts": [
      {
        "id": "15",
        "title": "First post from Kate",
        "body": "The text of the first post"
      },
      {
        "id": "15",
        "title": "Second post from Kate",
        "body": "The text of the second post"
      }
    ]
  }
]

Handling nested relationships #

Blueprinter allows you to handle nested relationships between models in your JSON response. For example, let’s say you have a User model that has many Post models, and each Post has many Comment models. You can define a blueprint for the User model that includes the associated Post objects, and a blueprint for the Post model that includes the associated Comment objects like this:

class UserBlueprint < Blueprinter::Base
    identifier :id
    fields :name, :email
    association :posts, blueprint: PostBlueprint
end

class PostBlueprint < Blueprinter::Base
    identifier :id
    fields :title, :body
    association :comments, blueprint: CommentBlueprint
end

class CommentBlueprint < Blueprinter::Base
    identifier :id
    fields :body
end

In this example, the UserBlueprint includes an association block that specifies the posts association on the User model, and specifies that the PostBlueprint should be used to serialize the associated objects. Similarly, the PostBlueprint includes an association block that specifies the comments association on the Post model, and specifies that the CommentBlueprint should be used to serialize the associated objects.

When you render a collection of User objects with this blueprint, the resulting JSON response will include nested arrays of Post and Comment objects for each user.

Click here to see the JSON result
[
  {
    "id": "53",
    "name": "John Doe",
    "email": "john.doe@mail.com",
    "posts": [
      {
        "id": "12",
        "title": "First post",
        "body": "The text of the first post",
        "comments": [
          {
            "id": "1",
            "body": "Good!"
          },
          {
            "id": "2",
            "body": "Perfect!"
          }
        ]
      },
      {
        "id": "13",
        "title": "Second post",
        "body": "The text of the second post",
        "comments": [
          {
            "id": "3",
            "body": "Amazing!"
          },
          {
            "id": "4",
            "body": "Perfect!"
          }
        ]
      }
    ]
  },
  {
    "id": "54",
    "name": "Kate Doe",
    "email": "kate.doe@mail.com",
    "posts": [
      {
        "id": "15",
        "title": "First post from Kate",
        "body": "The text of the first post",
        "comments": [
          {
            "id": "5",
            "body": "Amazing!"
          },
          {
            "id": "6",
            "body": "Perfect!"
          }
        ]
      },
      {
        "id": "15",
        "title": "Second post from Kate",
        "body": "The text of the second post",
        "comments": [
          {
            "id": "7",
            "body": "Amazing!!!!"
          },
          {
            "id": "8",
            "body": "Perfect!!!!!"
          }
        ]
      }
    ]
  }
]

Different outputs based on views #

You can have a situation when you need to have two, almost identical serializers for one model. For example, you have a User model and in one situation you need to return short version of User data and in another situation you need more complex User data.

class UserBlueprint < Blueprinter::Base
    identifier :id
    field :name, :email
    view :base do
        fields :first_name, :last_name
    end
    view :extended do
        include_view :base
        field :address
        association :posts
    end
end

# then inside controller

puts UserBlueprint.render(user, view: :extended)
Click here to see the JSON result
{
  "id": "53",
  "name": "John Doe",
  "email": "john.doe@mail.com",
  "first_name": "John",
  "last_name": "Doe",
  "address": "Main str 5, CoolCity, Country",
  "posts": [
    {
      "id": "12",
      "title": "First post",
      "body": "The text of the first post"
    },
    {
      "id": "13",
      "title": "Second post",
      "body": "The text of the second post"
    }
  ]
}

A view can include fields from another view by utilizing include_view and include_views.

Customizing output based on context #

Blueprinter allows you to customize the output of your JSON response based on the context in which it is being rendered. render takes an options hash which you can pass additional properties. For example, let’s say you want to include different fields in the JSON response based on whether the user making the request is an admin or a regular user. You can define a blueprint like this:

class UserBlueprint < Blueprinter::Base
    identifier :id
    if lambda { |options| options[:admin] }
        fields :name, :email, :admin_notes
    else
        fields :name, :email
    end
end

# then inside controller

puts UserBlueprint.render(user, admin: true)

In this example, the UserBlueprint includes an if block that checks whether the admin option is set in the rendering context. If it is, the blueprint includes the admin_notes field in the output; otherwise, it excludes it.

Click here to see the JSON result
{
  "id": "53",
  "name": "John Doe",
  "email": "john.doe@mail.com",
  "admin_notes": "Some important notes"
}

Customizing output based on conditionals #

Blueprinter allows you to customize the output of your JSON response based on conditional logic. For example, let’s say you want to include the email field in the JSON response only if it is present. You can define a blueprint like this:

class UserBlueprint < Blueprinter::Base
    identifier :id
    field :name
    field :email, if: lambda { |user| user.email.present? }
end

In this example, the UserBlueprint includes a field block for the name field as before, and a field block for the email field that includes the field only if the email attribute on the User object is present.

Click here to see the JSON result
{
  "id": "53",
  "name": "John Doe"
}

Summary #

In conclusion, Blueprinter is a great serialization library that can help you streamline your API development process and improve the performance of your application. With its simple syntax and flexible customization options, it’s easy to use and can be adapted to fit a wide variety of use cases. While it may not be the best choice for every application, it’s definitely worth considering if you value speed, simplicity, and flexibility in your code.

Also, the big plus is that Blueprinter has a really good documentation with a lot of examples, so it’s easy to find what you are looking for.

So why not give Blueprinter a try and see how it can help you build better APIs faster?

We are ready to provide expert's help with your product
or build a new one from scratch for you!

Contact MobiDev’s tech experts!