Documenting a Salesforce data model is a standard task during enterprise audits, managed package development, and migrations. Gathering object names, API field names, labels, and data types manually through the Object Manager takes hours. By leveraging Dynamic Apex Schema Describe, you can extract schema definitions across standard, custom, and namespaced objects into a clean CSV file within seconds.
1. How Dynamic Schema Describe Works
Apex provides the Schema namespace to inspect object tokens and field definitions dynamically at runtime:
Schema.getGlobalDescribe(): Returns a map of all sObject tokens in the organization (Map<String, Schema.SObjectType>).SObjectType.getDescribe(): Provides access to object-level metadata, including custom status, labels, and field maps.Schema.DescribeSObjectResult.fields.getMap(): Returns a map containing every field token associated with the target sObject.Schema.DescribeFieldResult: Exposes granular field metadata such as API name, label, data type, length, and help text.
2. Optimized Apex Implementation
The following production-ready utility class filters objects by namespace or custom naming conventions, builds an RFC-compliant CSV string with escaped values, and dispatches the file as an email attachment.
SchemaDataDictionaryExporter.cls)
public with sharing class SchemaDataDictionaryExporter {
public static void generateAndEmailDictionary(String targetNamespace, String recipientEmail) {
if (String.isBlank(recipientEmail)) {
throw new IllegalArgumentException('A valid recipient email address must be provided.');
}
List<String> csvRows = new List<String>();
// 1. Define CSV Header
csvRows.add('Object API Name,Object Label,Field API Name,Field Label,Data Type,Is Custom');
// 2. Fetch all SObjects in the org
Map<String, Schema.SObjectType> globalDescribe = Schema.getGlobalDescribe();
for (String objName : globalDescribe.keySet()) {
Schema.SObjectType objType = globalDescribe.get(objName);
Schema.DescribeSObjectResult objDescribe = objType.getDescribe();
// Filter by namespace or target criteria (e.g., custom objects only)
Boolean matchesFilter = false;
if (String.isNotBlank(targetNamespace)) {
matchesFilter = objName.startsWithIgnoreCase(targetNamespace.toLowerCase() + '__') && objName.endsWithIgnoreCase('__c');
} else {
// If no namespace provided, export all custom objects
matchesFilter = objName.endsWithIgnoreCase('__c');
}
if (!matchesFilter) {
continue;
}
String objectApi = objDescribe.getName();
String objectLabel = escapeCsv(objDescribe.getLabel());
// 3. Inspect fields for matching object
Map<String, Schema.SObjectField> fieldsMap = objDescribe.fields.getMap();
for (String fieldKey : fieldsMap.keySet()) {
Schema.DescribeFieldResult fieldDescribe = fieldsMap.get(fieldKey).getDescribe();
String fieldApi = fieldDescribe.getName();
String fieldLabel = escapeCsv(fieldDescribe.getLabel());
String fieldType = String.valueOf(fieldDescribe.getType());
Boolean isCustom = fieldDescribe.isCustom();
csvRows.add(String.format('{0},{1},{2},{3},{4},{5}', new List<String>{
objectApi,
objectLabel,
fieldApi,
fieldLabel,
fieldType,
String.valueOf(isCustom)
}));
}
}
// 4. Construct CSV File Blob
String csvContent = String.join(csvRows, '\n');
Blob csvBlob = Blob.valueOf(csvContent);
// 5. Build Email Attachment
Messaging.EmailFileAttachment attachment = new Messaging.EmailFileAttachment();
attachment.setFileName('Salesforce_Data_Dictionary_' + DateTime.now().format('yyyyMMdd') + '.csv');
attachment.setContentType('text/csv');
attachment.setBody(csvBlob);
// 6. Send SingleEmailMessage
Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
email.setToAddresses(new List<String>{ recipientEmail });
email.setSubject('Salesforce Schema Data Dictionary Export');
email.setPlainTextBody('Hello,\n\nPlease find attached the exported schema and field definitions CSV report.\n\nGenerated automatically via Apex.');
email.setFileAttachments(new List<Messaging.EmailFileAttachment>{ attachment });
Messaging.sendEmail(new List<Messaging.SingleEmailMessage>{ email });
}
// Helper to sanitize and escape CSV strings containing commas or quotes
private static String escapeCsv(String value) {
if (String.isBlank(value)) {
return '""';
}
if (value.contains(',') || value.contains('"') || value.contains('\n') || value.contains('\r')) {
value = value.replaceAll('"', '""');
return '"' + value + '"';
}
return value;
}
}
- Heap Usage: Avoid building large CSV strings via repeated
+concatenation. UseList<String>andString.join()to minimize transient memory allocations. - CPU Time Management: Describing every object in an enterprise org with hundreds of packages can hit the 10,000 ms synchronous CPU limit. Always filter by prefix or namespace.
- CSV Sanitization: Field labels often contain commas (e.g.,
"Address, Line 1"). Always escape double quotes and wrap values in quotes to prevent column misalignment in spreadsheet software.
3. How to Execute from Anonymous Apex
You can run this report directly from the Developer Console or VS Code without creating extra UI components.
// Example 1: Export all custom objects in the org
SchemaDataDictionaryExporter.generateAndEmailDictionary(null, 'admin@yourcompany.com');
// Example 2: Export only objects belonging to a specific managed package namespace
SchemaDataDictionaryExporter.generateAndEmailDictionary('c2g', 'developer@yourcompany.com');
4. Common Traps & Governor Limit Rules
If your sandbox has
Setup > Deliverability set to No Access, the email will fail silently without delivery. Additionally, in orgs with thousands of custom objects, attempting to describe every standard and managed object in a single transaction can exceed the 6 MB synchronous Apex heap limit. For full-org exports, batch the describe calls using Batch Apex.
- Use Batch Apex for Org-Wide Audits: If you must document all standard and custom objects simultaneously, split the object token list across a
Database.Batchableclass to reset CPU and Heap limits per batch chunk. - Include Field Data Types: Exporting
fieldDescribe.getType()alongside labels provides immediate visibility into picklist, lookup, and formula dependencies.
Summary
Automating data dictionary generation with Apex Schema Describe eliminates manual documentation overhead. By querying Schema.getGlobalDescribe(), filtering by namespace or object suffixes, escaping string fields safely, and emailing the generated CSV, administrators and developers can audit their Salesforce metadata quickly and accurately.