Students Save 30%! Learn & create with unlimited courses & creative assets Students Save 30%! Save Now
Advertisement
  1. Code
  2. Ruby on Rails
Code

Elaborando APIs Con Rails

by
Length:LongLanguages:

Spanish (Español) translation by Rafael Chavarría (you can also view the original English article)

Hoy en día es una práctica común depender de APIs (application programming interfaces). No solo grandes servicios como Facebook y Twitter las emplean--Las APIs son muy populares debido a la propagación de frameworks del lado del cliente como React, Angular y muchos otros. Ruby on Rails está siguiendo esta tendencia y la última versión presenta una nueva característica permitiéndote crear aplicaciones de solo API.

Inicialmente esta funcionalidad estaba empacada en una gema separada llamada rails-api, pero desde la liberación de Rails 5, es ahora parte del núcleo del framework. Esta característica con ActionCable fue probablemente la más anticipada, así que hoy la vamos a discutir.

Este artículo cubre cómo crear aplicaciones solo-API de Rails y explica cómo estructurar tus rutas y controladores, responder con el formato JSON, agregar serializadores y configurar CORS (Cross Origin Resource Sharing). También aprenderás sobre algunas opciones para asegurar la API y protegerla de abusos.

La fuente para este artículo está disponible en GitHub.

Creando una Aplicación Solo-API

Para comenzar, ejecuta el siguiente comando:

Va a crear una nueva aplicación de Rails solo-API llamado RailsApiDemo. No olvides que el soporte para la opción --api fue agregada solo en Rails 5, así que asegúrate de que tienes esta o una versión más nueva instalada.

Abre el Gemfile y nota que es mucho más pequeño de lo habitual: gemas como coffee-rails, turbolinks y sass-rails no están.

El archivo config/application.rb contiene una nueva línea:

Significa que Rails va a cargar un conjunto más pequeño de intermediario: por ejemplo, no hay soporte de cookies ni sesiones. Más aún, si tratas de generar un andamio, las vistas y recursos no serán creados. Actualmente, si revisas el directorio views/layouts, notarás que el archivo application.html.erb también falta.

Otra diferencia importante es que ApplicationController hereda de ActionController::API, no ActionController::Base.

Esto es prácticamente todo, esta es la aplicación básica de Rails que has visto muchas veces. Ahora agreguemos un par de modelos para que tengamos algo con que trabajar:

Nada elegante está pasando aquí: una publicación con un título y un cuerpo que pertenece a un usuario.

Asegúrate de que las asociaciones apropiadas están configuradas y también proporciona revisiones simples de validación:

models/user.rb

models/post.rb

¡Brillante! El siguiente paso es cargar un par de registros de ejemplo en las tablas recién creadas.

Cargando Datos Demostrativos

La manera más fácil de cargar alguna información es utilizando el archivo seeds.rb dentro del directorio db. Sin embargo, soy flojo (como muchos programadores) y no quiero pensar en cualquier contenido de ejemplo. De ahí, porqué no sacamos ventaja de la faker gem que puede producir datos aleatorios de varios tipos: nombres, emails, palabras hipsters, textos "lorem ipsum" y mucho más.

Gemfile

Instala la gema:

Ahora modifica el seeds.rb:

db/seeds.rb

Por último, carga tus datos:

Respondiendo Con JSON

Ahora, por supuesto, necesitamos algunas rutas y controladores para elaborar nuestra API. Es una práctica común anidar las rutas de la API bajo la ruta api/. También, los desarrolladores usualmente proporcionan la versión de la API en la ruta, por ejemplo api/v1/. Después, si algunos cambios importantes han sido introducidos, puedes simplemente crear un nuevo namespace (v2) y un controlador separado.

Aquí está como pueden lucir tus rutas:

config/routes.rb

Esto genera rutas como:

Podrías usar un método scope en vez del namespace, pero entonces por defecto buscará el UsersController y PostsController dentro del directorio controllers, no dentro de controllers/api/v1, así que ten cuidado.

Crea la carpeta api con el directorio anidado v1 dentro de controllers. Complétalo con tus controladores:

controllers/api/v1/users_controller.rb

controllers/api/v1/posts_controller.rb

Nota que no solo tienes que anidar el archivo del controlador bajo la tura api/v1, sino que también la clase misma tiene que tener namespace dentro de los módulo Api y V1.

La siguiente pregunta es ¿cómo responder apropiadamente con los datos formateados en JSON? En este artículo probaremos estas soluciones: las gemas jBuilder y active_model_serializers. Así que antes de proceder a la siguiente sección, ponlos en el Gemfile:

Gemfile

Entonces ejecuta:

Usando la Gema jBuilder

