SfSkills Domains Docs GitHub

Home / apex / apex-salesforce-id-patterns

apex-salesforce-id-patterns

Use when working with Salesforce Ids in Apex — validating Id format, detecting the target sObject type from a string Id, or safely handling 15 vs 18-character Ids. Trigger keywords: Id prefix, Id.valueOf, Id.getSobjectType, 15-char, 18-char, case-insensitive Id. NOT for hardcoded Profile or RecordType Ids — use apex/apex-hardcoded-id-elimination. NOT for bulk Id collection patterns — use apex/apex-collections-patterns.

Open the skill folder on GitHub


Apex Salesforce Id Patterns

Activate this skill when Apex must reason about a Salesforce Id — validating it came from a real record, determining which sObject it points to, or comparing Ids across the 15/18-character boundary. It covers the difference between String and Id types, the prefix-based describe lookup, and case sensitivity of 15-character Ids.


Before Starting

Gather this context before working on anything in this domain:


Core Concepts

15 vs 18 Character Ids

Apex Id Is A Typed Value, Not Just A String

Detecting sObject Type From An Id

Two mechanisms, same result:

  1. myId.getSobjectType() — returns the Schema.SObjectType for a typed Id. Cheap, no SOQL.
  2. Schema.getGlobalDescribe().get(myId.substring(0,3)) — prefix lookup, but not all prefixes are unique (some managed packages overlap, and custom objects share the a0* range).

Rule: always prefer getSobjectType() on a typed Id. Fall back to prefix only if you have a String you cannot type yet.

Key Prefixes To Recognize

PrefixsObject
001Account
003Contact
005User
006Opportunity
00QLead
500Case
a0x–a0z, a0*Custom objects (prefix is org-specific)
00DOrganization
0F0Folder

Do NOT hardcode custom object prefixes. They are org-specific.


Common Patterns

Validate A User-Supplied Id Before Using It

When to use: A controller receives an Id from a URL, Experience Cloud form, or external integration.

How it works:

public static Account fetchAccount(String rawId) {
    Id accountId;
    try {
        accountId = (Id) rawId;
    } catch (System.StringException e) {
        throw new AuraHandledException('Invalid Id format.');
    }
    if (accountId.getSobjectType() != Account.SObjectType) {
        throw new AuraHandledException('Id does not belong to an Account.');
    }
    return [SELECT Id, Name FROM Account WHERE Id = :accountId WITH USER_MODE LIMIT 1];
}

Why not the alternative: Querying directly with an unvalidated string either throws QueryException (wrong prefix) or a less helpful StringException with no caller guidance.

Detect Type Across A Heterogeneous List Of Ids

When to use: An Apex method accepts List<Id> where entries may belong to different sObjects.

How it works:

Map<Schema.SObjectType, List<Id>> byType = new Map<Schema.SObjectType, List<Id>>();
for (Id idValue : inputIds) {
    Schema.SObjectType type = idValue.getSobjectType();
    if (!byType.containsKey(type)) byType.put(type, new List<Id>());
    byType.get(type).add(idValue);
}
// Issue one SOQL per type instead of per record.

Normalize 15-Char Id To 18-Char For String Compare

When to use: Comparing an Id from an external system or CSV (often 15-char) with an Id from SOQL (always 18-char).

How it works: Cast through Id: Id normalized = (Id) fifteenCharString; — the typed Id is 18 chars and case-insensitive-safe for string compare.


Decision Guidance

SituationRecommended ApproachReason
Comparing two Ids you ownId == IdApex normalizes to 18 chars automatically
Comparing strings from mixed sourcesCast both to Id first15 vs 18 char mismatch is silent
Detecting sObject from a typed Idid.getSobjectType()No SOQL, no describe overhead
Detecting sObject from a string when you cannot trust the prefixId.valueOf(str).getSobjectType()Throws if not a legal Id
Validating external-system Idstry { (Id) str; } catch ...Give a helpful error, not a raw exception
Routing logic by objectSwitch on Schema.SObjectTypeSafer than hardcoded prefixes

  1. Identify every point the Id crosses a trust boundary — URL, DTO, CSV, external API response.
  2. Type the parameter as Id, not String, everywhere possible.
  3. Where the source is untrusted, wrap the cast in a try/catch and surface a clear error.
  4. Before SOQL, confirm sObject with getSobjectType() (not a string prefix compare).
  5. Add a negative test: pass an invalid string, an Id for the wrong object, and an empty string; each should fail cleanly.

Review Checklist


Salesforce-Specific Gotchas

  1. 15-char strings do not equal 18-char strings — always cast to Id before a string compare.
  2. Custom object prefixes are org-specific — a03 in one org is a different object in another.
  3. Id.valueOf(null) throws — guard null separately.
  4. Some managed-package prefixes collide with standard orgs — always prefer getSobjectType() over prefix lookup.
  5. Trigger oldMap keys are typed Id — iterating as String loses the type info.
  6. System.StringException from (Id) someString is the clue that a caller passed garbage; handle it with intent.
  7. Id cannot be deserialized from JSON with a typo — JSON.deserialize silently becomes null on invalid strings, not throw.

Output Artifacts

ArtifactDescription
scripts/check_apex_salesforce_id_patterns.pyScans for string-prefix Id checks, 15/18 char string compares, and untyped Id parameters
templates/apex-salesforce-id-patterns-template.mdWork template for validating and typing Id inputs at trust boundaries

Package files