Skip to content

Repository files navigation

RestClientLib

A comprehensive Salesforce Apex library that provides a clean, type-safe, and extensible framework for making REST API callouts. Built with enterprise security and developer productivity in mind, RestClientLib eliminates the complexity of HTTP callouts while providing powerful features like async processing, comprehensive testing utilities, and fluent API design.

Latest Release

Version: 1.0.8
Tag: v1.0.8
Release Type: latest
Package Version ID: 04tQm000002zB8fIAE
Package:REST-API-Library-v1.0.8.zip
Release Date: 2025-09-24

Quick Install

Change summary

Package Version ID: 04tQm000002zB8fIAE

What RestClientLib Does

RestClientLib provides three distinct approaches to REST API integration, each designed for different use cases:

1. RestClientLib - Extensible API Clients

Extend this virtual class to create dedicated API clients that are locked to specific Named Credentials. Perfect for reusable integrations with external services.

2. RestClient - Static Utility Methods

Use static methods for one-off API calls without creating dedicated classes. Ideal for simple integrations and quick prototypes.

3. AsyncRestClient - Background Processing

Queue API calls for asynchronous execution to avoid governor limits. Essential for high-volume integrations and non-blocking operations.

Key Features

  • Named Credential Integration: Secure API callouts using Salesforce's built-in credential management
  • Type-Safe HTTP Verbs: Strongly-typed enum for HTTP methods (GET, POST, PUT, PATCH, DELETE, HEAD)
  • Fluent API Design: Chainable methods for building complex API calls
  • Async Processing: Queueable-based background processing with finalizer pattern
  • Comprehensive Testing: Built-in mock factory for easy unit testing
  • Automatic Headers: Default JSON headers with custom header support
  • URL Encoding: Automatic query parameter encoding
  • PATCH Support: Handles PATCH requests via POST with _HttpMethod=PATCH parameter
  • Enhanced Logging: Optional Nebula integration with System.debug fallback for comprehensive API call logging

Common Use Cases

Third-Party API Integrations

Connect Salesforce to external services like payment processors, CRM systems, or marketing platforms.

Webhook Implementations

Send real-time notifications to external systems when Salesforce records change.

Data Synchronization

Keep Salesforce data in sync with external databases or applications.

Analytics and Reporting

Send data to analytics platforms, business intelligence tools, or custom dashboards.

Microservices Communication

Integrate with microservices architectures and modern API-first applications.

Rate-Limited API Integration

Manage API rate limits and avoid throttling by using AsyncRestClient for background processing.

Enhanced Logging with NebulaAdapter

RestClientLib includes optional integration with the Nebula logging framework for comprehensive API call logging. The NebulaAdapter provides structured logging when Nebula is available, with graceful fallback to System.debug when it's not.

Features

  • Automatic Request/Response Logging: Logs HTTP method, endpoint, headers, body, and response details
  • Record Association: Links API calls to specific SObject records for easy traceability
  • Graceful Degradation: Works with or without Nebula installed
  • Zero Dependencies: No compilation dependencies on external packages
  • Structured Logging: Rich, queryable logs when using Nebula
  • Fallback Logging: System.debug output when Nebula isn't available

Usage

// Enhanced logging with record contextHttpResponseresponse=restLib.get('/api/users', accountRecord);
HttpResponseresponse=RestClient.makeApiCall('MyAPI', apiCall, accountRecord);
// Async calls with loggingSystem.enqueueJob(newAsyncRestClient('MyAPI', apiCall, MyFinalizer.class, accountRecord));
// Direct NebulaAdapter usageNebulaAdapter.info('API call completed', accountRecord);
NebulaAdapter.logHttpRequest('Request details', httpRequest, headersToLog, accountRecord);
NebulaAdapter.logHttpResponse('Response details', httpResponse, accountRecord);

Logging Levels

  • info() - General information logging
  • debug() - Detailed debugging information
  • error() - Error logging with exception details
  • logHttpRequest() - HTTP request logging with customizable headers
  • logHttpResponse() - HTTP response logging with status and body

Quick Start

1. Extensible API Clients (Recommended)

Create dedicated API clients by extending RestClientLib:

