Access Apex as a REST data source

You can also use REST models to access Apex code by annotating your code as a @RestResource, with each method in the class annotated as its own HTTP verb. One unique advantage of using this method is that—since you’ll be connecting to Apex code as you would an API—you can also use Apex methods across orgs, with the appropriate connected app settings. You can make API calls using jQuery’s get() or similar calls, but this example will illustrate how to use Skuid models to access the code.

To access Apex as a REST data source:

  1. Annotate your Apex code as a @RestResource.
  2. Create a Salesforce connected app.
  3. Create a Skuid authentication provider and REST data source.
  4. Create Skuid models that access your REST data source.

Warning

Each call to a @RestResource will count against your API Usage limit. You can view your API Usage in Salesforce under Monitor > System Overview in Salesforce Setup.

Note

The steps below require My Domain to have been set up for any orgs used.

Note

If you are using Skuid SFX and all of the Apex you’ll be accessing exists within the same org as your Skuid instance, you can use your existing Session ID to authenticate to Salesforce instead. This means you would not have to create a connected app or authentication provider.

For more information, see the Using session ID to authenticate within the same org section.

Annotate your Apex code as a @RestResource

You can read more about creating REST APIs from Apex code, but this simple example will recreate some basic Salesforce behaviors to illustrate the basics. Here are two Apex methods that …

  • return a list of accounts—limited to 20—filterable by name and state;
  • create a new account, where end users can enter a phone number, a website, and billing state.

Copy and paste the following example code as a new Apex class:

@RestResource(urlMapping='/AccountsList/*')
global with sharing class AccountsList {

    @HttpGet
    global static List<Account> getAccounts() {
        List<Account> accts = new List<Account>();

        String searchVal = RestContext.request.params.get('namesearch');
        String billingState = RestContext.request.params.get('billingst');
        if (searchVal != null) {
            String searchStr = '%' + searchVal + '%';
            accts = [SELECT Id, Name, Phone, Website, BillingState FROM Account WHERE Name LIKE :searchStr LIMIT 20];
        } else if (billingState != null) {
            accts = [SELECT Id, Name, Phone, Website, BillingState FROM Account WHERE BillingState = :billingState LIMIT 20];
        } else {
            accts = [SELECT Id, Name, Phone, Website, BillingState FROM Account LIMIT 20];
        }
        return accts;
    }

    @HttpPost
    global static String newAccount(String Name, String Phone, String Website, String BillingState) {
        Account account = new Account();
        account.Name = Name;
        account.Phone = Phone;
        account.Website = Website;
        account.BillingState = BillingState;
        insert account;
        return account.Id;
    }
}

To break down the use of the @RestResource annotation in the code above:

  • First, the Apex class is annotated as a REST resource. Its URL endpoint is set, and the class to be accessed is declared: AccountsList.
  • Next are two functions: getAccounts() and newAccount(). Each function correlates to a REST verb and is prepended with the correlated annotation.
    • Queries will use the GET verb and the getAccount() function, requiring the @HttpGet annotation.
    • Creating new accounts will use the POST verb and the newAccount() function, hence @HttpPost.

The variables declared within these functions will correlate directly with the fields and URL merge conditions you must create in later steps, so enter them as needed.

Create a Salesforce connected app

Salesforce connected apps are the keys to the kingdom when accessing any type of Salesforce data through an API connection, from data objects to Apex code.

Follow the steps in Setting up a Salesforce connected app, however the Callback URL must be set for the org you are working within. If you’ll be using the Apex code within other orgs, enter their callback URLs as well.

Create a Skuid authentication provider

Once the code has been properly annotated and your Salesforce data is accessible through the connected app, configure the settings within Skuid that allow you to authenticate and access your Apex.

If you are using the Apex code in an external org, repeat the following steps for each org.

First, set up an authentication provider with the following properties:

  • Name: SFAuth
  • Authentication Method: OAuth 2.0 / OpenID
  • Provider Type: Salesforce
  • Grant Type: Authorization Code
  • Authorize and Token URLS: Replace “<My Domain>” with your own Salesforce domain.
  • Default Scopes: Must match the scopes set in your connected app in the previous step.
  • Client ID & Client Secret: These fields relate to the App Key and App Secret, respectively, from your connected app in the previous step

Create a REST data source

  • Data Source Type: REST
  • Name: ApexREST
  • URL: https://<Salesforce My Domain>.my.salesforce.com/services/apexrest
    • If you are using a namespace, include that namespace in the URL: https://<Salesforce My Domain>.my.salesforce.com/services/apexrest/your_namespace
  • Authentication Method: Authentication Provider
  • Authentication Provider: SFAuth

Using session ID to authenticate within the same org [[]]

