function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Kenji775Kenji775 

Custom Apex Rest - Extracting Request Data

Hey all,

I'm a bit new to writting anything more than very simple Apex REST services. All the ones I have written have had one method per HTTP verb. There was one GET, one POST etc. So now I am writting something a bit more complex. This one needs to support 5 different GET methods, based on the request URL. Good REST design says have basic operations separated by slashes, and hide complexity behind the question mark. So I want to be able to do something like

 

//Methods for retreiving data from the system. All query methods accept
// fields parameter which specifies data returned in the query. All methods also accept
// a filter param which can accepts an SOQL where string to refine results.
//Methods include:

// /customer - get all customers
// /customer/Id - get customer based on dmp code, SSN, or Salesforce Record ID
// /customer/Id/cars - get all customer cars based on dmp code, SSN, or Salesforce Record ID
// /car - get all cars
// /car/vin - get car by VIN

//Ex: /customer?fields=name,firstname,email&filter=firstname='frank'
//get the name, firstname, and email for any contact with a firstname frank

//Ex: /car?filter=type__c='truck'
//Find all cars that are of type truck




So there are 5 different GET actions. Of course you can only declair one method as  @HttpGet annotated. So within that method you need to inspect the request URL and do different things. The only real way I know of to inspect the URL is through just basic string manipulation/inspection, which seems messy. I am just wondering if I am even on the right track, or if people have another method? My first thought is to take the URL, split it based on the / and evalutate the strings in the various positions to figure out what I need to do, but it seems like doing that is going to be very.. static also just begging for null pointer exceptions. Any thoughts on this would be great.

    @HttpGet
    global static list<sObject> doGet(RestRequest req, RestResponse res) 
    {
        list<sObject> returnValues = new list<sObject>();
        String queryObject = 'first element after first /';
        String queryObjectId = 'second element after first /, may be null';  
        string queryFields = 'id,name';
        
        if (req.params.containsKey('fields'))
        {
            queryFields = req.params.get('fields');
        }
        
        if(queryObject = 'customer')
        {
             //call the query customer method
        }
        else if(queryObject = 'car')
        {
         //call the query car method
        }
        
        
        
        
        return returnValues;
    }

 

 

garybgaryb

I've been back and forth with you on twitter about some aspects of this - mostly, using requestURI() and headers() where you can. However, I wanted to add something else and there was no way I could get this down to 140 characters!

 

I'd have two rest handler classes with the appropriate RestResource URL mappings, and then a class with helper logic to extract information from the last part of the URL that can be used from either.

 

Maybe it's just because you've thrown together a quick example, but the

 

if(queryObject1) {

//

} else if(queryObject2){

//

}

 

logic suggests to me that this should be two different classes. If there was no need for the branch, I'd understand why you'd want to avoid separate classes; but if you're treating the objects differently, I'd separate them out and not lose too much sleep.

Kenji775Kenji775

Thanks for the input, I do appreciate it. I pretty much ended up with something similar to that. This is what I ended up with 

 

    @HttpGet
    global static list<sObject> doGet(RestRequest req, RestResponse res) 
    {
        list<sObject> returnValues = new list<sObject>();
        list<String> URLParams = req.requestURI.split('/');
        
        //The type of object they are querying for is held in the 7th element of the array
        String queryObject = URLParams[7];            
        string queryFields = 'id,name';    
        string queryFilter;      
        string sObjectType;
        
        
        //variouns branch statments to handle each object case.  
        if(queryObject == 'customer')
        {
            sObjectType = 'Account';
            if(URLParams[8] != null)
            {
                if(isValidId(URLParams[8]))
                {
                    queryFilter = 'where id = \''+URLParams[8]+'\'';     
                } 
                else
                {
                    queryFilter = 'where SSN__c = \''+URLParams[8]+'\'';    
                }         
            }                          
        }
        else if(queryObject == 'car')
        {
            sObjectType = 'Auto__c';
            if(URLParams[8] != null)
            {
                if(isValidId(URLParams[8]))
                {
                    queryFilter = 'where id = \''+URLParams[8]+'\'';     
                } 
                else
                {
                    queryFilter = 'where VIN__c = \''+URLParams[8]+'\'';    
                }         
            }              
        }

        else if(queryObject == 'ownership')
        {
            sObjectType = 'ownership__c';
            queryFields = 'VIN__c, SSN__c, Status__c';              
        }

        //If the user wants to override the default selected fields, let them.
        if (req.params.containsKey('fields'))
        {
            queryFields = req.params.get('fields');
        }

        //If the user has specified a filter, append it now
        if (req.params.containsKey('fiter'))
        {
            queryFilter = req.params.get('filter');
        }
        
        //if the filter is null, that will cause an error, so just set it to a space
        if(queryFilter == null)
        {
            queryFilter = ' ';
        }
        
        //run the query                        
        returnValues = querySObject(sObjectType, queryFields, queryFilter);
                            
        return returnValues;
    }

    
    public static list<sObject> querySObject(string objectType, string queryFields, string queryFilter)
    {
        list<sObject> sObjects = new list<sObject>();
        
        string queryString = 'select ' + queryFields + ' from ' + objectType + ' ' + queryFilter;
        system.debug(queryString);
        sObjects = Database.query(queryString);
        return sObjects;
    }   

    public static Boolean isValidId(String s) 
    {
        Id validId;
        try 
        {
           validId = s;
           return true; 
        } 
        catch (Exception ex) 
        {
            return false;
        }
    }