Providing Separate APIs

TL DR: Creating an API. We need different fields for different versions. Teach me wise.

I'm currently trying to find a better way to create a versioned API. That is, I want to have a URL /api/v1/projects.json that displays a list of projects with a bunch of fields and api/v2/projects.json to display a list of projects with separate fields.

I thought about this problem for about 15 minutes, which probably means that all this is wrong. At the moment, I have this in my app/models/project.rb file:

 def self.api_fields { :v1 => ["name"], :v2 => ["name", "tickets_count"] } end 

Then I can use this in my API controllers ( api/v1/projects_controller.rb ) as follows:

 def index respond_with(Project.all(:select => Project.api_fields[:v1])) end 

This is great and works the way I would like, but probably the best way. This is your task! Share with me your mountains of wisdom API development.

Bonus points if you come up with a solution that also allows me to use methods for instances of the model object, such as the tickets_count method for the Project method.

+4
source share
4 answers

I agree with polarblau that you should have several controllers for different versions of the API. So, I am aiming at the bonus point of this question.

I think in order to archive the ability to call #tickets_count , you must override the #as_json and #to_xml model. I think you will need to do it like this:

API / v1 / projects_controller.rb

 def index respond_with Project.all, :api_version => :v1 end 

project.rb

 class Project < ActiveRecord::Base API_FIELDS = { :v1 => { :only => [:name] }, :v2 => { :only => [:name], :methods => [:tickets_count] } } def as_json(options = {}) options.merge! API_FIELDS[options[:api_version]] super end def to_xml(options = {}, &block) options.merge! API_FIELDS[options[:api_version]] super end end 

However, if you don't mind the mess in the controller, I think specifying :only and :methods in the respond_with call in the controller might also be a good idea, since you don't have to override those #as_json and #to_xml .

+1
source

As a comment:

Do you have another look?

http://devoh.com/posts/2010/04/simple-api-versioning-in-rails

API versioning guidelines?

devoh.com offers to split versions already at the routing level, which seems like a good idea:

 map.namespace(:v1) do |v1| v1.resources :categories v1.resources :products end map.namespace(:v2) do |v2| v2.resources :categories, :has_many => :products end 

Then you can use different controllers to return different fields.

+1
source

The problem, as you know, is that everything you show allows the end customer to create a direct dependency. Having said that, if you directly expose your models to the world, for example. http://domain.com/products.json , whenever you change the product model, you have a limited number of options:

  • The end customer must live with him and act like a “schema database”. You say that this will change, and do it (read that customers will deal with it)!
  • In the API, you add more enterprise version control. This means that at a more advanced level, what you provide to the end customer is not your model. Instead, you publish public objects, which in turn may be versions. This is called a data transfer object (http://en.wikipedia.org/wiki/Data_transfer_object)

If we wanted to continue the second approach, we could do the following:

 class Project < ActiveRecord::Base end class PublicProject def to_json(version = API_VERSION) self.send("load_#{version}_project").to_json end private def load_v1_project project = load_v2_project # logic that transforms a current project in a project that v1 users can understand end def load_v2_project Project.find... end end 

Hope this helps.

0
source

Connect the Sinatra application on routes in / api / v 1 to handle API calls. It's easier to add a new API and still be backward compatible until you judge it.

0
source

Source: https://habr.com/ru/post/1334895/


All Articles