publicclassSlackApiClientextendsRestClientLib {
publicSlackApiClient() {
super('Slack_Named_Credential');
}
publicHttpResponsesendMessage(Stringchannel, Stringtext) {
Map<String, Object> message=newMap<String, Object>{
'channel'=>channel,
'text'=>text
};
returnpost('/api/chat.postMessage', JSON.serialize(message));
}
publicHttpResponsegetUserInfo(StringuserId) {
returnget('/api/users.info', 'user='+userId);
}
publicHttpResponseupdateUser(StringuserId, Map<String, Object> userData) {
returnpatch('/api/users/'+userId, JSON.serialize(userData));
}
// Using the generic makeApiCall method for custom scenariospublicHttpResponsecustomRequest(Stringendpoint, Map<String, String> params) {
StringqueryString='';
for (Stringkey:params.keySet()) {
queryString+=key+'='+EncodingUtil.urlEncode(params.get(key), 'UTF-8') +'&';
}
returnmakeApiCall(HttpVerb.GET, endpoint, queryString.removeEnd('&'));
}
}
// UsageSlackApiClientslack=newSlackApiClient();
HttpResponseresponse=slack.sendMessage('#general', 'Hello from Salesforce!');

2. Static Utility Methods

For one-off API calls without creating dedicated classes:

// Simple GET requestRestLibApiCallapiCall=newRestLibApiCall(
HttpVerb.GET, '/api/users', 'active=true&limit=10', ''
);
HttpResponseresponse=RestClient.makeApiCall('MyAPI_Named_Credential', apiCall);
// POST request with custom headersRestLibApiCallpostCall=RestLibApiCall.create()
.usingPost()
.withPath('/api/webhooks')
.withBody(JSON.serialize(webhookData))
.withHeader('X-API-Key', 'your-api-key')
.withTimeout(30000);
HttpResponsepostResponse=RestClient.makeApiCall('Webhook_Named_Credential', postCall);

3. Async Processing

For high-volume or non-blocking operations:

// Create your finalizer classpublicclassWebhookFinalizerextendsAsyncRestLibFinalizer {
publicoverridevoidexecute(HttpResponseresponse) {
if (response.getStatusCode() ==200) {
System.debug('Webhook sent successfully');
// Process successful response
} else {
System.debug('Webhook failed: '+response.getBody());
// Handle error or retry logic
}
}
}
// Queue the async callRestLibApiCallapiCall=RestLibApiCall.create()
.usingPost()
.withPath('/api/webhooks')
.withBody(JSON.serialize(webhookData));
System.enqueueJob(newAsyncRestClient(
'Webhook_Named_Credential', apiCall, WebhookFinalizer.class
));

API Reference

RestClientLib Methods (Protected - Extend to Use)

MethodDescriptionParameters
get(String path)GET request to specified pathpath - API endpoint path
get(String path, String query)GET request with query parameterspath - API endpoint path, query - Query string
get(String path, SObject relatedRecord)GET request with logging contextpath - API endpoint path, relatedRecord - Record for logging
get(String path, String query, SObject relatedRecord)GET request with query and logging contextpath - API endpoint path, query - Query string, relatedRecord - Record for logging
post(String path, String body)POST request with bodypath - API endpoint path, body - Request body
post(String path, String query, String body)POST request with query and bodypath - API endpoint path, query - Query string, body - Request body
post(String path, String body, SObject relatedRecord)POST request with body and logging contextpath - API endpoint path, body - Request body, relatedRecord - Record for logging
post(String path, String query, String body, SObject relatedRecord)POST request with query, body, and logging contextpath - API endpoint path, query - Query string, body - Request body, relatedRecord - Record for logging
put(String path, String body)PUT request with bodypath - API endpoint path, body - Request body
put(String path, String query, String body)PUT request with query and bodypath - API endpoint path, query - Query string, body - Request body
put(String path, String body, SObject relatedRecord)PUT request with body and logging contextpath - API endpoint path, body - Request body, relatedRecord - Record for logging
put(String path, String query, String body, SObject relatedRecord)PUT request with query, body, and logging contextpath - API endpoint path, query - Query string, body - Request body, relatedRecord - Record for logging
patch(String path, String body)PATCH request with bodypath - API endpoint path, body - Request body
patch(String path, String query, String body)PATCH request with query and bodypath - API endpoint path, query - Query string, body - Request body
patch(String path, String body, SObject relatedRecord)PATCH request with body and logging contextpath - API endpoint path, body - Request body, relatedRecord - Record for logging
patch(String path, String query, String body, SObject relatedRecord)PATCH request with query, body, and logging contextpath - API endpoint path, query - Query string, body - Request body, relatedRecord - Record for logging
del(String path)DELETE requestpath - API endpoint path
del(String path, String query)DELETE request with query parameterspath - API endpoint path, query - Query string
del(String path, SObject relatedRecord)DELETE request with logging contextpath - API endpoint path, relatedRecord - Record for logging
del(String path, String query, SObject relatedRecord)DELETE request with query and logging contextpath - API endpoint path, query - Query string, relatedRecord - Record for logging
makeApiCall(HttpVerb method, String path)Generic API call with method and pathmethod - HTTP verb, path - API endpoint path
makeApiCall(HttpVerb method, String path, String query)Generic API call with method, path, and querymethod - HTTP verb, path - API endpoint path, query - Query string
makeApiCall(HttpVerb method, String path, String query, String body)Generic API call with all parametersmethod - HTTP verb, path - API endpoint path, query - Query string, body - Request body
makeApiCall(HttpVerb method, String path, SObject relatedRecord)Generic API call with method, path, and logging contextmethod - HTTP verb, path - API endpoint path, relatedRecord - Record for logging
makeApiCall(HttpVerb method, String path, String query, SObject relatedRecord)Generic API call with method, path, query, and logging contextmethod - HTTP verb, path - API endpoint path, query - Query string, relatedRecord - Record for logging
makeApiCall(HttpVerb method, String path, String query, String body, SObject relatedRecord)Generic API call with all parameters and logging contextmethod - HTTP verb, path - API endpoint path, query - Query string, body - Request body, relatedRecord - Record for logging

