Skip to main content

Export Salesforce Objects and Fields to CSV Using Apex Schema Describe

In plain words: Dynamic Schema Describe lets you inspect the structure of your Salesforce org programmatically. Instead of clicking through Setup to document objects and fields by hand, an Apex script can read the metadata, generate a CSV data dictionary, and email it directly to your inbox.

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.

Create object, fields CSV file in Salesforce using Apex

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.

Apex Utility Class: (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;
    }
}
360 Schema Extraction Architecture Card:
  • Heap Usage: Avoid building large CSV strings via repeated + concatenation. Use List<String> and String.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.

Anonymous Apex Execution Script:
// 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

Developer Trap: Email Deliverability and Heap Limit Crashes
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.
Core Rule: Always sanitize field and object labels with CSV quotation escaping, and use targeted namespace filtering to keep Schema Describe execution within CPU time limits.
  • 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.Batchable class 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.