Saltar al contenido

Qué es y cómo crear una API en Ruby on Rails

Qué es una API, cómo funcionan sus recursos y end-points, y cómo construir una con Rails 4 aplicando TDD, rutas versionadas y vistas JBuilder.

7 min de lectura
  • ruby
  • rails
  • api

Publicado originalmente en Medium

Lo primero que veremos será qué es una API, qué conceptos están relacionados a ello y cómo creamos una en RoR 4 aplicando TDD, junto a algunas gemas que nos ayudarán a hacer nuestro trabajo más fácil y rápido.

De antemano os aviso que mi meta con este post no es la de crear un tutorial minucioso sobre cómo específicamente se debería crear una API, sino que le sirva como una introducción al mundo de las API's ( ◠‿◠).

Luego de trabajar con Ruby on Rails por un corto tiempo, encontré algunos artículos y tutoriales que hablaban sobre qué tan fácil era implementar una API con RoR, lo cual me llamó la atención e hizo que investigara más a fondo sobre ello. Curiosamente, 1 semana después se me encargó desarrollar una, y debo decir que de cierta forma fue una experiencia agradable gracias a la facilidad con que puedes crear grandes cosas en RoR.

Una API es una interfaz de programación de aplicaciones (del inglés API: Application Programming Interface). Es un conjunto de rutinas que provee acceso a funciones de un determinado software.

Como siempre, el concepto suena aburrido (ㆆ_ㆆ). Una API nos servirá para que diferentes aplicaciones (web y móviles) puedan comunicarse con nuestra propia aplicación. ¿Qué usan estas aplicaciones externas para consumir nuestros servicios? Una URL raíz o base que, combinada con una serie de 'end-points', parámetros o queries, devuelve un conjunto de datos planos [digo planos porque estos estarán en formatos como XML o JSON, dejando de lado HTML].

Como ejemplo tendrías un http://myapiname.com como URL raíz y como 'end-points' podríamos tener:

La imagen de abajo es un ejemplo perfecto de cómo los datos serían mostrados si se hace una petición a http://myapiname.com/users [dentro de los conceptos de una API REST, 'users' sería llamado 'recurso', que son palabras en sustantivo (¡¡no verbos!! 屮ಠ益ಠ屮) que se usan para representar a los datos; de por sí Ruby on Rails usa esta definición]. XML y JSON son los formatos más usados cuando se trata de API's [recomiendo usar JSON por tener una estructura simple, y al ser un objeto se nos será mucho más fácil usar los datos; además JSON es más rápido que XML]. El formato HTML es usado netamente para desplegar datos en páginas web.

Comparación de una respuesta en XML, JSON y HTML

Bye bye front-ends ( ˘︹˘ )…

I hate front-end developers

Para crear una API no necesitaremos hacer uso de CSS o HTML, esto porque simplemente no es necesario. Nuestra API tan solo servirá para que aplicaciones consuman nuestros servicios, y lo que estas 'aplicaciones' únicamente quieren son los datos que les interesan; no necesitan que esta data esté adornada con HTML o CSS porque, después de todo, serán ellos los que se encargarán de eso.

Una diferencia que pude percibir entre una WEB y una API es que, mientras la primera es diseñada e implementada para ser consumida y usada por usuarios finales, una API es creada para ser usada por desarrolladores y consumida por aplicaciones.

