Building an API in Rails without Realizing It
-
Upload
mark -
Category
Technology
-
view
2.477 -
download
0
description
Transcript of Building an API in Rails without Realizing It
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