Over a million developers have joined DZone.

Evolving APIs using Grape API Versioning

Learn how API management supports better integration in Achieving Enterprise Agility with Microservices and API Management, brought to you in partnership with 3scale

I’ve seen two common API versioning strategies in the wild. The first is to use a single API version and gradually deprecate methods. This usually means introducing new API routes, while retiring old ones, and representing the same objects in multiple, versioned, formats. Fast forward a few years and you are likely to inherit a significant amount of technical debt. The second strategy involves making a clean cut, leaving the version one of the API alone and building a fresh, new, API version two.

Starting with the next release of Grape (likely 0.6.0) you will have a third alternative: building a new API version incrementally on top of a previous one. There’re no hacks involved. Consider the following trivial API.

    module Acme
      class V1 < Grape::API
        format :json
        version 'v1', using: :header, vendor: 'acme', format: :json
        desc "Returns the current API version, v1."
        get do
          { version: 'v1' }
        desc "Returns pong."
        get "ping" do
          { ping: "pong" }

Define the next API version.

    module Acme
      class V2 < Grape::API
        format :json
        version 'v2', using: :header, vendor: 'acme', format: :json
        desc "Returns the current API version, v2."
        get do
          { version: 'v2' }

At this point we want v1 to be identical to v2, except for the root method. We’ll start by allowing v1 to respond to both v1 and v2 requests.

    version [ 'v2', 'v1' ], using: :header, vendor: 'acme', format: :json

Mount v2 before v1 and allow v2 to cascade the request to the next Rack middleware by adding cascade: true to its version declaration. Rack::Cascade works by passing on any requests until a middleware replies with something else than a 404.

new Rack::Cascade([ Acme::V2, Acme::V1 ])
version 'v2', using: :header, vendor: 'acme', format: :json, cascade: true

Try it on my demo project in https://github.com/dblock/grape-on-rack-v1-inside-v2.

By default we get the root of v2 and the ping method implemented on v1.

    curl http://localhost:9292/
    curl http://localhost:9292/ping
With version two, we get the same thing.
    curl http://localhost:9292 -H "Accept:application/vnd.acme-v2+json"
    curl http://localhost:9292/ping -H "Accept:application/vnd.acme-v2+json"
With version 1 we get the old behavior.
    curl http://localhost:9292 -H "Accept:application/vnd.acme-v1+json"
    curl http://localhost:9292/ping -H "Accept:application/vnd.acme-v1+json"
The only fix needed in Grape for this was to enable multiple versions specified with version, committed in 2be499c51.

This came up on the Grape mailing list.

Unleash the power of your APIs with future-proof API management - Create your account and start your free trial today, brought to you in partnership with 3scale.


Published at DZone with permission of Daniel Doubrovkine .

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}