A dynamic, reusable, and secure Apex REST framework for handling SObject integrations. This framework provides a single endpoint that can upsert any SObject and insert its related child records in a single, bulkified transaction, while allowing for custom logic injection without modifying the core code.
A common challenge we encounter in our Salesforce development journey is the initial approach to integrations: building a dedicated Apex REST endpoint for each SObject. While this direct method works for a single requirement, it creates a significant maintenance challenge as an organization scales. This framework was built to solve that problem.
- Dynamic Endpoint: A single endpoint handles any standard or custom SObject.
- Secure by Design: Automatically enforces Field-Level Security (FLS) and object permissions for the running user.
- Scalable & Extensible: Uses a Custom Metadata-driven factory pattern to allow developers to inject custom pre- and post-processing logic for any SObject without touching the core framework.
- Bulkified: Designed to handle payloads with multiple records efficiently and respect governor limits.
- Parent-Child Support: Upserts parent records and inserts their direct child records in the same transaction.
- Robust Error Handling: Provides detailed, record-by-record success and error messages in the response payload.
The framework is built with a clear separation of concerns to ensure maintainability and scalability:
- SObjectResource: The main REST controller that handles the inbound HTTP request and orchestrates the process.
- SObjectResourceHandler: A helper class that contains the core business logic for parsing, validating, and processing the data.
- SObjectResourceModels: A container class for the API's data contracts (request and response DTOs).
- IBeforeUpsertHandler/- IAfterUpsertHandler: Apex interfaces that define the contract for custom logic injection.
- BeforeUpsertHandlerFactory/- AfterUpsertHandlerFactory: Factory classes that use Custom Metadata to dynamically instantiate the correct handler class for a given SObject.
- SObject_Handler_Mapping__mdt: The Custom Metadata Type that maps SObjects to their corresponding handler classes.
Follow these steps to set up the framework in your Salesforce org.
This step is critical and must be done before deploying any Apex code.
- 
Navigate to Setup -> Custom Code -> Custom Metadata Types. 
- 
Click New Custom Metadata Type. 
- 
Enter the following details: - Label: SObject Handler Mapping
- Plural Label: SObject Handler Mappings
- Object Name: SObject_Handler_Mapping
- Visibility: Public
 
- Label: 
- 
Click Save. 
- 
In the Custom Fields section, add the following three fields: - 
Field 1: SObject API Name - Data Type: Text
- Field Label: SObject API Name
- Length: 255
- Field Name: SObject_API_Name
- Required: Check the box for "Always require a value in this field in order to save a record".
 
- Data Type: 
- 
Field 2: Handler Class Name - Data Type: Text
- Field Label: Handler Class Name
- Length: 255
- Field Name: Handler_Class_Name
- Required: Check the box for "Always require a value in this field in order to save a record".
 
- Data Type: 
- 
Field 3: Handler Type - Data Type: Picklist
- Field Label: Handler Type
- Values: Enter the values BeforeandAfter, each on a separate line.
- Field Name: Handler_Type
- Required: Check the box for "Always require a value in this field in order to save a record".
 
- Data Type: 
 
- 
Now that the Custom Metadata Type exists, you can deploy the Apex classes in the following order.
Deployment Wave 1: Models & Interfaces
- SObjectResourceModels.cls
- IBeforeUpsertHandler.cls
- IAfterUpsertHandler.cls
Deployment Wave 2: Logic & Handlers
- SObjectResourceHandler.cls
- BeforeUpsertHandlerFactory.cls
- AfterUpsertHandlerFactory.cls
- AccountBeforeUpsertHandler.cls(Example Implementation)
- AccountAfterUpsertHandler.cls(Example Implementation)
Deployment Wave 3: Endpoint Controller
- SObjectResource.cls
For secure server-to-server integration, the recommended approach is to use an External Client App with the OAuth 2.0 Client Credentials Flow. This method allows your external application to authenticate directly without needing to impersonate a specific human user.
Here is a high-level overview of the setup process:
- Create an Integration User: Set up a dedicated, API-only user in Salesforce.
- Create a Permission Set: Create a permission set that grants all necessary permissions (Apex class access, object/field permissions).
- Create and Configure an External Client App: Set this up and enable it for the Client Credentials Flow.
- Assign Permissions: Assign your new permission set to the integration user and authorize it for the External Client App.
For detailed, step-by-step instructions, please refer to the official Salesforce documentation:
Using the API is a two-step process. First, you obtain an access token using your External Client App credentials, and then you use that token to call the data gateway.
Your client application must first request an OAuth 2.0 access token from Salesforce.
- Method: POST
- URL: {{instance_url}}/services/oauth2/token?grant_type=client_credentials&client_id={{client_id}}&client_secret={{client_secret}}
Note: Replace {{...}} placeholders with your Salesforce My Domain URL and the credentials from your External Client App. A successful response will contain an access_token.
Once you have the access token, include it as a bearer token in the Authorization header of your request.
- Method: POST
- URL: {{instance_url}}/services/apexrest/v1/data-gateway/{SObjectApiName}
Note: Replace {SObjectApiName} with the API name of the object you are targeting (e.g., Account).
- Authorization: Bearer <Your_Access_Token_From_Step_1>
- Content-Type: application/json
This example upserts an Account and simultaneously inserts related Contacts and Opportunities.
{
  "externalIdField": "External_Account_ID__c",
  "data": [
    {
      "Name": "Apex Global Corporation",
      "Industry": "Consulting",
      "BillingCity": "California",
      "External_Account_ID__c": "AGC-002",
      "Contacts": [
        {
          "FirstName": "Abhishek",
          "LastName": "Thulasi",
          "Email": "abhishek.thulasi@apexglobal.com",
          "Title": "Software Architect"
        },
        {
          "FirstName": "David",
          "LastName": "Chen",
          "Email": "david.chen@apexglobal.com",
          "Title": "Project Manager"
        }
      ],
      "Opportunities": [
        {
          "Name": "AGC - Q4 Enterprise License",
          "StageName": "Prospecting",
          "CloseDate": "2025-12-20",
          "Amount": 75000
        },
        {
          "Name": "AGC - 2026 Support Contract",
          "StageName": "Needs Analysis",
          "CloseDate": "2026-01-31",
          "Amount": 25000
        }
      ]
    }
  ]
}To add custom logic for a new SObject (e.g., Opportunity), follow these steps:
- 
Create a Handler Class: Create a new Apex class that implements IBeforeUpsertHandlerand/orIAfterUpsertHandler.// Example: OpportunityBeforeUpsertHandler.cls public class OpportunityBeforeUpsertHandler implements IBeforeUpsertHandler { public List<SObject> beforeUpsert(List<SObject> records) { for (SObject sObj : records) { Opportunity opp = (Opportunity) sObj; // Custom logic: Ensure StageName is not null if (opp.StageName == null) { opp.StageName = 'Prospecting'; } } return records; } } 
- 
Create a Custom Metadata Record: - Go to Setup -> Custom Metadata Types -> SObject Handler Mapping.
- Click Manage Records, then click New.
- Enter the following details:
- Label: Opportunity Before Upsert
- SObject API Name: Opportunity
- Handler Class Name: OpportunityBeforeUpsertHandler
- Handler Type: Before
 
- Label: 
- Click Save.
 
That's it! The framework will now automatically execute your custom logic for any Opportunity records processed by the API.