If all of your Apex code exists within the same org as your Skuid SFX instance, you may use your session ID to authenticate instead.

  1. Set the following fields:
    • Data Source Type: REST
    • Name: ApexREST
    • URL: https://<Salesforce My Domain>.my.salesforce.com/services/apexrest
      • If you are using a namespace, include that namespace in the URL: https://<Salesforce My Domain>.my.salesforce.com/services/apexrest/your_namespace
    • Authentication Method: None
  2. Click Headers to send with every request.
  3. Click Append and enter the following:
    • FIELD: Authorization
    • VALUE: Bearer {{$Api.Session_Id}}
  4. Click Save.

Create Skuid models that access your REST data source

Now that your connection settings allow you to connect to your Apex methods, it’s time to set up models that properly access the Apex code—as well as passing along the variables needed for the example code above.

The key concept to remember: Skuid’s model properties should parallel the RestResource Apex. With properly written and annotated Apex, follow these tenets to access the code:

  • Ensure your Apex endpoints match your Skuid model’s data source URL.
  • Use URL merge conditions in your data source URLs for filtering
  • Send your updates as POST requests with a JSON body.

To illustrate these concepts, let’s walk through this example page and breakdown the pieces that allow for Apex to be accessed and used in the page.

The model

First, the model AccountsList has the following properties:

  • Data Source Type: REST
  • Data Source: ApexREST
  • Model Behavior: Read/Write

With its data source type set to REST, and the data source created to access the Apex specified, admins can enter the specific endpoints for its read/write methods.

And it’s using this model’s two methods, which will parallel the Apex methods above, that data is sent to the endpoints specified within the Apex code. First, the method to query for accounts:

Query:

  • Data Source URL: /AccountsList{{#NameSearch}}?namesearch={{NameSearch}}{{/NameSearch}}{{#BillingStFilter}}{{#NameSearch}}&{{/NameSearch}}{{/BillingStFilter}}{{#BillingStFilter}}{{^NameSearch}}?{{/NameSearch}}billingst={{BillingStFilter}}{{/BillingStFilter}}
  • Data Source HTTP Verb: GET

Notice that the data source URL and HTTP verb mirror what is specified by the urlMapping value and the @HttpGet annotation respectively. The URL merge conditions are contained within sections, meaning they will not be included in the query’s request if they are not specified. If no URL merge conditions are specified, the query request goes to:

https://<Salesforce My Domain>.my.salesforce.com/services/apexrest/AccountsList

But, if the user does specify those conditions through the Table component’s filter or search fields—more on that below—then they will be inserted.

Note

There are also some other sections that will insert the appropriate URL parameter (? or &) depending on whether or not one or both URL merges are specified.

Next, the method to create new accounts:

Insert

  • Data Source URL: /AccountsList
  • Data Source HTTP Verb: POST
  • Send new fields value…: As JSON in request body

When a user creates a new row on the Skuid Table component and clicks Save, Skuid will POST a request to the specified data source URL—which matches the urlMapping value in the Apex. The values for that new account sent along as JSON, as specified by the Send new fields value.

Finally, the model itself has two URL merge conditions, which match the merge variables listed in the query method’s data source URL. They are set as Filterable default off, which—alongside the if statements in the Apex method—allow for end users to query the accounts without first specifying some values.

The Table

To complete the page, a Skuid Table component is attached to the AccountsList model. As the model contains metadata for the fields specified by the Apex, they’re represented as fields in the Table. A filter is connected to the billingst URL merge condition, and the search field is connected to the NameSearch URL merge condition, allowing users to filter.

Whenever a user enters a value in either of these fields, their input is inserted into the URL merge conditions specified in the query methods data source URL, meaning the query request is sent to:

https://<Salesforce My Domain>.my.salesforce.com/services/apexrest/AccountsList?namesearch=aName&billst=aState

Expanding this example

While this is a very simple example, the concepts here can be used for a variety of complex Apex operations—especially since additional Salesforce logic is accessible.

If you want to expand on this basic example, see if you can code more functionality, such as a delete method:

@HttpDelete
global static void deleteAcount() {
     // Include your own delete code here!
}

Much like the above example methods, ensure that your REST model’s method correlates with what is specified in your Apex. For example, the data source HTTP verb would be DELETE. See what you can create!

Troubleshooting

If you have issues using this method, check the details of the requests Skuid is sending, in addition to the responses Salesforce is sending back. (To do this, use the Network tab in the developer tools of your browser of choice, e.g. Chrome or Firefox.)

Common issues and corrections

  1. Always ensure that your REST endpoints are correct.
  2. Determine if you are passing the correct parameters to the REST endpoint:
    • If you are receiving errors similar to System.QueryException: List has no rows for assignment to SObject, check your URL merge conditions. You may not be passing a field your Apex class requires.
  3. Ensure that your data source URL and your Apex @RestResource URL match. Do not include an extra slash within your data source URL and your endpoint URLs:
    • Not this: salesforce.com/ and /PaymentProcess/
    • But this: salesforce.com and /PaymentProcess/
  4. Check that the models’ data source HTTP verb and the Apex @Http annotation verbs match.
  5. Ensure that there are no spaces, particularly trailing spaces, in your query and insert REST endpoints.
  6. Verify that the connected app includes the callback URL of the org accessing the REST resources, which may be the current org.