Renderizar JSON con Rails y Active Model Serializer

Anderson Sánchez
Academia Hack
Published in
5 min readJul 10, 2019

Entreguemos la data desde nuestra API de una manera más custom.

Las API simplifican el desarrollo de las aplicaciones, lo cual permite ahorrar tiempo a los desarrolladores y dinero a las empresas. Cuando diseña herramientas y productos nuevos (o maneja otros actuales), las API le otorgan flexibilidad; simplifican el diseño, la administración y el uso; y proporcionan oportunidades para la innovación.

Photo by Jiroe on Unsplash

Hoy en día existen diversas maneras cuando se trata de servir data desde un API. Podría realizarse en Node con ExpressJS, en Go con Martini, entre otros. Pero en muchos casos se requiere desarrollar algo tan rápido y personalizado como sea posible, para esos casos mi elección es Ruby on Rails.

Esto debido, entre otras cosas, a que renderizar data en formato JSON es tan fácil como realizar un render :json. Por cierto, ¿que es JSON?.

JSON (JavaScript Object Notation) es un formato que puede ser utilizado para guardar o intercambiar data. Es fácil de leer para los humanos y fácil de analizar por las máquinas, por lo cual muchas APIS lo utilizan.

Estructura típica de un JSON por https://www.w3resource.com/JSON/introduction.php

Sin embargo, fuera de la caja, Rails sirve la data de una manera “fea”. En este artículo vamos a aprender a crear respuestas personalizadas JSON con ActiveModel::Serializer. Creando respuestas JSON en Rails es sumamente fácil, pero utilizando las ventajas por defecto del framework no es suficiente, además de no poder probarse de una manera sencilla.

Esta gema nos permite darle formato y organización a nuestra data JSON. Podemos seleccionar solo la información que queremos, además de tener acceso a las asociaciones de un objeto, todo al resolver una simple solicitud HTTP. Veamos tan solo este simple ejemplo y comparemos los resultados:

Standard render json en Rails

Acá podemos ver como se muestra data que puede no ser relevante como lo son “created_at”, “updated_at” e incluso un atributo que podría ser sensible como lo es el “id”.

Ejemplo de render json con ActiveModelSerializers

Y acá podemos observar como se filtra la data innecesaria de estos objetos, para solo mostrar el atributo que nos interesa.

Creando los archivos comunes

Para la utilización de un Serializer, debes tener definidos esto por cada recurso que lo requieras:

  • Rutas
  • Migraciones y Modelos
  • Controladores
  • Algunos registros desde seeds

Instalación

Luego que termines de configurar tu proyecto Rails, solo debes añadir la gema serializer a tu Gemfile, gem 'active_model_serializers', '~> 0.10.0' , y luego correr bundle install . Gracias a Rails y su magia, eso es todo lo que se necesita.

Generando un archivo serializer

Podemos utilizar el comando rails g serializer customer , o podemos crear la carpeta app/serializers y luego el archivo app/serializers/customer_serializer.rb, adentro debemos tener algo como esto:

class CustomerSerializer < ActiveModel::Serializer  attributes :name, :lastname, :address, :phone, :country_idend

Ya que instalamos Serializer, Rails ahora buscará dicha carpeta antes de renderizar un recurso. Cada modelo necesita su archivo serializer correspondiente. Esto es debido a que cuando se le pase un objeto a render :json en el controller, ahora dependerá del Serializer cuales atributos realmente se van a mostrar.

Se muestran los atributos definidos en el serializer de dicho recurso.

De esta manera, al recibir una solicitud del listado de clientes, al renderizar los objetos en formato JSON, solo se mostrarán los atributos que acá se definan. Por defecto este será el mismo comportamiento en los otros métodos definidos en el controller(show, create, update, destroy).

¿Que tal si realizas el serializer del recurso Book?

¿Eso resulto bastante bien o no?. Sin embargo, si nos fijamos detenidamente en el JSON resultado de Customer, tenemos el atributo "country_id": 3 , lo cual es bastante ambiguo si continuamos con nuestro objetivo de organizar y mostrar la data de una mejor manera.

Trayendo las asociaciones de un recurso

En el serializer se puede definir mostrar los datos de los recursos asociados:

class CustomerSerializer < ActiveModel::Serializerattributes :name, :lastname, :address, :phonebelongs_to :countryend

Se buscarán y mostrarán los atributos que estén definidos en el serializer de Country, por lo cual debemos crearlo.

class CountrySerializer < ActiveModel::Serializer  attributes :nameend

Así se muestra ahora:

Se crea una clave “country” cuyo valor es el objeto asociado y sus atributos.

De esta manera se puede crear un objeto JSON “anidado” dentro de otro objeto JSON, los cuales están relacionados a través de la clave foránea.

Tal como acabamos de observar como obtener los datos de un recurso asociado a través de belongs_to , se puede realizar con has_one o con has_many , por ejemplo, en el caso contrario:

class CountrySerializer < ActiveModel::Serializer  attributes :name  has_many :customersend
Clientes nacidos en Portugal.

En este caso obtenemos una colección representada por un arreglo de objetos JSON, cada uno de esos objetos tiene sus atributos(name). Uno de esos atributos es “customers” cuyo valor es un arreglo de objetos.

Definiendo métodos propios

En muchas ocasiones queremos mostrar la data a nuestra propia manera, esto debido a lógica de negocios. Para ello podemos realizar métodos en cada uno de los serializers. Por ejemplo, si queremos mostrar el nombre completo de un “customer”:

class CustomerSerializer < ActiveModel::Serializer  attributes :full_name  def full_name
"{#{object.name}#{object.lastname}}"
end
end

Definimos el método full_name, y dentro de el se retorna un string con llaves donde se está interpolando el nombre y el apellido del objeto. Tenemos acceso a el a través de object .

¿Sin embargo, que es ese objeto?

Ese objeto hace referencia a la instancia de CustomerSerializer la cual tiene un atributo llamado object, el cual es el objeto JSON que se esté serializando en ese momento. Digo esto por que el Serializer itera sobre cada objeto que se le haya dado al ser una colección(index) y hace lo mismo al ser solo uno(show).

¿Que tal si realizas un método propio donde se muestre la data como tu quieras?

Para finalizar, debemos saber que la gema ActiveModelSerializers permite una serie de opciones que pueden ser utilizadas dependiendo de lo que se requiera, las podemos encontrar aquí.

--

--