API Versioning with Symfony2 using URI

API Versioning with Symfony 2 using URI

There are a lot of ways in versioning your API. Inserting the version in the URI is the easiest way in doing so. Although this breaks the HATEOAS constraint of a RESTful API, URI versioning is the easiest to implement.

Symfony 2 has a feature called routes. In these routes, the developer can create URI patterns that when matched with a request will execute a block of code. These routes are powerful enough to filter request using regular expressions and even indicate the format or locale that will be used for the request.

getUser:
    pattern: /user/{user_id}
    defaults: { _controller: OsumWebAppBundle:User:get }
    methods: [GET]
    requirements:
        user_id: '^[1-9][0-9]*'

From the example above, we can derive this URI..

GET http://api.osumwebapp.com/user/123

A useful feature of Symfony2 routing is that it can include external “resources”. These resources are routing files that can be “included” in the main routing file. Another feature is the use of “prefixes”. These prefixes will be prepended to each routing pattern mentioned in the resources property. For example:

osum_web_app:
    resource: "@OsumWebAppBundle/Resources/config/routing.yml"
    prefix:   /v1

This routing file when joined with our previous route will result to this URI..

GET http://api.osumwebapp.com/v1/user/123

API versioning using URI

As you can see we have the ability to specify versions in our routes. Adding a second version for your route is a little complex. First you need to create a new route in your main routing file.

osum_web_app:
    resource: "@OsumWebAppBundle/Resources/config/routing.yml"
    prefix:   /v1
osum_web_app_v2:
    resource: "@OsumWebAppBundle/Resources/config/routing_v2.yml"
    prefix:   /v{version}

You may notice that we used a parameter in the prefix property. This will enable us to determine the version we are using in our code. Also, take note that we need to rename our routes. Notice the appended “_v2” in the code. Without these, Symfony2 will overwrite the previous routes that are already defined. Now all you need to do is create a routing file for that specified resource. You also need to rename the routes so it won’t get overwritten.

getUser_v2:
    pattern: /user/{user_id}
    defaults: { _controller: OsumWebAppBundle:v2/User:get }
    methods: [GET]
    requirements:
        user_id: '^[1-9][0-9]*'
addUser_v2
    pattern: /user
    defaults: { _controller: OsumWebAppBundle:User:add }
    methods: [POST]

You may also notice that in the first controller, there is a “v2” before the User but nothing in the second route. In Symfony2, you can reuse a controller as you wish. You can also group the controllers in a folder. In this way, we can group each controller to their specific version. The previous routing file will now result to these URI’s..

GET http://api.osumwebapp.com/v2/user/123
POST http://api.osumwebapp.com/v2/user

Securing your versioned route

What if we put v9999 instead of v2? This will definitely provide us a wrong version and we will possibly end up breaking our code and gets scolded by our project managers.

Symfony2 also has a feature which in can specify the requirements for each route parameters. As you can see in our previous example. We used a “version” parameter in our prefix. Yes, we could have just put “/v2” and get done with it but doing so will render us from knowing what version we are using. We can simply secure the route by putting a requirement to the version parameter like this..

osum_web_app:
    resource: "@OsumWebAppBundle/Resources/config/routing.yml"
    prefix:   /v1
osum_web_app_v2:
    resource: "@OsumWebAppBundle/Resources/config/routing_v2.yml"
    prefix:   /v{version}
    requirements:
        version: '^2[\.0-9]*$'

The regular expression will restrict the users to use “/v2” in order to match the route. This also enables them to match the route with a specific version.

GET http://api.osumwebapp.com/v2.1.3/user/123

One downside of this method is it will be a maintenance nightmare for your team. Your code base will certainly grow as your version moves forward. There are a better RESTful ways in versioning your API like using HTTP headers. I personally want to use headers since it complies more on the philosophical standpoint of REST, but putting versions in the URI is easier for my API clients.

Tired of googling? Check out these links:

Your API versioning is wrong, which is why I decided to do it 3 different wrong ways

Best practices for API versioning?

2 thoughts on “API Versioning with Symfony2 using URI”

  1. i think it would be better to abstract your API from a Bundle you can extend within your symfony2 project. This way, u can maintain your APi through a git repo and tag it as how much you want following (1.1, 1.2 and so on) , this is more reliable than maintaining several docs for a same project and multiplying routes for how much version u have.

Leave a Reply

%d bloggers like this: