Study guide
10.3 Programmatic Schema Access (Describe)
Salesforce Certified Platform Developer I Exam
Track your progress as you read
Finished sections get a check mark.
1 Introduction & Exam Overview
1.1 Current Exam Facts 1.2 Blueprint Weights & Study Strategy
2 Platform Fundamentals
2.1 Multi-Tenant Architecture & Shared Resources 2.2 MVC Mapping on the Lightning Platform 2.3 Declarative vs Programmatic Decisions 2.4 Agentforce for Developers: Use Cases & Limitations
3 Data Modeling & Schema
3.1 Objects, Fields & Schema Design 3.2 Relationship Types & Implications 3.3 Schema Design Impact on Apex 3.4 Data Import & Export for Development
4 Platform Data Features
4.1 Formula Fields & Roll-Up Summaries 4.2 Core CRM Objects & Standard Schema 4.3 AppExchange Extensions & Platform Boundaries
5 Declarative Process Automation
5.1 Record-Triggered Flows & Declarative Automation 5.2 Approval Processes & Business Rules 5.3 When to Use Apex vs Declarative Tools
6 Apex Language Basics
6.1 Variables, Constants & Apex Data Types 6.2 Lists, Sets & Maps 6.3 Control Flow & Exception Handling 6.4 Classes, Interfaces & OOP Patterns
7 SOQL, SOSL & DML
7.1 SOQL Query Fundamentals 7.2 SOSL Search Fundamentals 7.3 DML Operations & Bulkification 7.4 Governor Limits & Transaction Boundaries
8 Apex Triggers & Order of Execution
8.1 Trigger Structure & Context Variables 8.2 Bulk Trigger Patterns & Best Practices 8.3 Save Order of Execution 8.4 Recursion, Cascading & Transaction Control
9 Asynchronous Apex
9.1 @future Methods 9.2 Queueable Apex 9.3 Batch Apex 9.4 Schedulable Apex & Job Orchestration
10 Security, Events & Schema Access
10.1 Sharing, FLS, CRUD & Injection Prevention 10.2 Platform Events Publish & Subscribe 10.3 Programmatic Schema Access (Describe)
11 Visualforce
11.1 Visualforce Pages & Markup 11.2 Standard, Custom Controllers & Extensions 11.3 Visualforce in Lightning Experience
12 Lightning Components
12.1 Lightning Component Framework Benefits 12.2 Aura Component Structure & Resources 12.3 Lightning Web Components Fundamentals 12.4 LWC Data Access: Wire, Imperative & Events
13 UI Composition & Apex Integration Patterns
13.1 Composing UI in Lightning Apps 13.2 UI Security, Performance & Accessibility Patterns 13.3 Apex Actions for Flow, LWC, and Agentforce
14 Apex Testing
14.1 Testing Framework & Coverage Requirements 14.2 Writing Unit Tests for Triggers & Classes 14.3 Test Data Strategies & Isolation 14.4 Executing Tests & Execute Anonymous Differences
15 Debugging & Developer Tools
15.1 Debug Logs & Monitoring 15.2 Developer Console, CLI, VS Code & Workbench 15.3 Sandboxes, Scratch Orgs & Environment Strategy
16 Deployment & Release
16.1 Change Sets & Metadata Deployment 16.2 Unlocked Packages, Metadata API & Salesforce DX 16.3 Release Readiness & Credential Maintenance
10.3 Programmatic Schema Access (Describe)
Key Takeaways Explain This Topic AI
Schema.sObjectType and describe APIs expose object and field metadata at runtime—labels, API names, type, and permission flags
fields.getMap() returns field API name to SObjectField tokens for dynamic validation and UI building
Use isAccessible, isCreateable, isUpdateable, isDeletable (object and field) before dynamic SOQL/DML in security-sensitive paths
Static tokens (Account.Name, Account.SObjectType) are compile-time safe; describe is for dynamic, data-driven, or multi-object code
- Describe results can be cached in a transaction to avoid redundant describe calls and governor pressure
- Last updated: August 2026
10.3 Programmatic Schema Access (Describe)
Quick Answer: Apex Schema describe APIs let you inspect objects and fields at runtime : names, types, and CRUD/FLS flags . Use static tokens when the shape is known at compile time; use describe + getMap for dynamic SOQL/UI and permission checks. Cache describe results within a transaction when called repeatedly.
Dynamic apps—metadata-driven UIs, generic utilities, managed packages that adapt to customer fields—cannot hard-code every field. Platform Developer I tests whether you can read schema safely and when describe beats static references.
- Schema Entry Points
- Schema.sObjectType (common, readable) Schema.DescribeSObjectResult accountDescribe =
- Schema.SObjectType.Account;
// Or from a token:
- Schema.DescribeSObjectResult d =
- Account.SObjectType.getDescribe();
- Useful object-level facts: String apiName = accountDescribe.getName(); // Account
- String label = accountDescribe.getLabel(); // Account
- Boolean canRead = accountDescribe.isAccessible();
- Boolean canCreate = accountDescribe.isCreateable();
- Boolean canUpdate = accountDescribe.isUpdateable();
- Boolean canDelete = accountDescribe.isDeletable();
- Boolean isCustom = accountDescribe.isCustom();
- Schema.describeSObjects
When you have API names as strings (package config, custom metadata): List<Schema.DescribeSObjectResult> describes =
Schema.describeSObjects(new String[]{ 'Account', 'Contact', 'My_Object__c' });
- for (Schema.DescribeSObjectResult r : describes) {
- System.debug(r.getName() + ' createable=' + r.isCreateable());
- }
This bulk describe is preferable to many one-off describes when processing a list of object names.
- From an sObject instance Account a = new Account(Name = 'Acme');
- Schema.DescribeSObjectResult d = a.getSObjectType().getDescribe();
Generic code that accepts SObject often uses getSObjectType() so one utility works for many types.
- Field Describes and getMap Map<String, Schema.SObjectField> fieldMap =
- Schema.SObjectType.Account.fields.getMap();
- // Keys are field API names (case-insensitive map behavior—still use correct API names)
- Schema.SObjectField revenueToken = fieldMap.get('AnnualRevenue');
- Schema.DescribeFieldResult revenueDescribe = revenueToken.getDescribe();
- Schema.DescribeFieldResult essentials String fieldLabel = revenueDescribe.getLabel();
Schema.DisplayType dtype = revenueDescribe.getType(); // CURRENCY, STRING, ...
- Boolean readable = revenueDescribe.isAccessible();
- Boolean createable = revenueDescribe.isCreateable();
- Boolean updateable = revenueDescribe.isUpdateable();
- Boolean nillable = revenueDescribe.isNillable();
Boolean calculated = revenueDescribe.isCalculated(); // formula, etc.
Integer length = revenueDescribe.getLength();
Permission flags for the exam:
- Method Meaning
- isAccessible() Running user can read the field (FLS read)
- isCreateable() User can set the field on insert
- isUpdateable() User can edit the field on update
- Object isDeletable() User can delete records of that type
These checks power manual CRUD/FLS enforcement when you are not using USER_MODE / stripInaccessible. public static void safeUpdateRevenue(Account acc, Decimal value) {
- if (!Schema.sObjectType.Account.isUpdateable()) {
- throw new AuraHandledException('Cannot update Account');
- }
- if (!Schema.sObjectType.Account.fields.AnnualRevenue.isUpdateable()) {
- throw new AuraHandledException('Cannot update Annual Revenue');
- }
- acc.AnnualRevenue = value;
- update acc;
- }
Note the fluent forms Schema.sObjectType.Account.fields.AnnualRevenue.isUpdateable() versus map-based dynamic names—both appear on exams.
Dynamic SOQL with Describe
Dynamic SOQL is powerful and dangerous. Pattern: validate identifiers via describe , then bind values. public static List<SObject> queryReadableFields(
- String objectApiName,
- List<String> requestedFields,
- String nameFilter
- ) {
- Map<String, Schema.SObjectType> globalDescribe = Schema.getGlobalDescribe();
- if (!globalDescribe.containsKey(objectApiName)) {
- throw new IllegalArgumentException('Unknown object');
- }
- Schema.DescribeSObjectResult objDescribe =
- globalDescribe.get(objectApiName).getDescribe();
- if (!objDescribe.isAccessible()) {
- throw new SecurityException('Object not accessible');
- }
- Map<String, Schema.SObjectField> fields = objDescribe.fields.getMap();
- List<String> safeFields = new List<String>();
- for (String f : requestedFields) {
- if (!fields.containsKey(f)) {
- continue; // or throw
- }
- Schema.DescribeFieldResult fd = fields.get(f).getDescribe();
- if (fd.isAccessible()) {
- safeFields.add(fd.getName());
- }
- }
- if (safeFields.isEmpty()) {
- return new List<SObject>();
- }
- String soql =
- 'SELECT ' + String.join(safeFields, ',') +
- ' FROM ' + objDescribe.getName() +
- ' WHERE Name = :nameFilter LIMIT 50';
- return Database.query(soql);
- }
Security stack in this pattern:
- Object exists (global describe / whitelist)
- Object isAccessible
- Each field exists and isAccessible
- Values use binds ( :nameFilter ), not concatenation
Never concatenate free-form object or field names from the UI without describe validation.
Static Tokens vs Describe
Approach Example Strengths When
Static field/object tokens Account.Name , Account.SObjectType Compile-time validation; rename-safe in many IDEs; clear Known schema in first-party code
Describe / getMap fields.getMap().get(apiName) Works with strings, custom metadata, multi-tenant variability Dynamic UIs, generic libraries, optional fields
USER_MODE SOQL WITH USER_MODE Platform enforces FLS/CRUD Prefer for straightforward queries when dynamic field lists are not required // Static — preferred when you know the field
Decimal rev = (Decimal)a.get(Account.AnnualRevenue);
- // Dynamic — when field API name arrives as data
- String fieldName = 'AnnualRevenue';
- a.get(fieldName);
If the exam asks for the safest approach for fixed Account.Name access, static references (or ordinary static SOQL) win. If the exam asks how to support admin-configured field sets , describe + field sets / getMap is correct.
Field sets (related idea)
Field sets are a declarative way to group fields; Apex can iterate field set members and still describe each for permissions. You do not need full field-set syntax memorized, but know describe underpins dynamic field iteration.
Caching Describe Results
Describe calls are lighter than they once were, but repeated global describes or field-map retrieval in tight loops still waste CPU and can contribute to limit pressure in bulk contexts.
- Transaction-level caching pattern: public class SchemaCache {
- private static Map<String, Schema.DescribeSObjectResult> objectCache =
- new Map<String, Schema.DescribeSObjectResult>();
- public static Schema.DescribeSObjectResult getObjectDescribe(String apiName) {
- if (!objectCache.containsKey(apiName)) {
- Schema.SObjectType t = Schema.getGlobalDescribe().get(apiName);
- if (t == null) {
- return null;
- }
- objectCache.put(apiName, t.getDescribe());
- }
- return objectCache.get(apiName);
- }
- }
Considerations:
Static caches last for the transaction (one request / one async execution)—good for bulk triggers that check the same object many times
Do not assume static caches survive across separate transactions or hold forever like a custom object
Prefer caching only what you need (specific objects) rather than always calling getGlobalDescribe() if a tighter API suffices
getGlobalDescribe() returns a large map—cache thoughtfully when used repeatedly
Modern Apex also offers describe options (for example, deferred/token styles in newer APIs) that reduce cost—exam focus remains: don’t re-describe blindly in loops , do check permissions , do prefer static tokens when schema is fixed .
- Combining Describe with stripInaccessible and USER_MODE
- Describe checks are one enforcement style. Alternatives: // Platform-enforced read
- List<Account> rows = [
- SELECT Id, Name, AnnualRevenue FROM Account WITH USER_MODE LIMIT 10
- ];
- // Or strip after system-mode query
- SObjectAccessDecision d = Security.stripInaccessible(
- AccessType.READABLE,
- rows
- );
Use describe when you need branching logic (hide a component, choose a different field, build a dynamic SELECT list). Use USER_MODE/stripInaccessible when you simply need the platform to enforce access on a known operation.
Common Exam Traps
Assuming describe alone runs the query securely — you must use isAccessible results before query/DML.
Dynamic SOQL with concatenated user input — describe the field list, still bind filter values.
Confusing object isCreateable with field isCreateable — both can matter for an insert of a populated field.
Using without sharing to “fix” FLS — sharing ≠ FLS.
Calling getGlobalDescribe in a per-record loop — cache or restructure.
- Practical Mini-Patterns
- Guard a dynamic update map: public static void applyUpdates(SObject record, Map<String, Object> values) {
- Schema.DescribeSObjectResult od = record.getSObjectType().getDescribe();
- if (!od.isUpdateable()) {
- return;
- }
- Map<String, Schema.SObjectField> fmap = od.fields.getMap();
- for (String key : values.keySet()) {
- if (fmap.containsKey(key) && fmap.get(key).getDescribe().isUpdateable()) {
- record.put(key, values.get(key));
- }
- }
- update record;
- }
Discover custom fields only: for (Schema.SObjectField f : Schema.SObjectType.Account.fields.getMap().values()) {
- Schema.DescribeFieldResult fd = f.getDescribe();
- if (fd.isCustom() && fd.isAccessible()) {
- System.debug(fd.getName());
- }
- }
- Summary for Platform Developer I
Schema describe is the reflection layer of Salesforce data: SObjectType , describeSObjects , fields.getMap , and DescribeFieldResult expose structure and isAccessible / isCreateable / isUpdateable / isDeletable . Pair describe with bind variables for safe dynamic SOQL, prefer static tokens when the model is fixed, and cache describe results across bulk iterations. Together with sharing keywords and USER_MODE/stripInaccessible, describe completes the security and metadata toolkit tested on the exam.