Your PDF generation architecture needs a fundamental redesign to handle large documents within Salesforce’s governor limits. Here’s the comprehensive solution:
1. API Gateway Request Size Limits - Implement Asynchronous Pattern:
The 6MB REST API response limit is absolute and cannot be increased. Never return large PDFs directly in API responses. Instead, implement this pattern:
@HttpPost
global static String generateContractPDF(String contractId) {
// Immediate response with job ID
String jobId = generateAsyncPDF(contractId);
return JSON.serialize(new Map<String, String>{
'status' => 'processing',
'jobId' => jobId
});
}
The client polls a status endpoint until PDF is ready, then retrieves via ContentVersion API.
2. PDF Generation via Apex REST - Optimize and Store Pattern:
Replace direct PDF return with a storage-based approach:
Step 1: Generate PDF Asynchronously
@future(callout=true)
public static void generateAsyncPDF(Id contractId) {
PageReference pdfPage = Page.ContractTemplate;
pdfPage.getParameters().put('id', contractId);
Blob pdfBlob = pdfPage.getContentAsPDF();
storeAsContentVersion(pdfBlob, contractId);
}
Step 2: Store in Salesforce Files
ContentVersion cv = new ContentVersion();
cv.Title = 'Contract_' + contractId + '.pdf';
cv.PathOnClient = 'contract.pdf';
cv.VersionData = pdfBlob;
cv.IsMajorVersion = true;
insert cv;
Step 3: Link to Contract Record
Create ContentDocumentLink to associate the file with the contract record for easy retrieval.
3. Document Optimization Strategies - Reduce PDF Size:
Template Optimization:
Your Visualforce template likely contains inefficiencies. Implement these optimizations:
Remove Unnecessary Styling:
- Avoid embedded fonts (use system fonts)
- Minimize CSS complexity
- Remove redundant styling
- Use CSS classes instead of inline styles
Optimize Images:
- Compress logos to < 50KB
- Use SVG for vector graphics when possible
- Lazy-load images only when needed
- Set explicit image dimensions
Reduce Data Volume:
// Instead of loading all line items:
List<ContractLineItem> items = [
SELECT Id, Name, Quantity, Price
FROM ContractLineItem
WHERE ContractId = :contractId
LIMIT 1000
];
// Summarize if count exceeds threshold:
if (items.size() > 100) {
// Generate summary view
// Store detailed items in appendix
}
Conditional Content Loading:
Only include detailed sections when necessary:
- Legal terms: Link to standard terms document instead of embedding
- Historical data: Summarize instead of full detail
- Addendums: Generate separately and reference
Advanced Architecture Options:
Option A: Chunked PDF Generation
For extremely large contracts, generate multiple PDFs:
public class ContractPDFGenerator {
public void generateMultiPartPDF(Id contractId) {
// Part 1: Main contract (< 5MB)
generateMainContract(contractId);
// Part 2: Line items (< 5MB)
generateLineItemsAppendix(contractId);
// Part 3: Legal terms (< 5MB)
generateLegalTerms(contractId);
// Create manifest
createPDFManifest(contractId);
}
}
Option B: External PDF Generation Service
For consistently large documents, use an external service:
- Send contract data to external PDF service via callout
- Service generates PDF without Salesforce limits
- Service uploads to Salesforce via Files API
- Notify user via Platform Event
This bypasses Salesforce PDF generation limits entirely.
Option C: Hybrid Approach
Implement intelligent routing:
if (estimatedPDFSize < 5000000) {
// Generate in Salesforce
generateInternalPDF(contractId);
} else {
// Route to external service
generateExternalPDF(contractId);
}
Implementation Best Practices:
Caching Strategy:
Avoid regenerating unchanged PDFs:
if (contract.LastModifiedDate > lastPDFGenerationDate) {
regeneratePDF();
} else {
returnCachedPDF();
}
Progress Tracking:
For large PDF generation, provide status updates:
- Store generation progress in custom field
- Update via platform events
- Show real-time progress in UI
Error Handling:
Implement robust error recovery:
try {
Blob pdf = page.getContentAsPDF();
} catch (LimitException e) {
if (e.getMessage().contains('HEAP_SIZE')) {
// Trigger chunked generation
generateChunkedPDF(contractId);
}
}
Performance Monitoring:
Track PDF generation metrics:
- Average generation time by contract size
- Failure rate for different document types
- Cache hit rate
- Storage consumption
API Design for Clients:
Provide a clean API experience:
POST /contracts/{id}/pdf - Initiates generation, returns job ID
GET /contracts/{id}/pdf/status/{jobId} - Check generation status
GET /contracts/{id}/pdf/download - Retrieve ContentVersion download URL
This decouples generation from retrieval and works within all Salesforce limits.
Testing Strategy:
- Test with contracts of varying sizes (1MB, 3MB, 5MB, 10MB)
- Verify async processing completes successfully
- Validate ContentVersion storage and retrieval
- Test concurrent PDF generation requests
- Verify cleanup of old PDF versions
By implementing this architecture, you can handle contract PDFs of any size while staying within Salesforce governor limits and providing a professional API experience.