RESTful API versioning with PSX

Versioning an API is required if you want make changes to an existing API without breaking clients which rely on the API. The normal process to change an API is to create a new API version and deprecate the old one. Then the consumers have a specific amount of time to upgrade their client to the new API version. After the deprecation period the API gets closed so it can no longer be used by clients. In the REST world there are two notably conecpts howto version your API:

Url versioning

In url versioning the version is included in the url i.e.: The disadvantage is here that it is not so RESTful since you have multiple endpoints which represent the same resource. So i.e. and represent both news entries but with a different format.

Content-Type versioning

In Content-Type versioning the version is provided in the Accept header field. If we have the API endpoint we could request version 1 with the following GET request:

GET /api/news HTTP/1.1
Accept: application/vnd.acme.v1+json

Through the vendor specific content type application/vnd.* we can create for each version of our API an content type which represents the response format. This makes the API more RESTful since we have now a single endpoint for our resource but with different representations.

As conclusion I think both version concepts are viable. Personally I favor Content-Type versioning since it is more RESTful. In the following a short list of popular companies and how they version their API:

  • Facebook (Url:
  • Github (Content-Type: application/vnd.github.v3+json)
  • Google (Url:
  • Twitch (Content-Type: application/vnd.twitchtv.v2+json)

PSX supports both versioning concepts. For url versioning you can simply create an route/controller for each version. For Content-Type versioning PSX has build in support which I want to explain more detailed.

PSX gives you the option to document the request and response format of your API. This documentation can be made for each version. For example our API could have the following documentation:

public function getDocumentation()
    $doc = new Documentation\Version();
    $msg = $this->schemaManager->getSchema('Acme\Schema\Version1\SuccessMessage');

    $v1 = new View(View::STATUS_ACTIVE);
    $v1->setPost($this->schemaManager->getSchema('Acme\Schema\Version1\Create'), $msg);
    $v1->setPut($this->schemaManager->getSchema('Acme\Schema\Version1\Update'), $msg);
    $v1->setDelete($this->schemaManager->getSchema('Acme\Schema\Version1\Delete'), $msg);

    $doc->addView(1, $v1);

    return $doc;

In this example we define version 1 of our API. The view contains the request methods which are available and the corresponding schema format. More informations about the schema definition at the documentation. We could request this version with the following GET request:

GET /api/news HTTP/1.1
Accept: application/vnd.acme.v1+json

The provided version gets then passed to the doXXX methods where you can trigger the business logic depending on the version. PSX requires the client to specify an version on the Accept header. If no version is provided an 415 Unsupported Media Type gets returned.

Each view can have a different status: View::STATUS_ACTIVE, View::STATUS_DEPRECATED, View::STATUS_CLOSED. If the view is deprecated an Warning header gets added to each response containing a message that the version is deprecated. If the view is closed an 410 Gone response gets returned.