jBuilder es una gema popular mantenida por el equipo de Rails que proporciona un simple DLS (domain-specific language) permitiéndote definir estructuras JSON en tus vistas.

Supón que queremos mostrar todas las publicaciones cuando el usuario inicia la acción index:

controllers/api/v1/posts_controller.rb

Todo lo que necesitas hacer es crear la vista llamada como la acción correspondiente con la extensión .json.jbuilder. Nota que la vista debe ser colocada bajo la ruta api/v1 también:

views/api/v1/posts/index.json.jbuilder

json.array! traversa el arreglo @posts. json.idjson.title y json.body generan las llaves con los nombres correspondientes estableciendo los argumentos como los valores. Si navegas a http://localhost:3000/api/v1/posts.json, verás un resultado similar a este:

¿Qué si queremos mostrar el autor de cada publicación también? Es simple:

El resultado cambiará a:

Los contenidos de los archivos .jbuilder es código plano Ruby, así que podrías utilizar todas operaciones básicas de manera usual.

Nota que jBuilder soporta parciales como cualquier vista ordinaria de Rails, así que también podrías decir:

y después crear el archivo views/api/v1/posts/_post.json.jbuilder con los siguientes contenidos:

Así qué, como ves, jBuilder es sencillo y conveniente. Sin embargo, como una alternativa, deberías apegarte a los serializadores, así que discutámoslos en la siguiente sección.

Usando Serializadores

La gema rails_model_serializers fue creada por un equipo que inicialmente administró la rails-api. Como se declara en la documentación, rails_model_serializers trae convención sobre configuración a tu generación de JSON. Básicamente, defines cuales campos deberían ser usados en la serialización (eso es, generación JSON).

Aquí está nuestro primer serializador:

serializers/post_serializer.rb

Aquí decimos que todos estos campos deberían estar presentes en el JSON resultante. Ahora métodos como to_json y as_json llamados sobre una publicación usarán esta configuración y regresarán el contenido apropiado.

Para verlo en acción, modifica la acción index así:

controllers/api/v1/posts_controller.rb

as_json será llamado automáticamente sobre el objeto @posts.

¿Y los usuarios? Los serializadores te permiten indicar relaciones, justo como hacen los modelos. Lo que es más, los serializadores pueden ser anidados:

serializers/post_serializer.rb

Ahora cuando serializas el post, este contendrá automáticamente la llave de user anidada con su id y nombre. Si después creas un serializador separado para el usuario con el atributo :id excluído:

serializers/post_serializer.rb

entonces @user.as_json no regresará el id de usuario. Aun así, @post.as_json regresará tanto el nombre y id del usuario, así que tenlo en mente.

Asegurando la API

En muchos casos, no queremos que nadie ejecute ninguna acción usando la API. Así que presentemos una revisión simple de seguridad y forcemos a los usuarios a enviar sus tokens cuando crean y borran publicaciones.

El token tendrá un ciclo de vida ilimitado y será creado sobre el registro del usuario. Primero que todo, agrega una nueva columna token a la tabla users.

Este índice debería garantizar unicidad ya que no puede haber dos usuarios con el mismo token:

db/migrate/xyz_add_token_to_users.rb

Aplica la migración:

Ahora agrega el retroceso before_save:

models/user.rb

El método privado generate_token creará un token en un ciclo interminable y revisará si es único o no. Tan pronto como un token único sea encontrado, regrésalo:

models/user.rb

Podrías usar otro algoritmo para generar el token, por ejemplo basado en el hash MD5 para el nombre de usuario y algo de salt.

Registro de Usuario

Por supuesto, también necesitamos permitir a los usuarios registrarse, porque de otro modo no serán capaces de obtener su token. No quiero introducir ninguna vista HTML en nuestra aplicación, así que en su lugar agrega un nuevo método API:

controllers/api/v1/users_controller.rb

Es una buena idea regresar códigos de estado HTTP significativos de manera que los desarrolladores entiendan exactamente qué está pasando. Ahora podrías ya sea proporcionar un nuevo serializador para los usuarios o apegarse con un archivo .json.jbuilder. Prefiero la última variante (es por eso que no paso la opción :json al método render), pero eres libre de elegir cualquiera de ellos. Nota, sin embargo, que el token no debe ser siempre serializado, por ejemplo cuando regresas una lista de todos los usuarios--¡debería ser mantenido seguro!

views/api/v1/users/create.json.jbuilder

El siguiente paso es probar si todo está trabajando propiamente. Deberías ya sea usar el comando curl o escribir algo de código Ruby. Ya que este artículo es sobre Ruby, iré con la opción de código.

Probando el Registro de Usuario

Para ejecutar una petición HTTP, emplearemos la gema Faraday, la cuál proporciona una interfaz común sobre varios adaptadores (por defecto es Net::HTTP). Crea un archivo Ruby por separado, incluye Faraday y configura el cliente:

