Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Desarrollando API con Rails

Desarrollando API con Rails

Como usar Rails para construir APIs

Mario Alberto Chávez

June 07, 2012
Tweet

More Decks by Mario Alberto Chávez

Other Decks in Technology

Transcript

  1. “ “ Interface de programación de aplicaciones API. Es una

    especificación para ser usada como interface por componentes de software para comunicarse entre ellos. Fuente: Wikipedia
  2. ¿PORQUÉ UN API? Aumenta la flexibilidad de un servicio Aumenta

    la utilidad de un servicio Libera los datos de usuario Proporciona valor de negocio
  3. ¿CUÁLES SON LOS RETOS? La red es un eslabón débil

    API incompleta pone estrés en el cliente Lograr un API no “platicadora”
  4. REST-ISH + JSON Interface REST es deseable, pero no debe

    ser el objetivo final. Una gran cantidad de API exponen datos en JSON
  5. “ “ Transferencia de estado representacional REST. Diseño sencillo para

    sistemas distribuidos, predominante en servicios web. Ayuda a desacoplar el cliente del servidor. Fuente: Wikipedia
  6. REST ⋍ CRUD Recursos a través de URI Uso de

    verbos HTTP POST /api/users #Crear GET /api/users/2 #Ver PUT /api/users/2 #Actualizar GET /api/users #Listado DELETE /api/users/2 #Eliminar
  7. RECURSO Cualquier cosa expuesta mediante web Tienen una representación xml

    json Con un servicio web intercambiamos representaciones de recursos
  8. RESTRICCIONES Representar recurso como json Aceptamos json y x- form

    encode Autentificacion HTTP Básica/OAuth { "public_gists": 29, "following": 26, "type": "User", "blog": "http://www.decisionesinteligentes.com", "hireable": false, "avatar_url": "https://secure.gravatar.com/avatar/ 2ae35d2e0dfd4ca72c07ead1eec4a8f7?d=https:// a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars %2Fgravatar-140.png", "url": "https://api.github.com/users/mariochavez", "bio": null, "public_repos": 42, "html_url": "https://github.com/mariochavez", "company": "Crowd Interactive", "gravatar_id": "2ae35d2e0dfd4ca72c07ead1eec4a8f7", "login": "mariochavez", "name": "Mario Alberto Chavez", "email": "[email protected]", "id": 59967, "followers": 27, "created_at": "2009-03-04T04:46:01Z", "location": "San Francisco, CA" }
  9. ENTONCES REST pone énfasis en las acciones sobre un recurso

    El problema es la representación del recurso: API fácil ... pragmático Hay que ser consistente Reducir llamadas al server, recursos denormalizados
  10. FACADE API no es CRUD directo a SQL Facade es

    una cara de tu sistema: Hay que pensar en servicios y no en objetos Diseñamos a partir de una UI
  11. ¿API CON RAILS? No es la primera opción. Dicen que

    Rails es demasiado La gente prefiere Sinatra.rb O usar Grape con Rack
  12. PERO... Rails ya nos da REST por default Nos da

    un patrón común y conocido ... MVC No ofrece buena seguridad ¿Tenemos que perder estos beneficios?
  13. NO!

  14. RAILS A DIETA Rails es modular Para API cierto Middleware

    no es necesario No cargar todos los módulos de controlador ¿Pero como?
  15. use ActionDispatch::Static use Rack::Lock use #<ActiveSupport::Cache::Strategy::Loc alCache::Middleware:0x007fd3b32928c0> use Rack::Runtime use

    Rack::MethodOverride use ActionDispatch::RequestId use Rails::Rack::Logger use ActionDispatch::ShowExceptions use ActionDispatch::DebugExceptions use ActionDispatch::RemoteIp use ActionDispatch::Reloader use ActionDispatch::Callbacks use ActionDispatch::Cookies use ActionDispatch::Session::CookieStore use ActionDispatch::Flash use ActionDispatch::ParamsParser use ActionDispatch::Head use Rack::ConditionalGet use Rack::ETag use ActionDispatch::BestStandardsSupport use Rack::Mongoid::Middleware::IdentityMa p use ActionDispatch::Static use Rack::Lock use #<ActiveSupport::Cache::Strategy::LocalCac he::Middleware:0x007fe74448cf50> use Rack::Runtime use ActionDispatch::RequestId use Rails::Rack::Logger use ActionDispatch::ShowExceptions use ActionDispatch::DebugExceptions use ActionDispatch::RemoteIp use ActionDispatch::Reloader use ActionDispatch::Callbacks use ActiveRecord::ConnectionAdapters::Connecti onManagement use ActiveRecord::QueryCache use ActionDispatch::ParamsParser use ActionDispatch::Head use Rack::ConditionalGet use Rack::ETag
  16. <Module:0x007ff271221e40>, ActionDispatch::Routing::Helpers, #<Module:0x007ff2714ad268>, ActionController::Base, ActionDispatch::Routing::RouteSet::MountedHelpers, HasScope, ActionController::Compatibility, ActionController::ParamsWrapper, ActionController::Instrumentation, ActionController::Rescue,

    ActiveSupport::Rescuable, ActionController::HttpAuthentication::Token::ControllerMethods, ActionController::HttpAuthentication::Digest::ControllerMethods, ActionController::HttpAuthentication::Basic::ControllerMethods, ActionController::RecordIdentifier, ActionController::DataStreaming, ActionController::Streaming, ActionController::ForceSSL, ActionController::RequestForgeryProtection, AbstractController::Callbacks, ActiveSupport::Callbacks, ActionController::Flash, ActionController::Cookies, ActionController::MimeResponds, ActionController::ImplicitRender, ActionController::Caching, ActionController::Caching::Fragments, ActionController::Caching::ConfigMethods, ActionController::Caching::Pages, ActionController::Caching::Actions, ActionController::ConditionalGet, ActionController::Head, ActionController::Renderers::All, ActionController::Renderers, ActionController::Rendering, ActionController::Redirecting, ActionController::RackDelegation, ActiveSupport::Benchmarkable, AbstractController::Logger, ActionController::UrlFor, AbstractController::UrlFor, ActionDispatch::Routing::UrlFor, ActionDispatch::Routing::PolymorphicRoutes, ActionController::HideActions, ActionController::Helpers, AbstractController::Helpers, AbstractController::AssetPaths, AbstractController::Translation, AbstractController::Layouts, AbstractController::Rendering, AbstractController::ViewPaths, ActionController::Metal, AbstractController::Base, ActiveSupport::Configurable, Object, ActiveSupport::Dependencies::Loadable, Mongoid::Extensions::Object::Yoda, Mongoid::Extensions::Object::Substitutable, Mongoid::Extensions::Object::Reflections, Mongoid::Extensions::Object::DeepCopy, Mongoid::Extensions::Object::Checks, JSON::Ext::Generator::GeneratorMethods::Object, PP::ObjectMixin, Kernel, BasicObject #<Module:0x007f9211d5cd70>, ActionDispatch::Routing::Helpers, #<Module:0x007f9211f7b5e8>, ActionController::API, ActiveRecord::Railties::ControllerRuntime, ActionDispatch::Routing::RouteSet::MountedHelpers, ActionController::Instrumentation, ActionController::Rescue, ActiveSupport::Rescuable, ActionController::DataStreaming, ActionController::ForceSSL, AbstractController::Callbacks, ActiveSupport::Callbacks, ActionController::ConditionalGet, ActionController::Head, ActionController::Renderers::All, ActionController::Renderers, ActionController::Rendering, AbstractController::Rendering, AbstractController::ViewPaths, ActionController::Redirecting, ActionController::RackDelegation, ActiveSupport::Benchmarkable, AbstractController::Logger, ActionController::UrlFor, AbstractController::UrlFor, ActionDispatch::Routing::UrlFor, ActionDispatch::Routing::PolymorphicRoutes, ActionController::HideActions, ActionController::Metal, AbstractController::Base, ActiveSupport::Configurable, Object, JSON::Ext::Generator::GeneratorMethods::Object, ActiveSupport::Dependencies::Loadable, PP::ObjectMixin, Kernel, BasicObject
  17. API FACADE EN RAILS Al usar rails-api: Separamos el API

    de la APP principal Usamos controladores específicos Es buena idea usar namespaces
  18. ¿Y EL RECURSO? ActiveRecord nos ofrece .to_json Es muy simple

    Funciona bien cuando el recurso “mapea” a un modelo Cuando no, se vuelve complejo
  19. @posts.to_json( :only => [:title, :body, :created_at, :tags, :category], :include =>

    [ :likes => { :only => [:created_at], :include => [:author] }, :comments => { only => [:created_at, :body], :include => [:author] }, :user => { :only => [:first_name, :last_name}, :methods => [:full_name] }, :methods => [:likes_count, :comments_count]) Podemos ocultar haciendo override al método .as_json
  20. class PostSerializer < ActiveModel::Serializer attributes :id, :body attribute :title, :key

    => :name has_many :comments def tags tags.order :name end end Funciona transparente con .to_json Útil cuando el recurso mapea al modelo
  21. TEMPLATES .JSON Generar json mediante vistas RABL (@nesquena) y JBuilder

    (@DHH) gem install rabl gem install jbuilder Flexibles para generar json complejos Ambas proveen un DSL
  22. collection @posts attributes :id, :body attribute :title => :name child(:comments)

    { attributes :id, :comment } node(:new) { |post| post.created_at > 24.hours.ago } /app/views/posts/index.rabl render :index /api/posts.json
  23. TEMPLATES .JSON Usar presenters, exhibitors o decoradores simplifica templates Muy

    útiles cuando el recurso no “mapea” a modelo de objetos
  24. def index posts = Post.all @post_presenter = PostPresenter.new posts, self

    render :index end El decorador puede contener lógica compleja Cálculos que solo se presentan con el API Es posible manejar múltiples versiones con presenters
  25. class PostRepresenter < Representer::Base namespace :post attributes :id, :body fields

    :name def name hash “#{hash[‘title’]” end end posts = Post.limit 10 representer = PostRepresenter.new posts represnter.render :json Representer::Base convierte objetos AR a hashes
  26. class PostRepresenter < Representer::Simple attributes :id, :body fields :name def

    name record record.title end end posts = Post.limit 10 representer = PostRepresenter.new posts represnter.render :json Representer::Simple trabaja con objetos AR
  27. class PostRepresenter < Representer::Ligthing attributes :id, :body fields :name def

    name record record.title end end posts = Post.limit 10 representer = PostRepresenter.new posts represnter.render :json Representer::Ligthing hace llamada directa en SQL
  28. VERSIONES Hay 2 estrategias principalmente: Incluir la version en la

    url /api/v2/users.json Usan el Accept Header Accept: application/vnd.mycompany.com; version=2,application/json
  29. SI!