API v2

j15e edited this page Apr 20, 2013 · 22 revisions

We've gone about as far as we can go to make API v1 better without breaking backwards compatibility. Compiled here is a non-exhaustive list of changes that will require a version bump.

Philosophy

  1. More RESTful. Despite aspiring to be a RESTful API, some aspects of API v1 show a fundamental misunderstanding of REST. For example, it is redundant to say DELETE /api/v1/web_hooks/remove or DELETE /api/v1/gems/yank. In v2, you'll be able to simply call the DELETE method on the resource /api/v1/web_hooks or /api/v1/gems. To register a new web hook or gem, simply POST to the same resource. To list your existing web hooks or gems, simply GET that resource.
  2. More concise. Some routes are unnecessarily long, for example /api/v1/versions/[GEM NAME]-[GEM VERSION]/downloads/search.json. This hierarchy serves no useful purpose. In v2, all routes should be designed to have no more than two levels of nesting.
  3. More consistent. The API should explicitly declare what response formats are supported (e.g. JSON, Marshal, XML, YAML) and every resource should respond to a request for any supported format. If a user makes a request for a particular format, the response should always be in that format, even in the case of a 4xx error. This will make parsing responses easier.
  4. More complete. All methods should support a JSON-P response format, to allow anyone (including us) to build a complete RubyGems front-end. For example, the ability to update links via the API. This has the potential to dramatically increase the speed of development on RubyGems.org.
  5. More secure. Any methods that require authentication should only be accessible over HTTPS.
  6. More semantic. Parameters (i.e. things that come after the question mark in a GET request) should always be optional.
  7. Pagination. There should be a consistent pagination scheme that can be applied to any GET method that can't return all of its results. I'd recommend adopting the GitHub v3 pagination scheme.
  8. Better XML. XML responses should have non-dasherized keys (using underscores instead). This is perfectly valid XML and will make this response format more consistent with JSON and YAML responses. All API responses should return data structures that can be valid XML. There is currently one api call that can't return valid XML, https://rubygems.org/api/v1/versions/coulda-0.6.3/downloads/search.json, since it's a hash with dates as keys.

Comments

Thoughts? Edit away, it's a wiki for a reason! :) —@qrush

In terms of suggestion (1), wouldn't it make more sense to have it be DELETE /api/v2/web_hooks/[HOOK_NAME] or DELETE /api/v2/gems/[GEM_NAME]-[GEM_VERSION]? Making them into proper resources. I don't think having lots of parameters passed to an API method is necessarily better than having a multi-segment URL, especially if each segment has some meaning, and each end point is a 'resource'. —@namelessjon

I definitely understand where you're coming from. Let's take web hooks as an example. Today, you can pass * instead of a gem name to make the hook apply globally, to all your gems. Given that we need to maintain this functionality (but ideally without the *), I think it makes sense to think of the resource as "the list of all your webhooks" to which you can add (POST) or subtract (DELETE). If you want to operate on a more granular basis, you can pass an optional gem_name parameter (see 6 above). How would you handle this specific case in your scheme? —@sferik

I think something like this: POST /web_hooks/[HOOK_NAME] would create a hook for all of your gems. DELETE to that uri would delete it for all your gems. POST /web_hooks/[HOOK_NAME]/[GEM_NAME] makes a hook just for that gem, DELETE removes it for a gem. With GET /web_hooks giving you a list of all hooks, and GET /web_hooks/[HOOK_NAME] giving you specific details (assuming you don't display the full resource in the list on the upper hook.) —@namelessjon

I'd love to see OPTIONS support and <link>s in the XML version (instead of bare IDs). I don't think there are a lot of clients out there right now that would benefit from either, but the RESTafarians of the world really emphasize both. I bet if we build it, people will build clients to take advantage, and that could really improve the state of Ruby + REST. —@jamesarosen

I am in complete agreement and plan to implement both of the features you've proposed, following the advice of Steve Klabnik. —@sferik

It might be nice to be able to specify the API version as a header (X-API-Version) instead of in the URL. —@wycats

I disagree. Now, instead of just running a simple GET request passing just the URL (with, say open() ('open-url')) you need to add code to provide headers as well. —@dvyjones

We would just default to the latest version if no version is specified, so this header would be optional. —@sferik

It would be great to support CORS in addition to JSON-P. Check out the CORS Rack middleware: https://github.com/cyu/rack-cors. —@wycats

 +1 for CORS or jsonp support - @j15e