RestClient Static Methods

MethodDescriptionParameters
makeApiCall(String namedCredential, RestLibApiCall apiCall)Make a REST calloutnamedCredential - Named Credential name, apiCall - API call configuration
makeApiCall(String namedCredential, RestLibApiCall apiCall, SObject relatedRecord)Make a REST callout with logging contextnamedCredential - Named Credential name, apiCall - API call configuration, relatedRecord - Record for logging

RestLibApiCall Fluent API

MethodDescriptionReturns
create()Create new API call instanceRestLibApiCall
withMethod(HttpVerb method)Set HTTP method (generic)RestLibApiCall
usingGet()Set HTTP method to GETRestLibApiCall
usingPost()Set HTTP method to POSTRestLibApiCall
usingPut()Set HTTP method to PUTRestLibApiCall
usingPatch()Set HTTP method to PATCHRestLibApiCall
usingDelete()Set HTTP method to DELETERestLibApiCall
usingHead()Set HTTP method to HEADRestLibApiCall
withPath(String path)Set API endpoint pathRestLibApiCall
withQuery(String query)Set query parametersRestLibApiCall
withBody(String body)Set request bodyRestLibApiCall
withHeaders(Map<String,String> headers)Set custom headersRestLibApiCall
withHeader(String key, String value)Add single headerRestLibApiCall
withTimeout(Integer timeout)Set timeout in millisecondsRestLibApiCall

RestLibApiCall Constructors

ConstructorDescriptionParameters
RestLibApiCall(HttpVerb method, String path, String query, String body)Basic constructor with default headersmethod - HTTP verb, path - API path, query - Query string, body - Request body
RestLibApiCall(HttpVerb method, String path, String query, String body, Map<String,String> headers)Full constructor with custom headersmethod - HTTP verb, path - API path, query - Query string, body - Request body, headers - Custom headers

NebulaAdapter Methods

MethodDescriptionParameters
isAvailable()Check if Nebula is installed and availableNone
info(String message, SObject record)Log info message with record contextmessage - Log message, record - Associated SObject record
debug(String message, SObject record)Log debug message with record contextmessage - Log message, record - Associated SObject record
error(String message, SObject record, Exception ex)Log error message with record and exception contextmessage - Log message, record - Associated SObject record, ex - Exception details
logHttpRequest(String title, HttpRequest req, List<String> headersToLog, SObject record)Log HTTP request detailstitle - Log title, req - HttpRequest object, headersToLog - Headers to include, record - Associated SObject record
logHttpResponse(String title, HttpResponse res, SObject record)Log HTTP response detailstitle - Log title, res - HttpResponse object, record - Associated SObject record
save()Persist accumulated log entriesNone

HttpVerb Enum

ValueDescription
GETHTTP GET method
POSTHTTP POST method
PUTHTTP PUT method
PATCHHTTP PATCH method (converted to POST with _HttpMethod=PATCH)
DELHTTP DELETE method
HEADHTTP HEAD method

Method Reference

When to Use Each Approach

MethodUse CaseBenefitsExample
Extend RestClientLibReusable API clients for specific servicesType-safe, locked to Named Credential, easy to testSlackApiClient, StripeApiClient
RestClient.makeApiCall()One-off API calls, quick prototypesNo class creation needed, simple static callsWebhooks, notifications, simple integrations
RestLibApiCall fluent APIComplex API calls with custom headers/timeoutsChainable methods, readable code, flexibleCustom authentication, complex payloads
AsyncRestClientHigh-volume operations, non-blocking callsAvoids governor limits, background processingBulk data sync, non-critical notifications
HttpCalloutMockFactoryUnit testing API integrationsEasy mocking, multiple response scenariosTest success/error cases, edge conditions

