building an api in rails without realizing it

Post on 05-Dec-2014

2.477 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

Presented at Confoo (Montreal, Canada) on 3/1/2013 In this talk I’ll show you how to build your application and get a working, well tested, and useable API in the process, with almost no extra overhead. I’ll also show you how to do it without making your controllers a mess of respond_to blocks. If anything, you’re controllers will become cleaner and leaner.

TRANSCRIPT

Rails APIsMonday, February 25, 13

@markbates

Monday, February 25, 13

Monday, February 25, 13

Monday, February 25, 13

http://www.metacasts.tv

CONFOO2013

Monday, February 25, 13

Rails APIsMonday, February 25, 13

Rails APIsMonday, February 25, 13

Rails APIsWeb

Monday, February 25, 13

you’re probably doing it wrong

Monday, February 25, 13

Don’t Panic!

Monday, February 25, 13

most APIs are an after thought

Monday, February 25, 13

we can prevent that!

Monday, February 25, 13

3 simple rules for building an API

Monday, February 25, 13

1. consume your own API

Monday, February 25, 13

2. document your API

Monday, February 25, 13

3. version your API

Monday, February 25, 13

building the API

Monday, February 25, 13

SOAMonday, February 25, 13

Service Oriented Architecture

Monday, February 25, 13

SOA Pros

Monday, February 25, 13

SOA Pros

• Scales Easily

Monday, February 25, 13

SOA Pros

• Scales Easily

• Separate Concerns/Very Clean

Monday, February 25, 13

SOA Pros

• Scales Easily

• Separate Concerns/Very Clean

• Can be easier to maintain

Monday, February 25, 13

SOA Pros

• Scales Easily

• Separate Concerns/Very Clean

• Can be easier to maintain

• Solid Architecture

Monday, February 25, 13

SOA Pros

• Scales Easily

• Separate Concerns/Very Clean

• Can be easier to maintain

• Solid Architecture

• Easier to refactor/rebuild

Monday, February 25, 13

SOA Cons

Monday, February 25, 13

SOA Cons

• Can be more difficult to maintain

Monday, February 25, 13

SOA Cons

• Can be more difficult to maintain

• More complex deploys

Monday, February 25, 13

SOA Cons

• Can be more difficult to maintain

• More complex deploys

• Managing several applications

Monday, February 25, 13

SOA Cons

• Can be more difficult to maintain

• More complex deploys

• Managing several applications

• Potential for ‘out-of-sync’ apps

Monday, February 25, 13

SOA Cons

• Can be more difficult to maintain

• More complex deploys

• Managing several applications

• Potential for ‘out-of-sync’ apps

• More difficult to test integration

Monday, February 25, 13

API Client

Monday, February 25, 13

API

Client

Client

Monday, February 25, 13

API Client

Client

Client

Monday, February 25, 13

Client

Client

Client

Service 1

Service 2

Service 3

Monday, February 25, 13

a quick detour

Monday, February 25, 13

Rails encourages poor API design!

Monday, February 25, 13

Monday, February 25, 13

class  TodosController  <  ApplicationController    #  GET  /todos    #  GET  /todos.json    def  index        @todos  =  Todo.all          respond_to  do  |format|            format.html  #  index.html.erb            format.json  {  render  json:  @todos  }        end    end      #  GET  /todos/1    #  GET  /todos/1.json    def  show        @todo  =  Todo.find(params[:id])          respond_to  do  |format|            format.html  #  show.html.erb            format.json  {  render  json:  @todo  }        end    end      #  GET  /todos/new    #  GET  /todos/new.json    def  new        @todo  =  Todo.new          respond_to  do  |format|            format.html  #  new.html.erb            format.json  {  render  json:  @todo  }        end    end      #  GET  /todos/1/edit    def  edit        @todo  =  Todo.find(params[:id])    end

   #  POST  /todos    #  POST  /todos.json    def  create        @todo  =  Todo.new(params[:todo])          respond_to  do  |format|            if  @todo.save                format.html  {  redirect_to  @todo,  notice:  'Todo  was  successfully  created.'  }                format.json  {  render  json:  @todo,  status:  :created,  location:  @todo  }            else                format.html  {  render  action:  "new"  }                format.json  {  render  json:  @todo.errors,  status:  :unprocessable_entity  }            end        end    end      #  PUT  /todos/1    #  PUT  /todos/1.json    def  update        @todo  =  Todo.find(params[:id])          respond_to  do  |format|            if  @todo.update_attributes(params[:todo])                format.html  {  redirect_to  @todo,  notice:  'Todo  was  successfully  updated.'  }                format.json  {  head  :no_content  }            else                format.html  {  render  action:  "edit"  }                format.json  {  render  json:  @todo.errors,  status:  :unprocessable_entity  }            end        end    end      #  DELETE  /todos/1    #  DELETE  /todos/1.json    def  destroy        @todo  =  Todo.find(params[:id])        @todo.destroy          respond_to  do  |format|            format.html  {  redirect_to  todos_url  }            format.json  {  head  :no_content  }        end    endend

