The Spring ’23 release of the Salesforce Platform, generally available as of Feb 13th, added some fantastic updates to the Apex language. We have implemented some of the Spring ’23 updates in the apex-recipes sample app, which has significantly simplified the existing codebase.

In this blog post, we will review the updates in Spring ’23 for Apex with code examples. These updates help developers create more secure applications for their organizations.

1. User mode database operations

Apex, by default, runs in System mode with elevated permissions, meaning that developers can inadvertently bypass security controls when writing code.

Before proceeding, let’s quickly review security controls that Salesforce administrators can place to ensure that users can only access and manipulate the data they are authorized to see or edit. The bullet points below summarize different mechanisms to enforce a fine-grained security model for your Salesforce data.

  • CRUD stands for “Create, Read, Update, and Delete,” the four basic operations a user can perform on a record in Salesforce
  • FLS stands for “Field-Level Security,” which determines which fields within a record that a user can view or edit
  • Record sharing allows the administrator to set up rules for who can view or edit a record based on various criteria

With user mode database operations, developers can choose to run Apex in the user’s context, ensuring that the user’s CRUD/FLS and sharing rules configured are enforced. Let’s see this in action with detailed code examples.

Enforce CRUD/FLS and sharing rules for static SOQL

You can indicate the mode of the operation by using the keyword WITH USER_MODE for the user mode and WITH SYSTEM_MODE for the system mode in your SOQL query. See the example below.

In the above example, by using the keyword WITH USER_MODE, the query respects these security constraints:

  • Read permissions on Account object (configured for Profile/Permission set) for the user
  • Field permissions (FLS) for Name, Shipping Street, and ShippingCity for the user
  • Record level settings (like org-wide defaults and sharing rules) for the Account object for the user

The WITH USER_MODE keyword is also supported for aggregate SOQL to enforce CRUD/FLS and record sharing rules.

In the above example, by using the keyword WITH USER_MODE, the query respects these security constraints:

  • Read permissions on Opportunity object for the user
  • Field permissions (FLS) for Amount and AccountId (yes, even fields used in the SOQL WHERE clause are checked for FLS) for the user
  • Record-level access (like org-wide defaults and sharing rules) on the Opportunity object for the user

For more examples, check out the SOQLRecipes class from the apex-recipes app.

Enforce CRUD/FLS and sharing rules for dynamic SOQL

The new Database methods (see docs) now support an AccessLevel parameter that lets you run database operations in user mode instead of in the default system mode. Let’s look at example code to run a dynamic SOQL in the user mode.

In the above example, the user mode will apply similarly to that of the static SOQL example that we saw in the previous section.

For more examples, check out the DynamicSOQLRecipes class from the apex-recipes. We have updated all of the methods in the class to use the AccessLevel parameter.

Enforce CRUD/FLS and sharing rules for SOSL

WITH USER_MODE or WITH SYSTEM_MODE are also supported for SOSL (Salesforce Object Search Language) statements.

Let’s look at an example for a static SOSL statement.

In the above example, by using the keyword WITH USER_MODE, the query respects these security constraints:

  • Read permissions on the Account and Contact objects for the user
  • Field permissions (FLS) for the Name field on Account and LastName field on Contact for the user
  • Record-level access (like org-wide defaults and sharing rules) on the Account and Contact objects for the user

For Dynamic SOSL, the new Search methods (see docs) also support the AccessLevel parameter similar to the new Database methods. Below is an example of how to use the AccessLevel parameter to run SOSL in the users’ context.

Enforce CRUD/FLS and sharing rules for DML

Database operations can specify user or system mode using the keywords as user or as system.

The following is example code that runs DML in the user mode enforcing CRUD/FLS and sharing rules.

For Dynamic DML, developers can make use of the AccessLevel parameter to run database operations in the user mode or system mode.

Let’s take a look at an example from the apex-recipes app to see how you can design methods to be generic, so that the consumer of the method can decide to run code in the user or the system mode.

The code snippet below shows how to invoke this method in the user mode.

The following code snippet shows how to invoke this method in the system mode.

For more examples, check out the DMLRecipes class from the apex-recipes app.

Important considerations

  1. User mode database operations generate security exceptions if a CRUD/FLS violation is found. If you have a requirement to avoid exceptions and still enforce security, use the Security.stripInaccessible() method (see docs). Refer to the StripInaccessibleRecipes class (see docs) from the apex-recipes app for code examples.
  2. If you are using the WITH SECURITY_ENFORCED keyword in your SOQL statements for enforcing CRUD/FLS, we now recommend that you use the WITH USER_MODE keyword instead due to the following reasons:
    1. The SOQL query using the WITH USER_MODE keyword supports lots of new innovations, such as restriction rules, scoping rules, and any other security operations for data access and CRUD/FLS, that may be added by the platform in the future, so it’s sort of future-proof
    2. The SOQL query using the WITH USER_MODE keyword handles complex security use cases much better. For example, WITH USER_MODE is supported in SOSL and polymorphic queries.
    3. SOQL statements using the WITH USER_MODE keyword handles CRUD/FLS for fields used in the where clause and order by or fields used in the relationship query or polymorphic lookup
    4. SOQL queries using the WITH USER_MODE keyword perform far better compared to using WITH SECURITY_ENFORCED
  3. User mode overrides the class-level setting for the SOQL query or DML written in user mode. Let’s explore this with the example code below.