Method Comparison

FeatureRestClientLibRestClientRestLibApiCallAsyncRestClient
Named CredentialRequired in constructorRequired per callRequired per callRequired in constructor
Type SafetyHigh (protected methods)Medium (static methods)High (fluent API)High (constructor)
ReusabilityHigh (extend once)Low (per call)Medium (reuse instances)High (queue multiple)
TestingEasy (mock once)Easy (mock per call)Easy (mock per call)Medium (test finalizer)
Governor LimitsSubject to callout limitsSubject to callout limitsSubject to callout limitsAvoids callout limits
ComplexityLow (simple methods)Low (static calls)Medium (fluent chaining)Medium (finalizer pattern)

Testing

RestClientLib includes comprehensive testing utilities through the HttpCalloutMockFactory class:

Basic Mock Setup

@isTest
publicclassMyApiClientTest {
@isTest
staticvoidtestGetUsers() {
// Set up mock responseHttpCalloutMockFactory.setMock('MyAPI_Named_Credential', 200, 'OK', '{"users": [{"id": 1, "name": "John"}]}', newMap<String, String>());
// Test your clientMyApiClientclient=newMyApiClient();
HttpResponseresponse=client.getUsers();
// AssertionsSystem.assertEquals(200, response.getStatusCode());
System.assertEquals('{"users": [{"id": 1, "name": "John"}]}', response.getBody());
}
@isTest
staticvoidtestMultipleCallouts() {
// Set up multiple mock responsesList<HttpResponse> responses=newList<HttpResponse>{
HttpCalloutMockFactory.generateHttpResponse(200, 'OK', '{"users": []}', newMap<String, String>()),
HttpCalloutMockFactory.generateHttpResponse(201, 'Created', '{"id": 123}', newMap<String, String>())
};
HttpCalloutMockFactory.setMock('MyAPI_Named_Credential', responses);
// Test multiple callsMyApiClientclient=newMyApiClient();
HttpResponsegetResponse=client.getUsers();
HttpResponsepostResponse=client.createUser('{"name": "Jane"}');
System.assertEquals(200, getResponse.getStatusCode());
System.assertEquals(201, postResponse.getStatusCode());
}
}

Testing Async Operations

@isTest
publicclassWebhookFinalizerTest {
@isTest
staticvoidtestSuccessfulWebhook() {
// Set up mockHttpCalloutMockFactory.setMock('Webhook_Named_Credential', 200, 'OK', '{"status": "success"}', newMap<String, String>());
// Test finalizerTest.startTest();
WebhookFinalizerfinalizer=newWebhookFinalizer();
finalizer.response=HttpCalloutMockFactory.generateHttpResponse(200, 'OK', '{"status": "success"}', newMap<String, String>());
finalizer.execute(finalizer.response);
Test.stopTest();
// Verify behaviorSystem.assertEquals(200, finalizer.response.getStatusCode());
}
}

Installation

Option 1: Install Package (Recommended)

  1. Install: Click the install link in the Latest Release section above
  2. Configure: Set up Named Credentials for your APIs

Option 2: Deploy from Source

git clone https://github.com/your-org/RestClientLib.git
cd RestClientLib
sf project deploy start --source-dir force-app

Requirements

  • Named Credentials configured

Package Information

What's Included

  • RestLib - Base virtual class with core REST callout functionality
  • RestClient - Static wrapper for one-off API calls
  • RestClientLib - Extensible virtual class for creating API clients
  • RestLibApiCall - Fluent API builder for complex API calls
  • HttpVerb - Type-safe enum for HTTP methods
  • AsyncRestClient - Queueable implementation for async processing
  • AsyncRestLibFinalizer - Abstract class for handling async responses
  • NebulaAdapter - Optional logging integration with Nebula framework
  • HttpCalloutMockFactory - Comprehensive testing utilities
  • RestLibTests - Complete test suite
  • NebulaAdapter_Test - Test suite for NebulaAdapter functionality

Package Details

  • Size: ~50KB
  • API Version: 50.0+
  • Dependencies: None
  • Installation: Upload package zip file

Architecture

RestClientLib follows a layered architecture:

  1. RestLib - Core functionality and HTTP request handling
  2. RestClient - Static utility layer for simple use cases
  3. RestClientLib - Extensible layer for dedicated API clients
  4. RestLibApiCall - Fluent API builder for complex scenarios
  5. AsyncRestClient - Queueable layer for background processing
  6. NebulaAdapter - Optional logging layer with Nebula integration and System.debug fallback

Support

License

This package is provided as-is for use in Salesforce development projects.

About

This is a general class and package for modularizing any API call with Apex.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages