18

Are there any people who already implemented a custom apex REST api that requires versioning in order not to break existing applications who consume the web service? Do you know any best practices around this?

Use case: a third party application at our customers uses a custom API to access Salesforce. At some point there is a request to add/remove parameters or change the response or the logic of the method. This may impact the third party implementations if they are not updated at the same time.

Thx in advance for your ideas.

1
  • 2
    Idea is easy -- make a single entry point class, define version as request parameter, define some base interface, that perform your logic, and multiple classes, that corresponds into version and instances of which are constructed in factory
    – kurunve
    Commented Jun 21, 2017 at 7:51

4 Answers 4

16
+50

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.

1
  • I use SoC so I have separate selectors/domains/services etc. These classes I do not version but still using them in the versioned REST API services. These classes are regularly updated over time but this breaks the constraint "never touch it again" as they are used in the REST API services. How do you handle the versioning and "never touch it again" when you don't have all the code in the @RestResource method?
    – ondrakr
    Commented May 7, 2022 at 11:59
6

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
}
5

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.

1

Just adding this repo as another way to do this, without needing to create separate classes. a bit higher cognitive complexity than the other options which for most use cases are probably fine, but still cool nonetheless

the author mentions you can use this router/nested url concept for versioning too. So your restresource would just be /api/* and the router would handle the rest of the URI

https://github.com/callawaycloud/apex-rest-route

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .