Apex Security - Read Enforcement

WITH SECURITY_ENFORCED in Apex Explained

A practical guide for Salesforce developers, admins, architects, and security reviewers who still maintain legacy secure SOQL patterns, need to understand where WITH SECURITY_ENFORCED helps, and need a clear recommendation on when to keep it, when to replace it, and how to review AI-generated Apex that still uses it.

12 min readPublished April 30, 2026By Shivam Gupta
Shivam Gupta
Shivam GuptaSalesforce Architect and founder at pulsagi.com
Salesforce WITH SECURITY_ENFORCED infographic

WITH SECURITY_ENFORCED still matters for secure read queries, but modern Salesforce guidance generally prefers WITH USER_MODE for new work.

Introduction

WITH SECURITY_ENFORCED was an important step forward for secure Apex because it gave developers a concise way to fail a query when the user lacked access to a selected object or field. For a long time, it was far cleaner than large blocks of describe-based CRUD and FLS checks.

It is still relevant today because many Salesforce orgs, managed packages, shared libraries, and AI-generated code samples use it. But it is no longer the whole story. Salesforce's later guidance around WITH USER_MODE changes how modern teams should think about it.

Practical framing: WITH SECURITY_ENFORCED is still useful for some read queries, especially in legacy code, but it is no longer the default recommendation for most new secure Apex data access.

What it is

Salesforce introduced WITH SECURITY_ENFORCED as a SOQL clause that enforces object- and field-level read access for the fields returned by the query. If the user cannot access even one selected field, Salesforce throws a System.QueryException and returns no data.

Official Salesforce guidance from the Spring '20 developer blog also explains an important scope limit: this clause checks the fields in the SELECT portion of the query, including subqueries and cross-object relationships, but it does not validate field-level access for fields that appear only in the WHERE clause.

Why it still matters

Even though user mode is the modern preference for many teams, WITH SECURITY_ENFORCED still matters for three reasons. First, a large amount of production Apex already uses it. Second, some teams need a clear fail-fast read pattern while they gradually migrate older code. Third, reviewers still need to understand what it covers so they can judge legacy or AI-generated code accurately.

It also remains valuable as a teaching tool because it helps teams understand that sharing keywords alone are not enough. A query can respect record-level access and still expose fields the user is not allowed to read unless another security mechanism is added.

Example

Here is the classic pattern. The query is wrapped in try/catch because the secure behavior is to throw an exception when access is denied.

public with sharing class AccountDirectoryService {
    public static List<Account> getVisibleAccounts() {
        try {
            return [
                SELECT Id, Name, Industry, Owner.Name
                FROM Account
                WITH SECURITY_ENFORCED
                ORDER BY Name
                LIMIT 100
            ];
        } catch (QueryException ex) {
            throw new AuraHandledException(
                'You do not have access to one or more requested fields.'
            );
        }
    }
}

This is still a respectable pattern for read-only data access in many legacy code paths. But remember the boundary: if a sensitive field appears only in the filter, older Salesforce guidance says WITH SECURITY_ENFORCED does not protect that gap.

Use cases

  • Legacy read services: existing selectors and controllers that need a concise fail-fast read check.
  • Incremental hardening: teams improving old SOQL before a larger migration to user mode.
  • AppExchange or compliance review cleanup: places where a fast improvement to read-query enforcement is helpful.
  • Training and code review: helping teams distinguish field/object enforcement from record-sharing enforcement.

WITH SECURITY_ENFORCED vs WITH USER_MODE

Area WITH SECURITY_ENFORCED WITH USER_MODE
Main purpose Fail-fast field and object read enforcement in SOQL. Broader user-context enforcement for SOQL, SOSL, and DML.
Read coverage Selected fields, subqueries, cross-object reads. Selected fields plus broader modern access checks, including fields in filters according to Salesforce's Spring '23 guidance.
Write support No. Yes, through user-mode DML patterns.
Modern recommendation Useful, but often legacy or transitional. Preferred for most new secure database operations.

The most practical summary is this: WITH USER_MODE is broader, newer, and better aligned with how modern Salesforce teams secure user-context data access. WITH SECURITY_ENFORCED still has value, but mostly where legacy read patterns already depend on it or where migration is still in progress.

Admin and developer perspective

Admins care about whether Apex exposes fields they intentionally hid through permission sets and field-level security. WITH SECURITY_ENFORCED is reassuring because it fails closed instead of leaking data quietly. But admins should also know that it is read-only and not the final answer for secure writes.

Developers care about migration cost. If a codebase already uses this clause extensively, there is no need to rip everything out overnight. The better move is to understand which read paths are still acceptable, which ones have WHERE-clause gaps, and which newer services should move directly to user mode.

Best practices

  • Use try/catch intentionally. This clause throws exceptions by design, so the user experience should handle that cleanly.
  • Keep it for legacy reads where it is already working well. Not every older class needs immediate refactoring.
  • Do not treat it as a write-security solution. It is not for DML.
  • Watch out for filter-only field exposure. If the query relies on sensitive fields in WHERE, evaluate whether user mode is the safer replacement.
  • Prefer user mode for new development. Salesforce's Spring '23 guidance explicitly recommends that modern direction.
  • Pair it with explicit sharing on the class. Record visibility still depends on sharing design.
  • Review AI-generated queries carefully. Many generated snippets still use this clause without explaining its boundaries.

Limitations

The first limitation is scope. WITH SECURITY_ENFORCED helps with secure reads, not writes. It cannot protect inserts, updates, deletes, or read-modify-write paths by itself.

The second limitation is coverage nuance. Official Salesforce Spring '20 guidance says it does not validate field permissions for fields used only in WHERE. That means a query can still rely on a sensitive field indirectly unless you choose a broader pattern.

The third limitation is strategic. Salesforce later recommended WITH USER_MODE for post-Spring '23 modern development because it covers more access scenarios and adapts better to evolving platform security features.

Recommendation

Use WITH SECURITY_ENFORCED when you are maintaining or incrementally hardening legacy SOQL and you need a concise fail-fast read check. For most new secure Apex data access, prefer WITH USER_MODE.

If the business requirement is graceful degradation rather than an exception, consider Security.stripInaccessible(). If the requirement is record-level control, remember to pair this clause with the right sharing keyword on the class.