In the above example, even though the Apex class is set to run in the system context (via without the sharing keyword), the SOQL query runs in the user mode, enforcing security. The user mode for the operation (SOQL/SOSL or DML) overrides the class-level sharing.

2. Dynamically pass bind variables to SOQL queries

Spring ’23 added new methods like Database.queryWithBinds, Database.getQueryLocatorWithBinds, and Database.countQueryWithBinds.

These methods provide the following benefits:

  • Previously, if developers used bind variables in dynamic SOQL (using the Database.query method) that are out of the context, the query could not resolve the variables. With queryWithBinds, the query’s bind variables are resolved directly from a Map parameter with a key rather than from Apex code variables.
  • With Database.queryWithBinds SOQL injection attacks are automatically prevented.

Let’s take a look at a code example to understand the second bullet point in more depth.

The above code runs a dynamic SOQL in the user mode. The method accepts a Map parameter and can be called using the code below.

Notice that we do not need to ensure that the variable name is in the same method scope as the dynamic query. Also, there is no need to use the String.escapeSingleQuotes method for the value in the name variable when using queryWithBinds.

For more code examples, check out this pull request in our apex-recipes GitHub repository.

3. Specify a delay in scheduling queueable jobs

Another important feature that we shipped in Spring ‘23 is the ability to specify delays for scheduled queueable jobs.

It can be beneficial to adjust the timing before queueable job runs in the use cases below:

  • If the external system is rate-limited and can be overloaded by chained queueable jobs that are making rapid callouts
  • When polling for results, and executing too fast can cause wasted usage of the daily async Apex limits

Use the System.enqueue(queueable, delay) method (see docs) to specify delays. Delays can range from zero to 10 minutes. Let’s take look at an example to better understand this feature.

The above example adds a job for delayed asynchronous execution by passing in an instance of your class implementation of the Queueable interface for execution. There’s a minimum delay of five minutes before the job is executed.

Specify an org-wide default delay in scheduling queueable jobs

Currently, if you have an Apex queueable job, it uses standard queueable timing with no additional delays. Admins can define an org-wide default delay for all the queueable jobs that do not specify delay using
System.enqueue(queueable, delay). This is primarily a mechanism to handle runaway jobs that might be executing too fast.

Important consideration

When you set the delay to 0 (zero), the queueable job is run as quickly as possible. With chained queueable jobs, implement a mechanism to slow down or halt the job if necessary. Without such a fail-safe mechanism in place, you can rapidly reach the daily async Apex limit.

There is also an upcoming Beta feature in the Summer ’23 release (Planned to be available by June 10th 2023 in all the orgs) that allows developers to control the depth of the chained queueable jobs.

4. Get the source SObject of a DescribeFieldResult instance using the new getSObjectType method

The getSObjectType method (see docs) on the DescribeFieldResult object (see docs) is a developer quality of life improvement method that was implemented in Spring ’23.

Previously, developers had to do hacks and write additional code to get the Source object from the Fields schema information obtained via the Field description. You can check previous workarounds via this stackexchange post.

Below is a code example of how to use the new getSObjectType method.

With the getSObjectType method, developers no longer have to pass around the object name as string. Check out a more comprehensive example in the Spring ’23 release notes.

Tooling updates

We have updated the Apex language server to support the latest syntax additions, such as insert as user, insert as system, and much more. And we now support the latest syntax additions in the recent release of the Salesforce Extensions for VSCode.

I also want to thank Dang Mai for updating the prettier plugin for Apex (used to format Apex code automatically) to support all of the keywords introduced for user mode database operations.

Conclusion

In conclusion, the Spring ’23 release of Salesforce includes several updates. By using these new features, developers can create more effective and secure applications for their organizations.

The Apex product teams are not stopping there, and more innovations are on the roadmap. You can get a preview of what’s coming for Apex in Summer ’23 (our next release) in the release notes preview. I also recommend watching the recording of the TrailblazerDX ’23 session, Apex: What’s New and What’s Coming, to learn more about what’s cooking!

Additional references

About the author

Mohith Shrivastava is a Developer Advocate at Salesforce with a decade of experience building enterprise-scale products on the Salesforce Platform. He is presently focusing on the Salesforce Developer Tools, Flow, Apex, and Lightning Web Components at Salesforce. Mohith is currently among the lead contributors on Salesforce Stack Exchange, a developer forum where Salesforce Developers can ask questions and share knowledge. You can follow him via his Twitter @msrivastav13.

Get the latest Salesforce Developer blog posts and podcast episodes via Slack or RSS.

Add to Slack Subscribe to RSS