Monday, February 25, 13

Don’t Scaffold!

Monday, February 25, 13

Monday, February 25, 13

class  TodosController  <  ApplicationController      def  index        @todos  =  Todo.all    end      def  show        @todo  =  Todo.find(params[:id])    end      def  new        @todo  =  Todo.new    end      def  edit        @todo  =  Todo.find(params[:id])    end      def  create        @todo  =  Todo.new(params[:todo])        if  @todo.save            redirect_to  @todo,  notice:  'Todo  was  successfully  created.'        else            render  action:  "new"        end    end      def  update        @todo  =  Todo.find(params[:id])          if  @todo.update_attributes(params[:todo])            redirect_to  @todo,  notice:  'Todo  was  successfully  updated.'        else            render  action:  "edit"        end    end      def  destroy        @todo  =  Todo.find(params[:id])        @todo.destroy        redirect_to  todos_path,  notice:  'Todo  was  successfully  destroyed.'    endend

Monday, February 25, 13

class  TodosController  <  ApplicationController    inherit_resourcesend

Monday, February 25, 13

https://github.com/josevalim/inherited_resources

Inherited Resources

Monday, February 25, 13

Monday, February 25, 13

class  Api::V1::TodosController  <  ApplicationController    respond_to  :json      before_filter  do        request.format  =  :json    end      def  index        @todos  =  Todo.all        respond_with  @todos    end      def  show        @todo  =  Todo.find(params[:id])        respond_with  @todo    end      def  create        @todo  =  Todo.new(params[:todo])        if  @todo.save            respond_with  @todo        else            render  json:  @todo.errors,  status:  :unprocessable_entity        end    end      def  update        @todo  =  Todo.find(params[:id])          if  @todo.update_attributes(params[:todo])            respond_with  @todo        else            render  json:  @todo.errors,  status:  :unprocessable_entity        end    end      def  destroy        @todo  =  Todo.find(params[:id])        @todo.destroy        head  :no_content    endend

Monday, February 25, 13

Api::V1::TodosController

version your API!

/api/v1/todos

Monday, February 25, 13

i prefer URL versioning

Monday, February 25, 13

others prefer header versioning

Monday, February 25, 13

just pick one and stick with it!

Monday, February 25, 13

SOA Review

Monday, February 25, 13

SOA Review

• We’ve cleaned up our code

Monday, February 25, 13

SOA Review

• We’ve cleaned up our code

• We know the API Works

Monday, February 25, 13

SOA Review

• We’ve cleaned up our code

• We know the API Works

• We’re now in a good place to scale

Monday, February 25, 13

consuming the API

Monday, February 25, 13

don’t use Rails views

Monday, February 25, 13

JavaScript

Monday, February 25, 13

CORSMonday, February 25, 13

Cross-origin Resource Sharing

Monday, February 25, 13

https://github.com/cyu/rack-cors

rack-cors

Monday, February 25, 13

module  YourApp    class  Application  <  Rails::Application      #  ...      config.middleware.use  Rack::Cors  do        allow  do            origins  '*'            resource  '*',  headers:  :any,                                          methods:  [:get,  :post,  :put,  :delete,  :options]        end    end    end

Monday, February 25, 13

JavaScript Pros

Monday, February 25, 13

JavaScript Pros

• Scales Easily/Pushes processing to client side

Monday, February 25, 13

JavaScript Pros

• Scales Easily/Pushes processing to client side

• Separate Concerns/Very Clean

Monday, February 25, 13

JavaScript Pros

• Scales Easily/Pushes processing to client side

• Separate Concerns/Very Clean

• Can be easier to maintain

Monday, February 25, 13

JavaScript Pros

• Scales Easily/Pushes processing to client side

• Separate Concerns/Very Clean

• Can be easier to maintain

• “Responsive/Native” feel for clients

Monday, February 25, 13

JavaScript Pros

• Scales Easily/Pushes processing to client side

• Separate Concerns/Very Clean

• Can be easier to maintain

• “Responsive/Native” feel for clients

• Easily consumes your API

Monday, February 25, 13

JavaScript Cons

Monday, February 25, 13

JavaScript Cons• Can be more difficult to maintain

