Custom Apex REST API versioning
For each version of your Custom Rest API service, you should create a separate Class and a separate endpoint. This implies that you should make the API version a component of the URI. For example /services/apexrest/V1/cases and /services/apexrest/V2/cases. These should live in your code base as two separate Classes.
@RestResource(urlMapping='/V1/cases/*')
global with sharing class REST_cases_v1 {
@HttpGet
global static Case[] doGet(){
//Do some Awesome GET service...
.
.
.
}
//Do some other awesome methods....
}
AND
@RestResource(urlMapping='/V2/cases/*')
global with sharing class REST_cases_v2 {
@HttpGet
global static Case[] doGet(){
//Do some even Awesomoar VERSION 2! GET service...
.
.
.
}
//Do some other awesome methods....
}
Once Version "next" is released, indicate that version "previous" is deprecated and never touch it again! Over time you can work with your consumers and eventually remove the deprecated versions. I've been versioning APIs like this for a few years and it's worked very well for me.
You can add a version parameter in the header of your requests. You can then create an endpoint for each service/method you want to expose and check then which version was called based on the value in the header and execute the code required for that version.
When you have a method in your service where both v1 and v2 are identical but v3 is different then your code in your method might look like this:
if(version == 1 || version == 2){
//do stuff
}
if(version == 3){
// do other stuff
}
else{
//throw unsupported version error
}
Working as an ISV where once a global
is packaged and installed the signature can't be changed, I've found this style of working very painful:
@RestResource(urlMapping='/V1/cases/*')
global with sharing class REST_cases_v1 {
@HttpGet
global static Case[] doGet(Id someId) {
Essentially the URL mapping, the inputs and the outputs are all fixed. So the only way to evolve is to add another class as John describes which then also means that you or your customer has to ensure that the new class is made visible in all the relevant profiles. (Permission sets can help.)
The pattern I now use instead is this one where the entry point classes once created never need to be replaced and the versioning logic - including the shape of the request and response objects - is entirely under the control of the programmer:
@RestResource(urlMapping='/cases/*')
global with sharing class REST_cases {
@HttpGet
global static void doGet() {
// Parse RestContext.request.requestBody
// Internal versioning logic based on requests data
// Serialize and set RestContext.response.responseBody
// and set RestContext.response.statusCode
}
}
The parsing/serializing is easy given that JSON.deserialize
and JSON.serialize
methods are available.