Another way to access the Tooling API via Apex is by simply establishing an HTTP connection and sending your query to /services/data/v48.0/tooling/query/?q=Select...
.
To see a full example of this (as well as deserializing the JSON response to access the data) see Audit Salesforce Org with Tooling API.
Here's a general gist of how it will work using an example that audits Salesforce Objects:
// Establish HTTP connection
HttpRequest req = new HttpRequest();
req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionID());
req.setHeader('Content-Type', 'application/json');
Http h = new Http();
// Gather our instance name to use for calls to the Tooling API
String instanceName = System.Url.getSalesforceBaseUrl().toExternalForm();
// Query 2 - Get Id, DeveloperName, Description, CreatedBy.Name, CreatedDate, LastModifiedBy.Name, LastModifiedDate from custom objects
req.setEndpoint(instanceName + '/services/data/v48.0/tooling/query/?q=Select+Id,DeveloperName,Description,CreatedBy.Name,CreatedDate,LastModifiedBy.Name,LastModifiedDate+From+CustomObject');
req.setMethod('GET');
HttpResponse res1 = h.send(req);
String responseBody = res1.getBody();
// Deserialize the response into an object
Map<String, Object> payload1 = (Map<String, Object>)JSON.deserializeUntyped(responseBody);
for (Object data : (List<Object>)payload1.get('records')){
Map<String, Object> record = (Map<String, Object>)data;
String id = (String)record.get('Id');
String formalizedId = id.substring(0,id.length() - 3);
ObjectData oData = resultsMap.get(formalizedId);
if(oData != null){
String lastModifiedName, createdName;
// Custom objects in a managed package do not include these details, so only attempt to add them if the createdByObject is not null
Object createdByObject = (Object)record.get('CreatedBy');
if(createdByObject != null){
Map<String, Object> createdByMap = (Map<String, Object>)createdByObject;
createdName = (String)createdByMap.get('Name');
}
Object lastModifiedByObject = (Object)record.get('LastModifiedBy');
if(lastModifiedByObject != null){
Map<String, Object> LastModifiedByMap = (Map<String, Object>)LastModifiedByObject;
lastModifiedName = (String)LastModifiedByMap.get('Name');
}
oData.description = (String)record.get('Description');
oData.createdDate = formatDateTime((String)record.get('CreatedDate'));
oData.lastModifiedDate = formatDateTime((String)record.get('LastModifiedDate'));
oData.createdBy = createdName;
oData.lastModifiedBy = lastModifiedName;
}
}