api_client.rb

Todas estas opciones son bastante claras: elegimos el adaptador por defecto, establecemos la URL de petición a http://localhost:300/api/v1/users, cambiamos el tipo de contenido a application/json y proporcionamos el cuerpo de nuestra petición.

La respuesta del servidor va a contener JSON, así que para pasarlo usaré la gema Oj.

api_client.rb

Aparte de la respuesta analizada, también muestro el código de estado para propósitos de depuración.

Ahora puedes simplemente ejecutar este script:

y almacenar el token recibido en alguna parte--lo usaremos en la siguiente sección.

Autenticando Con el Token

Para hacer cumplir la autentificación de token, puede ser usado el método authenticate_or_request_with_http_token. Es una parte del módulo  ActionController::HttpAuthentication::Token::ControllerMethods, así que no olvides incluirlo:

controllers/api/v1/posts_controller.rb

Agrega un nuevo before_action y el método correspondiente:

controllers/api/v1/posts_controller.rb

Ahora si el token no es establecido o si un usuario con dicho token no puede ser encontrado, será regresado un error 401, interrumpiendo la ejecución de la acción.

Nota que la comunicación entre el cliente y el servidor tien que ser hecha sobre HTTPS, porque de otro modo los tokens podrían ser fácilmente falsificados. Por supuesto, la solución proporcionado no es ideal, y en muchos casos es preferible emplear el protocolo OAuth para autenticación. Hay por lo menos dos gemas que simplfican enormemente el proceso de soportat esta característica: Doorkeeper y oPRO.

Creando una Publicación

Para ver nuestra autenticación en acción, agrega la acción create al PostsController:

controllers/api/v1/posts_controller.rb

Tomamos ventaja del serializador aquí para mostrar el JSON apropiado. El @user ya fue establecido dentro de before_action.

Ahora prueba todo usando este simple código:

api_client.rb

Reemplaza el argumento pasado al token_auth con el token recibido en el registro, y ejecuta el script.

Borrando una Publicación

El borrado de una publicación es hecho de la misma manera. Agrega la acción destroy:

controllers/api/v1/posts_controller.rb

Solo permitimos a los usuarios destruir las publicaciones que les pertenecen. Si la publicación es removida satisfactoriamente, el código de estado 204 (sin contenido) será regresado. De manera alternativa, podrías responder con el id de la publicación que fue borrada ya que aún estará disponible desde la memoria.

Aquí está la pieza de código para probar esta nueva característica:

api_client.rb

Reemplaza el id de la publicación con un número que funcione para ti.

Configurando CORS

Si quieres habilitar otros servicios web para acceder a tu API (del lado del cliente), entonces CORS (Cross-Origin Resource Sharing) debería ser propiamente configurado. Básicamente, CORS permite a las aplicaciones web enviar peticiones AJAX a servicios de terceros. Afortunadamente, hay una gema llamada rack-cors que nos habilita fácilmente para configurar todo. Agrégalo al Gemfile:

Gemfile

Instálalo:

Y después proporciona la configuración dentro del archivo config/initializers/cors.rb. Actualmente, este archivo ya está creado por ti y contiene un uso de ejemplo. También puedes encontrar documentación bastante detallada en la página de la gema.

La siguiente configuración, por ejemplo, permitirá a cualquiera acceder a tu API usando cualquier método:

config/initializers/cors.rb

Previniendo Abuso

La última cosa que voy a mencionar en esta guía es cómo proteger tu API de abuso o ataques DDoS. Hay una buena gema llamada rack-attack (creada por la gente desde Kickstarter) que te permite poner en lista negra o blanca a clientes, previniendo la inundación de un servidor con peticiones, y más.

Pon la gema en Gemfile:

Gemfile

Instala:

Y después proporciona configuración dentro del archivo de inicialización rack_attack.rb La documentación de la gema lista todas las opciones disponibles y sugiere algunos casos de uso. Aquí está la muestra de la configuración que restringe a cualquiera excepto a ti de acceder al servicio y limita el número máximo de peticiones a 5 por segundo:

config/initializers/rack_attack.rb

Otra cosa que necesita ser hecha es incluir RackAttack como un intermediario:

config/application.rb

Conclusión

Hemos llegado al final de este artículo. ¡Espero que ahora te sientas más confiado sobre elaborar APIs con Rails! Nota que esta no es la única opción disponible--otra solución popular que lleva un tiempo es el framework Grape, así que podrías estar interesado en revisarlo también.

No dudes en publicar tus preguntas si algo no te pareció claro. Te agradezco por permanecer conmigo, ¡y feliz codificación!

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.