Monday, February 25, 13

JavaScript Cons• Can be more difficult to maintain

• Multiple languages (backend/front-end)

Monday, February 25, 13

JavaScript Cons• Can be more difficult to maintain

• Multiple languages (backend/front-end)

• Difficult to test integration

Monday, February 25, 13

JavaScript Cons• Can be more difficult to maintain

• Multiple languages (backend/front-end)

• Difficult to test integration

• JavaScript

Monday, February 25, 13

JavaScript Cons• Can be more difficult to maintain

• Multiple languages (backend/front-end)

• Difficult to test integration

• JavaScript

• Paradigm shift in architecture

Monday, February 25, 13

JavaScript Cons• Can be more difficult to maintain

• Multiple languages (backend/front-end)

• Difficult to test integration

• JavaScript

• Paradigm shift in architecture

• Accessibility issues

Monday, February 25, 13

JavaScript Cons• Can be more difficult to maintain

• Multiple languages (backend/front-end)

• Difficult to test integration

• JavaScript

• Paradigm shift in architecture

• Accessibility issues

• SEO concerns

Monday, February 25, 13

JavaScript Cons• Can be more difficult to maintain

• Multiple languages (backend/front-end)

• Difficult to test integration

• JavaScript

• Paradigm shift in architecture

• Accessibility issues

• SEO concerns

• !!Internet Explorer!!

Monday, February 25, 13

pick a framework

Monday, February 25, 13

don’t just use jQuery

Monday, February 25, 13

the big three

Monday, February 25, 13

Backbone.js

http://backbonejs.org/

Monday, February 25, 13

ember

http://emberjs.com/

Monday, February 25, 13

Angular.js

http://angularjs.org/

Monday, February 25, 13

i can’t use JavaScript

Monday, February 25, 13

2 approaches

Monday, February 25, 13

“compiled” sites

Monday, February 25, 13

build your own API library (and open source it!)

Monday, February 25, 13

Monday, February 25, 13

class  TodosController  <  ApplicationController      def  index        @todos  =  ApiLib::Todo.all    end      def  show        @todo  =  ApiLib::Todo.find(params[:id])    end      def  new        @todo  =  ApiLib::Todo.new    end      def  edit        @todo  =  ApiLib::Todo.find(params[:id])    end      def  create        @todo  =  ApiLib::Todo.new(params[:todo])        if  @todo.save            redirect_to  @todo,  notice:  'Todo  was  successfully  created.'        else            render  action:  "new"        end    end      def  update        @todo  =  ApiLib::Todo.find(params[:id])          if  @todo.update_attributes(params[:todo])            redirect_to  @todo,  notice:  'Todo  was  successfully  updated.'        else            render  action:  "edit"        end    end      def  destroy        @todo  =  ApiLib::Todo.find(params[:id])        @todo.destroy        redirect_to  todos_path,  notice:  'Todo  was  successfully  destroyed.'    endend

Monday, February 25, 13

hey! there is duplicate* code

now

Monday, February 25, 13

is there? or did we just move it

around?

Monday, February 25, 13

Todo became ApiLib::Todo

Monday, February 25, 13

we now have a reference

implementation

Monday, February 25, 13

make sure to cache!!

Monday, February 25, 13

The HackMonday, February 25, 13

Please don’t do this!

Monday, February 25, 13

I’m Serious.

Monday, February 25, 13

Don’t use this hack!

Monday, February 25, 13

I probably shouldn’t even show you it.

Monday, February 25, 13

Ok, I’ll show you, but don’t tell

people where you heard it.

Monday, February 25, 13

Monday, February 25, 13

class  TodosController  <  ApplicationController      def  index        res  =  Api::V1::TodosController.action(:index).call(env)        @todos  =  JSON.parse(res[2].body).map  do  |data|            OpenStruct.new(data)        end    end  end

Monday, February 25, 13

projects worth noting

Monday, February 25, 13

rails-apiMonday, February 25, 13

Monday, February 25, 13

api_docMonday, February 25, 13

Monday, February 25, 13

Monday, February 25, 13

Final Thoughts

Monday, February 25, 13

Final Thoughts

• Consume your API

Monday, February 25, 13

Final Thoughts

• Consume your API

• Version your API

Monday, February 25, 13

Final Thoughts

• Consume your API

• Version your API

• Document your API

Monday, February 25, 13

Final Thoughts

• Consume your API

• Version your API

• Document your API

• Did I mention consume your own API?

Monday, February 25, 13

http://www.metacasts.tv

CONFOO2013

Thank You

@markbates

Monday, February 25, 13

top related