In Salesforce, the Database.AllowsCallouts interface is used to enable Apex classes to perform HTTP callouts or web service callouts in asynchronous processes like Batch Apex or Queueable Apex. Callouts are requests made to external systems, such as HTTP requests or SOAP/REST web service calls defined with the webService keyword.
While most developers are familiar with @future(callout=true) or making synchronous HTTP callouts in Apex, there’s a lesser-known, powerful tool that Salesforce provides for this: Database.AllowsCallouts.
What Is Database.AllowsCallouts?
- Purpose: Database.AllowsCallouts is a marker interface in Apex. It doesn’t require any method implementations but acts as a flag to let Salesforce know that your asynchronous class (like a batch or queueable job) is allowed to make HTTP or web service callouts. Without implementing this interface, Salesforce restricts callouts in certain contexts (e.g., Queueable Apex) to prevent governor limit violations or performance issues.
- Context: It is primarily used in Batch Apex and Queueable Apex to integrate Salesforce with external systems, such as APIs, web services, or third-party servers.
- Callout Types: HTTP Callouts via HttpRequest and Http classes (e.g., REST APIs). SOAP Web Services via webService keyword in Apex.
How to Use Database.AllowsCallouts
To enable callouts, the Apex class must implement the Database.AllowsCallouts interface alongside other interfaces like Database.Batchable
- Batch Apex Example
- Queueable Apex Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
global class SearchAndReplace implements Database.Batchable<sObject>, Database.AllowsCallouts { global Database.QueryLocator start(Database.BatchableContext BC) { String query = 'SELECT Id, Name FROM Account LIMIT 100'; return Database.getQueryLocator(query); } global void execute(Database.BatchableContext BC, List<Account> scope) { for (Account acc : scope) { try { HttpRequest req = new HttpRequest(); req.setEndpoint('https://api.example.com/data'); req.setMethod('GET'); req.setHeader('Content-Type', 'application/json'); Http http = new Http(); HttpResponse res = http.send(req); if (res.getStatusCode() == 200) { System.debug('Response: ' + res.getBody()); } } catch (Exception e) { System.debug('Error: ' + e.getMessage()); } } } global void finish(Database.BatchableContext BC) { // Post-processing logic } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
public with sharing class WarehouseCalloutService implements Queueable, Database.AllowsCallouts { private static final String WAREHOUSE_URL = 'https://th-superbadge-apex.herokuapp.com/equipment'; public void execute(QueueableContext context) { Http http = new Http(); HttpRequest request = new HttpRequest(); request.setEndpoint(WAREHOUSE_URL); request.setMethod('GET'); HttpResponse response = http.send(request); if (response.getStatusCode() == 200) { List<Object> equipmentList = (List<Object>)JSON.deserializeUntyped(response.getBody()); List<Product2> productsToUpsert = new List<Product2>(); for (Object obj : equipmentList) { Map<String, Object> item = (Map<String, Object>) obj; Product2 p = new Product2(); p.Warehouse_SKU__c = (String) item.get('sku'); p.Name = (String) item.get('name'); p.Replacement_Part__c = (Boolean) item.get('replacement'); p.Cost__c = (Decimal) item.get('cost'); p.Current_Inventory__c = (Integer) item.get('quantity'); p.Lifespan_Months__c = (Integer) item.get('lifespan'); p.Maintenance_Cycle__c = (Integer) item.get('maintenanceperiod'); productsToUpsert.add(p); } if (!productsToUpsert.isEmpty()) { upsert productsToUpsert Warehouse_SKU__c; } } } } |
Key Features and Behavior
- Enables External Integration
- Allows Salesforce to communicate with external systems, such as APIs (e.g., REST, SOAP), third-party services (e.g., RazorSync, Skedulo), or other servers.
- Marker Interface
- Database.AllowsCallouts is a marker interface, meaning it has no methods to implement. It simply informs the Salesforce runtime that the class is allowed to make callouts.
- It does not introduce performance overhead, even if included in classes that don’t make callouts.
- Asynchronous Context:
- Designed for asynchronous processes like Batch Apex and Queueable Apex, where long-running operations (e.g., callouts) won’t block database transactions.
- Governor Limits:
- Callout Limit: A single Apex transaction can make up to 100 callouts (HTTP requests or web service calls).
- Batch Size: To avoid hitting the 100-callout limit, the batch size should be set to 100 or lower if each record requires one callout.
- Callout Size: The maximum size of a callout request or response is 6 MB for synchronous Apex and 12 MB for asynchronous Apex.
- Timeout: Callouts can take up to 120 seconds per request, but the total transaction time is also governed by Salesforce limits.
- Error Handling
- Callouts can fail due to network issues, timeouts, or external server errors. Robust error handling is critical:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
try { HttpResponse res = http.send(req); if (res.getStatusCode() != 200) { throw new CalloutException('Non-200 response: ' + res.getStatus()); } } catch (Exception e) { System.debug('Callout failed: ' + e.getMessage()); } |
Limitations and Constraints
- Not Supported in Triggers:
- Direct callouts from triggers are not allowed because they could delay database transactions (up to 120 seconds).
- Constructor Limitation:
- Callouts in the constructor of a Batch Apex class are not supported, even with Database.AllowsCallouts. This is because the constructor runs outside the start, execute, or finish methods, where the interface’s effect is applied.
- Scheduled Apex:
- Callouts from Scheduled Apex classes are not supported, even with Database.AllowsCallouts, if the callout is made directly in the execute method of the Schedulable interface.
- Future Methods:
- Methods annotated with @future cannot be called from Batch Apex, as they are not compatible with batch processing.
- Use Queueable Apex instead for callout-heavy logic that needs to be chained or invoked from Batch Apex.
- Callout Limits in Batch Apex:
- Each start, execute, and finish method can make up to 100 callouts. If each record in the execute method requires one callout, the batch size must be 100 or less to avoid the System.LimitException: Too many callouts: 101 error.
- Testing Challenges
- Callouts cannot be made in Apex unit tests directly. You must use mock callouts with the Test.setMock method:
1 |
Test.setMock(HttpCalloutMock.class, new MyCalloutMock()); |
Conclusion
Database.Callout might not be the first tool Apex developers learn, but it’s a powerful enabler for real-world, scalable integrations, especially when working with large datasets and external systems.
If you’re looking to handle callouts efficiently in batch jobs, understanding this interface is a must. It’s a hidden gem that makes Apex integration more enterprise-ready
bluethinkinc_blog
2025-05-07