Bien, una vez que hemos entendido qué es y cómo funciona una API, veamos cómo podemos crear una API con RoR 4 [con la salida de RoR 5 se anunció que este ya viene con un comando predeterminado para crear API's], así que lo primero que haremos es crear un nuevo proyecto llamado my_api.

Para no complicarnos mucho, my_api tendrá un único recurso llamado users, el cual desplegará todos los users en formato JSON.

Creamos nuestra app en RoR con el comando:

$ rails new my_api

Luego nos vamos a la carpeta de nuestro proyecto y configuramos nuestro archivo routes.rb:

# my_api/config/routes.rb
 
Rails.application.routes.draw do
  namespace :api, defaults: { format: "json" } do
    namespace :v1 do
      resources :users
    end
  end
end

Según lo de arriba, nuestra URL base o raíz quedará de la siguiente forma:

http://localhost:3000/api/v1

Rails routes

Si lo notaron, en la línea 5 creé otro namespace [un namespace nos sirve para agregar un slug '/api/' a nuestra ruta base]; este nos servirá para el versionado de nuestra API. En este caso es 'v1' porque obviamente es la primera versión de nuestra API ლ(╹◡╹ლ). ¡Ah!, casi lo olvido: al final de la línea 4 tenemos un format: "json"; con esto le estamos indicando que, por defecto, todos los datos que nos devuelva esa ruta serán en formato JSON.

La URL para acceder a nuestro recurso users, que fue definido en la línea 6, sería la siguiente:

http://localhost:3000/api/v1/users

Bien, ahora vayamos a crear nuestro modelo User, que tendrá únicamente dos campos: name y email.

$ rails g model User name:string email:string

Vayamos a agregar unas cuantas validaciones a nuestro modelo para asegurar que los usuarios sean creados correctamente. Pero antes, ¡¡hagamos uso de TDD!! (͡° ͜ʖ ͡°), es decir, creemos primero los tests y luego nuestro código [por cierto, si aún no has leído el post en donde hablo sobre TDD en RoR, lo puedes hacer aquí (͡• ͜ʖ ͡•)]. Para ello usaré la famosa gema rspec-rails y, como complemento a esta, usaré las gemas shoulda-matchers y factory_girl_rails.

# my_api/spec/models/user_spec.rb
 
require 'rails_helper'
 
RSpec.describe User, type: :model do
  it { should validate_presence_of(:email) }
  it { should validate_presence_of(:name) }
  it { should validate_uniqueness_of(:email) }
end

Tenemos 3 tests: los dos primeros sirven para validar que el email y name siempre estén presentes en nuestro modelo, y el último para validar que nuestro email sea único, es decir, que dos usuarios no puedan tener el mismo email ლ(╹◡╹ლ).

Una vez hechas las pruebas, debemos correrlas y obviamente estas fallarán porque aún no tenemos las validaciones en nuestro modelo ლ(╹◡╹ლ).

Agreguemos las validaciones a nuestro modelo User:

# my_api/app/models/user.rb
 
class User < ActiveRecord::Base
 
  validates :email, presence: true, uniqueness: true
  validates :name, presence: true
 
end

Si corremos los tests, estos deberían pasar, así que ahora crearemos nuestro controlador. Para ello vamos a nuestra carpeta controllers y dentro creamos una carpeta llamada 'api', y dentro de esta, otra carpeta llamada 'v1' (esto para versionar nuestra API), y finalmente dentro de esta última carpeta crearemos nuestro controlador llamado users_controller.rb. ¡¡PERO ANTES!! crearemos los tests (͡° ͜ʖ ͡°), ya saben lo que dicen: test first (͡° ͜ʖ ͡°).

Estos serán nuestros tests para nuestro controlador, que únicamente tendrá un método para obtener todos los datos en formato JSON. Dentro de nuestra carpeta spec crearemos una carpeta llamada requests [el nombre hace referencia a que testearemos las peticiones o requests de nuestra API] y dentro nuestro archivo llamado user_spec.rb:

# my_api/spec/requests/users_spec.rb
 
require 'rails_helper'
 
RSpec.describe Api::V1::UsersController, type: :request do
 
  describe "GET /users" do
    before :each do
      FactoryGirl.create_list(:users, 1)
      get "/api/v1/users"
    end
 
    it { have_http_status(200) }
    it 'show all users' do
      json = JSON.parse(response.body)
      expect(json.length).to eq(User.count)
    end
  end
 
end

Y aunque lo parezca, el test de arriba es muy sencillo. Tenemos un describe (línea 7) que nos servirá para agrupar nuestra petición GET con la que obtendremos todos los usuarios. Luego de esto tenemos un before, que tiene un grupo de líneas de código que se ejecutarán antes de cada prueba; aquí creamos un usuario haciendo uso de las funcionalidades de la gema FactoryGirl [en este caso estamos creando una lista con create_list, que acepta dos parámetros: el primero será el nombre del modelo a crear y el segundo el número de registros que se crearán]. La línea 10 la agregamos para que antes de cada prueba mande una petición GET a "http://localhost:3000/api/v1/users".

Nuestro primer test (línea 13) sirve para comprobar que el status de la petición sea de 200 (es decir, que todo resultó como queremos, sin errores), y el segundo (línea 14) es para comprobar que la petición nos devolvió un JSON con la misma cantidad de objetos que hay en nuestro modelo User.

Ahora agreguemos código a nuestro controlador:

# my_api/app/controllers/api/v1/users_controller.rb
 
class Api::V1::UsersController < ApplicationController
 
  def index
    @users = User.all
  end
 
end

El código no es nada del otro mundo: tenemos una variable @users a la cual le asignamos todos los registros que hay en nuestro modelo User.

Y finalmente nuestra vista tendrá lo siguiente:

# my_api/app/views/api/v1/users/index.json.jbuilder
 
json.array! @users do |user|
  json.(user, :id, :email, :name)
end

Notarás que nuestra vista está dentro de la carpeta /views/api/v1/users, que es una estructura similar a la de nuestro controlador. El formato de nuestra vista será 'json.jbuilder', esto nos permitirá desplegar datos en formato JSON ლ(╹◡╹ლ). La sintaxis usada tampoco es nada complicada: iteramos todos los objetos que tiene nuestra variable @users y le especificamos cuáles son los campos que queremos mostrar.

Finalmente, si agregamos un par de registros a nuestro modelo User y accedemos a http://localhost:3000/api/v1/users, tendremos lo siguiente:

[
  {
    "id": 1,
    "email": "giancarlos.isasi@devacademy.la",
    "name": "giancarlos"
  },
  {
    "id": 2,
    "email": "nexuszgt@gmail.com",
    "name": "Nexus zgt"
  }
]

Finally… We did it!! (҂◡̀_◡́)ᕤ

How? Rails magic

Ruby on Rails nos permite crear cosas fabulosas de forma muy rápida, realizando el mínimo esfuerzo posible.

Aún hay muchos conceptos por aprender dentro de las API's, pero decidí pasar de ellos por no hacer el post más largo de lo que ya es. Espero que esto le haya sido de ayuda y le sirva, como lo mencioné al inicio, como una introducción al mundo de las API's.

¡Gracias por leer! (ಥ_ಥ)

Pueden revisar otros posts (҂◡̀_◡́)ᕤ

Por cierto, aquí les dejo los links de las gemas usadas en el post junto a la documentación oficial de Ruby on Rails sobre API's: