Question : What all asynchronous process available in salesforce.
Question : Explain batch processing
Question : Best Practices follwed for apex
Question : Best practices follwed for triggeres.
Question : How to connect two systems (Integartion related questions - Connected app, named credentials)
Question : Different types of flows.
Question : What is the maximum number of records that can be processed by a trigger?
Question : Context variables in triggers
Question : Future method and Quable methods (Limitation, which senario you will go for future menthod)
Question : How to set field level security in Apex (WITH SECURITY_ENFORCED)?
Question : Best Practices for flows (Avoid hard code values, mixed DMLs, Avoid DML statements inside Loop, Error handling etc).
Question : Mixed DML exception(Future method)
Question : Schedulable classes can we do callouts directly from schedulable class or not.
Question : Imaging you are processing a batch fo 50000 and you have to seprate the process sucessful and failed record, how you will achieve it.
Question : Questions related to field level security.
Question : Database stateful and stateless (Difference and senario if you faced any)
Question : How to pass the values from flow to Apex.
Question : Field restriction by apex
Question : application event in LWC
Question : life cycle of LWC
Question : Named credential
Question : Mixed DML exception
Question : Open Id connect for integration
Question : Calling one batch to another batch
Question : Is there a way to callout triggers
Question : About recursive triggers
Question : Reason for bulkification of your code
Question : Database operation and syntax
Question : What are public component in LWC
Question : Types of decorators in LWC
Question : Difference between, trigger.old and Trigger.oldmap
Question : Not using @future annotation in webservices callout, what will happen
Question : How can you implement recursive trigger in salesforce
Question : Composite request
Question : Component event and application event difference
Question : What is LDS(Lightning Data Services) in lwc
Question : SOQL 101 Exception
Question : Nested aura component can we have, which event will execute first
Question : Deployment tool.. CI/CD process.. how to setup
Question : explain one LWC component created by you.
Question : How will you get the data from Apex to LWC
Question : how many ways we can query the data
Question : What is Wire method
Question : Concept of promises in LWC ?
Question : how to establish communication between components
Question : Integration - breif what you have done.
Question : diff between userflow and web server flow ( connected App authorization)?
Question : serve side - how the authentication is happen?
Question : Platform event?
Question : Managed Package - exp
Question : Aura - share the method with another Aura comp?
Question : styling hooks in LWC?
Question : have you worked in service window.runnerWindow.proxyConsole.
Question : How did you do deployment
Question : Involved in any deployment
Question : Can we use multiple decorators for one property
Question : what is the need of LWC, when we are already having Aura
Question : How will you overcome the governor limit
Question : How to call from one batch class into another batch class
Question : Why apex callout is always asyc
Question : SECURITY_ENFORCE use in SOQL
Question : Security question on Triggers
Question : Can we pass one batch data to another batch process
Question : Types of Async classes
Question : About Iterable class in Batchable
Question : PMD violations
Question : Devops process
Question : Imperative apex
Question : Difference between aura and LWC component
Question : Types of Events in AURA
Salesforce provides several asynchronous processing options to handle operations that require large volumes of data, complex processing, or tasks that should not be performed in a synchronous transaction. Here's a comprehensive list of asynchronous processes available in Salesforce:
- Future Methods
- Batch Apex
- Scheduled Apex
- Queueable Apex
- Apex REST Callouts
- Use Case: Perform long-running operations or callouts to external services without blocking the main thread.
- Key Points:
- Methods annotated with
@future
. - Limited to 50 future method calls per Apex invocation.
- Future methods cannot return values.
- Cannot be chained.
- Methods annotated with
public class FutureMethodExample {
@future(callout=true)
public static void callExternalService() {
// Callout code
}
}
- Use Case: Process large volumes of records asynchronously.
- Key Points:
- Implement
Database.Batchable
interface. - Can handle millions of records.
- Allows for stateful processing using
Database.Stateful
.
- Implement
public class BatchApexExample implements Database.Batchable<SObject> {
public Database.QueryLocator start(Database.BatchableContext BC) {
return Database.getQueryLocator('SELECT Id, Name FROM Account');
}
public void execute(Database.BatchableContext BC, List<SObject> scope) {
// Process each batch of records
}
public void finish(Database.BatchableContext BC) {
// Final operations
}
}
- Use Case: Schedule a batch job or other Apex code to run at specific times.
- Key Points:
- Implement
Schedulable
interface. - Use
System.schedule
to schedule the job.
- Implement
public class ScheduledApexExample implements Schedulable {
public void execute(SchedulableContext SC) {
// Code to execute
}
}
// Schedule the job
System.schedule('Job Name', '0 0 12 * * ?', new ScheduledApexExample());
- Use Case: Chain jobs and perform complex operations asynchronously.
- Key Points:
- Implement
Queueable
interface. - Supports job chaining.
- More flexible than future methods.
- Implement
public class QueueableApexExample implements Queueable {
public void execute(QueueableContext context) {
// Code to execute
}
}
// Enqueue the job
Id jobId = System.enqueueJob(new QueueableApexExample());
- Use Case: Make HTTP requests to external services asynchronously.
- Key Points:
- Use
@future(callout=true)
for asynchronous callouts. - Can be combined with future methods or Queueable Apex.
- Use
public class RestCalloutExample {
@future(callout=true)
public static void makeCallout() {
HttpRequest req = new HttpRequest();
req.setEndpoint('https://example.com/api');
req.setMethod('GET');
Http http = new Http();
HttpResponse res = http.send(req);
}
}
https://github.com/therishabh/salesforce-apex/blob/main/README.md#batch-apex
https://www.salesforceben.com/12-salesforce-apex-best-practices/
Bulkification of your code is the process of making your code able to handle multiple records at a time efficiently.
SOQL and DML are some of the most expensive operations we can perform within Salesforce Apex, and both have strict governor limits associated with them.
When we begin writing a brand-new class, one of the first things we should do is declare our sharing model. If we require our code to bypass record access, we must always declare without sharing, but when we want it to enforce sharing rules, developers can often find themselves skipping this step.
When we query a large set of records and assign the results to a variable, a large portion of our heap can be consumed.
Salesforce mandates that we have at least 75% code coverage when we wish to deploy Apex code into production, and while having a high number of lines covered by tests is a good goal to have, it doesn’t tell the whole story when it comes to testing.
Writing tests only to achieve the code coverage requirement shows one thing: your code has been run, and it doesn’t actually provide any value other than showing that in a very specific scenario (which may or may not ever happen in practice!).
When writing our tests, we should worry about code coverage less, and instead concern ourselves with covering different use cases for our code, ensuring that we’re covering the scenarios in which the code is actually being run. We do this by writing multiple test methods, some of which may be testing the same methods and not generating additional covered lines, each of which runs our code under a different scenario.
Loops inside of loops – sometimes it can’t be avoided. You simply need to iterate over one thing related to another. Good stuff, right? While there may not seem to be anything immediately wrong here, and the code could very well run perfectly fine without running into performance issues or governor limits, the issue here is more one of maintainability and readability.
Rather than using nested loops, a good strategy is to abstract your logic into separate methods (which perform the logic themselves). This way, when we’re looking at a block of code from a higher level, it is far easier to understand. Abstracting our code out like this also has other benefits, such as making it easier to test specific aspects of our code.
Naming conventions tend to be a hot topic in any developer team. The benefits are clear if everyone follows them, as they make it easier for other people in your team to understand what’s going on within an org.
When we’re writing @AuraEnable methods for our Lightning Components, we frequently need to return more complex data structures, such as records, custom data types, or lists of these types.
An easy approach would be to serialize these objects into JSON, and then deserialize them within our component’s JavaScript (that is what the JS in JSON standards for after all!).
However, this approach is actually an anti-pattern and can lead to some poor performance within our components. Instead, we should be directly returning these objects and letting the platform handle the rest for us.
Converting our returned data into JSON results in us consuming large amounts of heap memory and spending many CPU cycles converting these objects into a lovely and long string
https://www.pantherschools.com/apex-trigger-best-practices-in-salesforce/
https://github.com/therishabh/salesforce-apex/blob/main/README.md#trigger
As a developer, to develop effective triggers we can follow the below best practices
- One trigger per object
- Logic Less Trigger
- Context-specific handler methods
- Avoid SOQL Query inside for loop
- Avoid hardcoding IDs
- Avoid nested for loop
- Avoid DML inside for loop
- Bulkify Your Code
- Enforced Sharing in Salesforce
- Use @future Appropriately
- Use WHERE Clause in SOQL Query
- Use Test-Driven Development
Question : How to connect two systems (Integartion related questions- Connected app,named credentials)
https://github.com/therishabh/salesforce-apex/blob/main/README.md#integration-in-salesforce
Salesforce Flows can be classified into five subtypes:
Screen Flows
Require user interaction and include screens, local actions, steps, choices, or dynamic choices. For example, a customer survey after a support case is completed.
Schedule-Triggered Flows
Run in the background at a specified time and at a repeated frequency to perform actions on a batch of records. For example, a financial firm might need to generate reports at the end of every financial quarter.
Autolaunched Flows
Run without user interaction and don't support screens, local actions, choices, or choice sets. They are triggered by a specific event or record update and can automate complicated tasks like sending email notifications, modifying records, or creating new records. For example, when a large opportunity closes, send an alert to the right people on your team.
Record-Triggered Flows
Run in the background, either before or after a record save. They are triggered when a record is created or updated and can be used to automate processes such as updating related records or sending notifications to users. For example, when a large opportunity closes, send an alert to the right people on your team.
Platform Event-Triggered Flows
Run when a platform event message is received.
A trigger can process a maximum of 200 records at a time.
However, if you're dealing with a larger number of records, Salesforce will automatically break down the process into chunks of 200. For example, if you have 1000 records to process, the trigger will be called 5 times, each time handling 200 records.
Additional Notes:
Platform Event triggers can handle up to 2000 records per chunk, which is different from standard object triggers.
https://github.com/therishabh/salesforce-apex/blob/main/README.md#trigger-context-variables
Question : Future method and Quable methods (Limitation, which senario you will go for future menthod)
https://github.com/therishabh/salesforce-apex/blob/main/README.md#asynchronous-processing-basics
Setting field-level security in Apex ensures that your code respects the field-level security settings configured in Salesforce. Using the WITH SECURITY_ENFORCED keyword in SOQL queries is one way to enforce field-level security checks. Here's how you can use it:
Enforcing Field-Level Security with WITH SECURITY_ENFORCED
When you add WITH SECURITY_ENFORCED to your SOQL queries, Salesforce will automatically enforce field- and object-level security checks. If a user does not have access to a field or object, the query will fail, ensuring that your code does not inadvertently expose restricted data.
Example Usage
Here's an example of how to use WITH SECURITY_ENFORCED in a SOQL query:
try {
List<Account> accounts = [SELECT Id, Name, Phone FROM Account WITH SECURITY_ENFORCED WHERE IsActive = true];
// Process the accounts
for (Account acc : accounts) {
System.debug('Account Name: ' + acc.Name);
}
} catch (Exception e) {
System.debug('Insufficient permissions: ' + e.getMessage());
}
Question : Best Practices for flows (Avoid hard code values, mixed DMLs, Avoid DML statements inside Loop, Error handling etc).
https://www.salesforceben.com/salesforce-flow-best-practices/
Here are the best Salesforce Flow practices I regularly follow when developing my flows.
- Always Test Your Flows
- Consider Using Subflows
- Never Perform DML Statements in Loops
- Document Your Flows
- Never Hard Code Ids (Use Constants if You Must)
- Plan for Faults (and Handle Them)
- Make Use of Schedule-Triggered Flow and Asynchronous Paths
https://github.com/therishabh/salesforce-apex/blob/main/README.md#future-method-in-apex
A mixed DML Operation Error comes when you try to perform DML operations on setup and non-setup objects in a single transaction.
Setup objects are the sObjects that affect the user’s access to records in the organization. Here are examples of the Setup Objects:
- ObjectPermissions
- PermissionSet
- PermissionSetAssignment
- QueueSObject
- Territory
- UserTerritory
- UserRole
- User
Salesforce does not allow us to make callouts from Schedulable classes
We used to get “System.CalloutException: Callout from scheduled Apex not supported” Error when we are making a web service callout from a class which is implementing Database.Schedulable interface because Salesforce does not allow us to make callouts from Schedulable classes.
@future method : A future runs as asynchronously. You can call a future method for executing long-running operations, such as callouts to external Web services or any operation you’d like to run in its own thread, on its own time
Scheduler Class
global class SampleScheduler implements Schedulable{
global void execute(SchedulableContext sc)
{
BatchUtilClass.futureMethodSample();
}
}
Future Class Method
public class BatchUtilClass {
@future(callout=true)
public static void futureMethodSample() {
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals');
request.setMethod('GET');
HttpResponse response = http.send(request);
if (response.getStatusCode() == 200) {
Map<String, Object> results = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
List<Object> animals = (List<Object>) results.get('animals');
System.debug('Received the following animals:');
for (Object animal: animals) {
System.debug(animal);
}
}
}
}
https://www.apexhours.com/system-calloutexception-callout-from-scheduled-apex-not-supported/
Question : Imaging you are processing a batch fo 50000 and you have to seprate the process sucessful and failed record, how you will achieve it.
Processing large batches of records in Salesforce requires careful handling to ensure that successful and failed records are managed properly. Here’s how you can achieve this when processing a batch of 50,000 records using Batch Apex, with separate handling for successful and failed records:
Steps
- Define Batch Apex Class: Implement the Database.Batchable interface.
- Separate Successful and Failed Records: Use collections to keep track of successful and failed records during processing.
- Handle Results in the Finish Method: Log or process the successful and failed records accordingly.
Example Batch Apex Class
Here’s a complete example demonstrating how to process 50,000 records, separating successful and failed records:
global class MyBatchClass implements Database.Batchable<SObject>, Database.Stateful {
// Lists to keep track of successful and failed records
private List<Account> successfulRecords = new List<Account>();
private List<Account> failedRecords = new List<Account>();
global Database.QueryLocator start(Database.BatchableContext BC) {
// Query to fetch the records to be processed
return Database.getQueryLocator('SELECT Id, Name FROM Account WHERE ...');
}
global void execute(Database.BatchableContext BC, List<Account> scope) {
// Process each batch of records
for (Account acc : scope) {
try {
// Perform some processing (e.g., update the name)
acc.Name = acc.Name + ' - Processed';
// Add to successful records list
successfulRecords.add(acc);
} catch (Exception e) {
// Add to failed records list with an error message
acc.addError('Processing failed: ' + e.getMessage());
failedRecords.add(acc);
}
}
// Perform DML operations
if (!successfulRecords.isEmpty()) {
try {
update successfulRecords;
} catch (Exception e) {
// Handle exception if update fails for any record
for (Account acc : successfulRecords) {
acc.addError('Update failed: ' + e.getMessage());
failedRecords.add(acc);
}
}
}
}
global void finish(Database.BatchableContext BC) {
// Log successful records
for (Account acc : successfulRecords) {
System.debug('Successfully processed account: ' + acc.Id);
}
// Log failed records
for (Account acc : failedRecords) {
System.debug('Failed to process account: ' + acc.Id + ' - Error: ' + acc.getErrors()[0].getMessage());
}
// Further handling of failed records (e.g., reprocess, notify admin)
}
}
Explanation
- Stateful Interface: The Database.Stateful interface is used to maintain the state across batch transactions, allowing you to keep track of successful and failed records.
- Collections for Successful and Failed Records: Two lists (successfulRecords and failedRecords) are used to track records that are processed successfully and those that fail during processing.
- Try-Catch Blocks: In the execute method, each record is processed within a try-catch block to handle exceptions individually and categorize the records accordingly.
- Error Handling: Failed records are added to the failedRecords list with an appropriate error message.
- Finish Method: In the finish method, both successful and failed records are logged. You can extend this part to send notifications, write to a custom object, or perform other actions as needed.
Field-Level Security (FLS) determines whether a user can view or edit the data in a specific field on an object. It is a key component of Salesforce’s security model and is applied to profiles and permission sets to control access to sensitive data.
https://github.com/therishabh/salesforce-apex/blob/main/README.md#batch-apex
In Salesforce, Batch Apex classes can implement either the Database.Stateful
or Database.Batchable
interfaces. Understanding the difference between these two interfaces, as well as when and how to use them, is important for developing efficient and effective batch processes.
-
Database.Stateful:
- Definition: Implements the
Database.Stateful
interface to maintain state across transaction boundaries. - Usage: Use
Database.Stateful
when you need to maintain state information (like aggregate values, collections, etc.) across multiple batch transactions. - Example: Tracking processed record IDs, maintaining counters, or aggregating values across all batches.
- Definition: Implements the
-
Database.Batchable (Stateless):
- Definition: Implements the
Database.Batchable
interface without theDatabase.Stateful
interface, which means it is stateless. - Usage: Use
Database.Batchable
when you do not need to maintain state across batch transactions. Each batch transaction is independent and does not share any data with other transactions. - Example: Processing records independently where no shared state is required.
- Definition: Implements the
Imagine you have a batch process that needs to calculate the total amount of all Opportunity
records and update a custom field on the Account
record with the aggregated value. Since this calculation requires maintaining a running total across multiple batches, you would use Database.Stateful
.
global class AggregateOpportunitiesBatch implements Database.Batchable<SObject>, Database.Stateful {
private Decimal totalAmount = 0; // Maintain state
global Database.QueryLocator start(Database.BatchableContext BC) {
return Database.getQueryLocator('SELECT Amount, AccountId FROM Opportunity WHERE IsClosed = true');
}
global void execute(Database.BatchableContext BC, List<SObject> scope) {
for (Opportunity opp : (List<Opportunity>) scope) {
totalAmount += opp.Amount;
}
}
global void finish(Database.BatchableContext BC) {
// Update Accounts with the aggregated totalAmount
List<Account> accountsToUpdate = [SELECT Id FROM Account WHERE Id IN (SELECT AccountId FROM Opportunity WHERE IsClosed = true)];
for (Account acc : accountsToUpdate) {
acc.Total_Opportunity_Amount__c = totalAmount;
}
update accountsToUpdate;
System.debug('Total Opportunity Amount: ' + totalAmount);
}
}
For a stateless scenario, consider a batch process that updates a field on each Contact
record independently, such as setting a Last_Processed__c
date field to the current date.
global class UpdateContactsBatch implements Database.Batchable<SObject> {
global Database.QueryLocator start(Database.BatchableContext BC) {
return Database.getQueryLocator('SELECT Id FROM Contact');
}
global void execute(Database.BatchableContext BC, List<SObject> scope) {
for (Contact con : (List<Contact>) scope) {
con.Last_Processed__c = System.today();
}
update scope;
}
global void finish(Database.BatchableContext BC) {
System.debug('Contact records updated with the current date.');
}
}
Aspect | Database.Stateful | Database.Stateless |
---|---|---|
State Maintenance | Maintains state across transactions | Does not maintain state across transactions |
Use Case | Aggregations, counters, accumulating data | Independent record processing |
Example | Aggregating Opportunity amounts |
Updating Contact fields independently |
Situation: I needed to process a large number of records and keep a running count of how many records met certain criteria (e.g., number of closed Opportunities). This required using Database.Stateful
to maintain the count across multiple batch transactions.
Implementation: Using a counter variable in a stateful batch class to maintain the running total and update a summary field on a parent record at the end.
Situation: I had to update a specific field on all Contact
records in the system, and each update was independent of the others (e.g., setting a flag or date field).
Implementation: A stateless batch class was used because each Contact
record was processed independently, and no shared state was required.
By understanding and applying the differences between Database.Stateful
and Database.Batchable
(stateless), you can choose the appropriate approach for your batch processing needs, ensuring efficient and effective execution of your Salesforce batch processes.
https://github.com/therishabh/salesforce-apex/blob/main/README.md#invocable-method
Passing values from a Salesforce Flow to an Apex class involves creating an Apex invocable method that accepts input parameters. These parameters can then be mapped to Flow variables when the Flow is designed. Here is a step-by-step guide on how to achieve this:
-
Create the Apex Class with an Invocable Method:
- Define an Apex class.
- Create an inner class to hold the input parameters.
- Annotate the method with
@InvocableMethod
.
-
Define Input Parameters in the Flow:
- Use the Flow Builder to create variables.
- Map these variables to the inputs of the invocable method.
Let's say you want to pass a contact's first name, last name, and email from a Flow to an Apex class to create a new Contact record.
public class CreateContactFromFlow {
// Define an inner class to hold the input parameters
public class ContactInput {
@InvocableVariable
public String firstName;
@InvocableVariable
public String lastName;
@InvocableVariable
public String email;
}
// Define the invocable method
@InvocableMethod(label='Create Contact' description='Create a new contact from Flow')
public static void createContact(List<ContactInput> inputParams) {
List<Contact> contactsToCreate = new List<Contact>();
for (ContactInput input : inputParams) {
Contact newContact = new Contact(
FirstName = input.firstName,
LastName = input.lastName,
Email = input.email
);
contactsToCreate.add(newContact);
}
if (!contactsToCreate.isEmpty()) {
insert contactsToCreate;
}
}
}
-
Open Flow Builder: Go to Setup -> Process Automation -> Flows and click "New Flow".
-
Select Screen Flow or Auto-launched Flow: Choose the type of Flow based on your requirements.
-
Create Variables:
- Create three variables:
firstName
,lastName
, andemail
. - Ensure the data types match those expected by the Apex class (
Text
in this case).
- Create three variables:
-
Add an Action:
- In the Flow, add an "Action" element.
- Search for the "Create Contact" action (the label you defined in the
@InvocableMethod
annotation). - Map the Flow variables to the input parameters of the invocable method.
-
Finish the Flow:
- Complete the Flow by adding any other necessary elements (e.g., screens, decisions, etc.).
- Save and activate the Flow.
-
Create Variables:
- Click "Manager" on the left panel.
- Click "New Resource" and select "Variable".
- Create
firstName
,lastName
, andemail
variables withText
data type.
-
Add Action Element:
- Drag the "Action" element onto the canvas.
- In the Action search box, type "Create Contact".
- Select the "Create Contact" action.
-
Map Flow Variables:
- In the Action configuration, map the Flow variables to the inputs of the invocable method.
firstName
->firstName
lastName
->lastName
email
->email
- In the Action configuration, map the Flow variables to the inputs of the invocable method.
-
Complete the Flow:
- Add any other elements as needed.
- Connect the elements to define the Flow logic.
- Save and activate the Flow.
Here's how the Action configuration might look:
- Label: Create Contact
- API Name: Create_Contact
- Set Input Values:
firstName
: {!firstName}lastName
: {!lastName}email
: {!email}
By following these steps, you can effectively pass values from a Flow to an Apex class in Salesforce. This approach leverages the power of Flow for user-friendly configuration and the flexibility of Apex for complex business logic.
https://github.com/therishabh/salesforce-apex/blob/main/README.md#user-mode-vs-system-mode
In Salesforce, Field-Level Security (FLS) is an essential component of data security, ensuring that users can only view or edit fields they have permission to access. While Field-Level Security is primarily managed through profiles and permission sets, you might need to handle field restrictions programmatically in Apex to respect these settings or implement additional security measures.
Before performing operations on a field, you should check if the current user has access to that field. Salesforce provides methods in the Schema
class to check field accessibility.
Example: Checking Field Accessibility
// Example to check if the current user has access to a field
public class FieldSecurityUtil {
public static Boolean isFieldAccessible(Schema.SObjectField field) {
Schema.DescribeFieldResult fieldDescribe = field.getDescribe();
return fieldDescribe.isAccessible();
}
public static Boolean isFieldEditable(Schema.SObjectField field) {
Schema.DescribeFieldResult fieldDescribe = field.getDescribe();
return fieldDescribe.isUpdateable();
}
}
Usage in Apex Code
if (FieldSecurityUtil.isFieldAccessible(Account.MyField__c)) {
// Proceed with logic for accessible field
Account acc = [SELECT Id, MyField__c FROM Account LIMIT 1];
System.debug('Field value: ' + acc.MyField__c);
} else {
System.debug('Field is not accessible.');
}
When writing triggers, ensure that your code respects field-level permissions. For example, if your trigger updates fields, check if the fields are editable.
Example: Trigger Code
trigger AccountTrigger on Account (before update) {
for (Account acc : Trigger.new) {
if (FieldSecurityUtil.isFieldEditable(Account.MyField__c)) {
// Logic to update the field if it is editable
acc.MyField__c = 'New Value';
} else {
// Logic for fields that are not editable
System.debug('Field MyField__c is not editable.');
}
}
}
Events are used in LWC for components communication. There are typically 3 approaches for communication between the components using events.
- Parent to Child Event communication in Lightning web component
- Child to Parent Event Communication in Lightning Web Component
- Publish Subscriber model (PubSub model) in Lightning Web Component or LMS (Two components which doesn’t have a direct relation)
https://github.com/therishabh/salesforce-lwc?tab=readme-ov-file#parent-to-child-communication
https://github.com/therishabh/salesforce-lwc?tab=readme-ov-file#lifecycle-hooks
In Salesforce, a Named Credential is a configuration that allows you to securely store authentication details for external services. This feature is particularly useful for integrating with external systems or APIs, as it simplifies the management of authentication and improves security by keeping sensitive information out of the codebase.
You can use Named Credentials in Apex code to make HTTP requests without hardcoding credentials. Salesforce handles authentication automatically based on the Named Credential configuration.
Example: Making an HTTP Request
public class MyHttpClient {
public void callExternalService() {
HttpRequest req = new HttpRequest();
req.setEndpoint('callout:My_Named_Credential/some/resource');
req.setMethod('GET');
Http http = new Http();
HttpResponse res = http.send(req);
if (res.getStatusCode() == 200) {
// Process response
System.debug(res.getBody());
} else {
// Handle error
System.debug('Error: ' + res.getStatusCode() + ' ' + res.getStatus());
}
}
}
In this example, callout:My_Named_Credential
refers to the Named Credential you configured, and Salesforce automatically handles the authentication.
Named Credentials in Salesforce are a powerful feature for securely managing authentication details when integrating with external services. They simplify the process of making secure HTTP requests and managing credentials, enhancing both security and maintainability of your integration solutions.
https://www.emizentech.com/blog/call-batch-apex-from-another-batch-apex.html
https://github.com/therishabh/salesforce-apex/blob/main/README.md#batch-apex
In Salesforce, "callout triggers" typically refers to making HTTP callouts from triggers, but it’s important to note that Salesforce does not support direct HTTP callouts from triggers due to governor limits and best practices.
Here’s how you can indirectly achieve callouts from triggers using asynchronous methods:
Queueable Apex allows you to perform asynchronous operations, including HTTP callouts. You can call a Queueable Apex class from a trigger to handle the callout.
Steps to Use Queueable Apex for Callouts
-
Create the Queueable Apex Class
This class will perform the HTTP callout.
public class CalloutQueueable implements Queueable, Database.AllowsCallouts { public void execute(QueueableContext context) { HttpRequest req = new HttpRequest(); req.setEndpoint('https://api.example.com/resource'); req.setMethod('GET'); req.setHeader('Content-Type', 'application/json'); Http http = new Http(); HttpResponse res = http.send(req); if (res.getStatusCode() == 200) { // Process response System.debug(res.getBody()); } else { // Handle error System.debug('Error: ' + res.getStatusCode() + ' ' + res.getStatus()); } } }
-
Call the Queueable Apex Class from the Trigger
In your trigger, you can enqueue the Queueable job.
trigger AccountTrigger on Account (after insert) { if (Trigger.isAfter && Trigger.isInsert) { // Call Queueable Apex to perform callout System.enqueueJob(new CalloutQueueable()); } }
Future methods also allow asynchronous processing but are less flexible than Queueable Apex. They can be used for simple asynchronous operations and are limited in their ability to chain or handle complex scenarios.
Steps to Use Future Methods for Callouts
-
Create a Future Method
Define a method in a class marked with
@future
to perform the callout.public class CalloutFuture { @future(callout=true) public static void makeCallout() { HttpRequest req = new HttpRequest(); req.setEndpoint('https://api.example.com/resource'); req.setMethod('GET'); req.setHeader('Content-Type', 'application/json'); Http http = new Http(); HttpResponse res = http.send(req); if (res.getStatusCode() == 200) { // Process response System.debug(res.getBody()); } else { // Handle error System.debug('Error: ' + res.getStatusCode() + ' ' + res.getStatus()); } } }
-
Call the Future Method from the Trigger
Invoke the future method from your trigger.
trigger AccountTrigger on Account (after insert) { if (Trigger.isAfter && Trigger.isInsert) { // Call Future Method to perform callout CalloutFuture.makeCallout(); } }
https://github.com/therishabh/salesforce-apex/blob/main/README.md#how-to-avoid-recursion-in-trigger
Bulkification in Salesforce refers to the practice of designing Apex code to handle multiple records efficiently in a single execution context.
-
Governor Limits Compliance
Salesforce enforces various governor limits, such as limits on the number of SOQL queries, DML operations, and CPU time. Non-bulkified code can easily exceed these limits if it processes records individually. Bulkified code ensures that operations are performed in bulk, minimizing the number of SOQL queries and DML statements, and helps avoid hitting these limits.
-
Performance Optimization
Processing records in bulk reduces the number of database operations and HTTP requests, which improves performance.
-
Avoiding Recursive Triggers
Bulkified code reduces the likelihood of hitting recursive triggers.
-
Scalability
Bulkification ensures that code can handle large volumes of data without performance degradation. For example, if you need to process 10,000 records, bulkified code can handle this efficiently by processing records in batches rather than one at a time.
-
Maintaining Data Integrity
Bulk operations help maintain data integrity by ensuring that related records are processed together. For instance, when updating a set of records, bulk operations ensure that related updates are performed consistently, reducing the risk of partial updates or data inconsistencies.
-
Use Collections
Always use collections (such as Lists, Maps, and Sets) to handle multiple records. This allows you to perform operations in bulk.
// Bulk Insert Example List<Account> accountsToInsert = new List<Account>(); for (Integer i = 0; i < 200; i++) { accountsToInsert.add(new Account(Name = 'Account ' + i)); } insert accountsToInsert;
-
Limit SOQL Queries
Avoid performing SOQL queries inside loops. Instead, query all needed records in a single query and use a Map to access them by ID.
// Bulk Query Example List<Account> accounts = [SELECT Id, Name FROM Account WHERE CreatedDate = LAST_N_DAYS:30]; Map<Id, Account> accountMap = new Map<Id, Account>(); for (Account acc : accounts) { accountMap.put(acc.Id, acc); } // Access accounts by ID from the map
-
Bulkify Triggers
Ensure that triggers are designed to handle multiple records at once. Use collections to process records and avoid SOQL or DML operations inside loops.
trigger AccountTrigger on Account (before insert, before update) { Set<Id> accountIds = new Set<Id>(); for (Account acc : Trigger.new) { accountIds.add(acc.Id); } // Perform bulk operations List<Contact> contacts = [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accountIds]; // Process contacts }
-
Use Batch Apex for Large Data Volumes
When dealing with very large datasets, use Batch Apex to process records in manageable chunks.
global class MyBatchClass implements Database.Batchable<sObject> { global Database.QueryLocator start(Database.BatchableContext BC) { return Database.getQueryLocator('SELECT Id FROM Account'); } global void execute(Database.BatchableContext BC, List<sObject> scope) { // Bulk processing logic List<Account> accountsToUpdate = new List<Account>(); for (Account acc : (List<Account>)scope) { acc.Name = 'Updated Name'; accountsToUpdate.add(acc); } update accountsToUpdate; } global void finish(Database.BatchableContext BC) { // Post-processing logic } }
SOQL is used to query Salesforce data. It’s similar to SQL (Structured Query Language) but is tailored for the Salesforce data model.
Basic Syntax
SELECT field1, field2 FROM ObjectName WHERE condition
Examples
-
Query Records
// Query to select all Account records with a specific name List<Account> accounts = [SELECT Id, Name FROM Account WHERE Name = 'Acme Corp'];
-
Query with Multiple Conditions
// Query to select contacts with a specific last name and account List<Contact> contacts = [SELECT Id, FirstName, LastName FROM Contact WHERE LastName = 'Smith' AND Account.Name = 'Acme Corp'];
-
Query with Relationships
// Query to select accounts and related contacts List<Account> accountsWithContacts = [SELECT Id, Name, (SELECT Id, FirstName, LastName FROM Contacts) FROM Account];
SOSL is used for text searches across multiple objects and fields.
Basic Syntax
FIND {searchQuery} IN {RETURNING object1(field1, field2), object2(field1, field2)}
Examples
-
Search Across Objects
// Search for 'Acme' across Account and Contact objects List<List<SObject>> searchResults = [FIND 'Acme' IN ALL FIELDS RETURNING Account(Id, Name), Contact(Id, FirstName, LastName)];
-
Search with Specific Fields
// Search for 'Smith' in the LastName field of Contact List<Contact> contacts = [FIND 'Smith' IN NAME RETURNING Contact(Id, FirstName, LastName)];
DML operations are used to manipulate data, including inserting, updating, deleting, and undeleting records.
Syntax
// Insert
insert newObject;
// Update
update existingObject;
// Delete
delete existingObject;
// Undelete
undelete existingObject;
Examples
-
Insert Records
// Create a new account and insert it Account newAccount = new Account(Name = 'Acme Corp'); insert newAccount;
-
Update Records
// Update an existing account Account existingAccount = [SELECT Id, Name FROM Account WHERE Name = 'Acme Corp' LIMIT 1]; existingAccount.Name = 'Acme Corporation'; update existingAccount;
-
Delete Records
// Delete an existing account Account accountToDelete = [SELECT Id FROM Account WHERE Name = 'Acme Corporation' LIMIT 1]; delete accountToDelete;
-
Undelete Records
// Undelete a record from the Recycle Bin Account accountToUndelete = [SELECT Id FROM Account WHERE Name = 'Acme Corporation' LIMIT 1 ALL ROWS]; undelete accountToUndelete;
There are three type of Decorators in Lightning web components.
- Api
- Track
- Wire
https://www.apexhours.com/decorators-in-lightning-web-component/
Trigger.old: Returns a list of the old versions of the sObject records. Trigger.old should return a type ofList<sObject__c>.Note that this sObject list is only available in the update and delete triggers.
Trigger.oldMap: A map of IDs to the old versions of the sObject records. Trigger.oldMap should return a type of Map<Id, sObject__c>.Note that this map is only available in the update and delete triggers.
Without the annotation, the Web service callout is made from the same thread that is executing the Apex code, and no additional processing can occur until the callout is complete
In Salesforce, the @future
annotation is used to execute methods asynchronously. This can be useful for operations that need to run in the background and do not require immediate results, such as making callouts to external web services. However, if you do not use the @future
annotation for web service callouts and instead make a synchronous callout from within the same execution context, there are several considerations and potential issues:
https://github.com/therishabh/salesforce-apex/blob/main/README.md#how-to-avoid-recursion-in-trigger
A Composite Request enables you to bundle multiple REST API requests into a single HTTP call. This is helpful for operations that involve creating, updating, or querying multiple records simultaneously. It helps to:
- Reduce API Call Counts: Minimize the number of API calls by sending a single composite request instead of multiple individual requests.
- Improve Efficiency: Decrease network latency and improve performance by batching operations.
- Handle Complex Transactions: Perform complex transactions and maintain data consistency by processing multiple related operations together.
Lets understand the advantage of composite API.
- Multiple REST API calls for single call
- Processing speed can be made faster by collating the subrequests.
- Improves the performance of the application
- No additional coding required at Salesforce.
- Single call toward API limits.
- Synchronous responses.
- Multiple ways in composite resources work.
- Upsert upto five levels deep relationships.
- Flexible on upserting related or non related records.
- Can do a GET subrequest and the result can be input to next POST subrequest.
- Can handle more complicated and related objects and data.
Sample Request and URL.
URL : /services/data/vXX.X/composite
Request Body sample.
{
"compositeRequest" : [
{
"method" : "POST",
"url" : "/services/data/v52.0/sobjects/Account",
"referenceId" : "refAccount",
"body" : { "Name" : "APEX HOURS" }
},
{
"method" : "POST",
"url" : "/services/data/v52.0/sobjects/Contact",
"referenceId" : "refContact",
"body" : {
"LastName" : "AMIT CHAUDHARY",
"AccountId" : "@{refAccount.id}"
}
}]
}
Component Events in LWC are used for communication between components that have a parent-child relationship. These events are specifically designed to facilitate interaction within a component hierarchy.
Child Component (childComponent.html)
<template>
<lightning-button label="Click Me" onclick={handleClick}></lightning-button>
</template>
Child Component (childComponent.js)
import { LightningElement } from 'lwc';
export default class ChildComponent extends LightningElement {
handleClick() {
// Create and dispatch a custom event
const event = new CustomEvent('myevent', {
detail: { message: 'Hello from child' }
});
this.dispatchEvent(event);
}
}
Parent Component (parentComponent.html)
<template>
<c-child-component onmyevent={handleChildEvent}></c-child-component>
</template>
Parent Component (parentComponent.js)
import { LightningElement } from 'lwc';
export default class ParentComponent extends LightningElement {
handleChildEvent(event) {
// Handle the event from the child component
const message = event.detail.message;
console.log(message); // Outputs: Hello from child
}
}
Application Events in LWC are not a part of the framework like in Aura components. LWC uses a different approach for global communication. Instead of application events, LWC relies on other methods such as:
-
Pub/Sub (Publish/Subscribe) Model: For cross-component communication that is not tied to a parent-child relationship. This pattern is typically managed using a custom event bus or a pub/sub library.
-
Lightning Message Service (LMS): For inter-component communication within the Lightning Experience and Salesforce mobile app. LMS enables message passing across different components, even if they are not in a direct parent-child relationship.
https://github.com/therishabh/salesforce-lwc?tab=readme-ov-file#component-communication
https://medium.com/@saurabh.samirs/lightning-data-services-lds-in-lwc-67e9a50039d5
Lightning Data Service (LDS) is a powerful tool provided by Salesforce that allows Lightning Web Components (LWC) to interact with Salesforce data without the need for Apex code. It operates on the client-side and offers several key features that streamline data retrieval and management within LWC.
One of the key benefits of LDS is its client-side caching mechanism. When a component fetches data using LDS, the data is cached locally in the browser’s memory. Subsequent requests for the same data within the same session can be served from the cache, eliminating the need for additional server round trips. This improves performance and responsiveness of the application, especially in scenarios where the same data is accessed frequently.
Benefits Over Traditional Apex Data Queries:
Compared to traditional Apex data queries, LDS offers several advantages:
Reduced Server Load
: Since data is fetched and cached on the client side, LDS reduces the load on the Salesforce server by minimizing the number of server requests.
Faster Response Times
: With client-side caching, LDS can serve data more quickly, leading to faster response times and improved user experience.
Enhanced Offline Support
: LDS supports offline data access, allowing components to continue functioning even when the user is offline or experiencing connectivity issues.
Automatic Record Updates
: LDS automatically detects changes to records and updates the local cache accordingly, ensuring that components always have access to the latest data without manual refreshing.
Example:
Let’s consider an example where we have a Lightning Web Component that displays a list of accounts using LDS.
<!-- accountsList.html -->
<template>
<lightning-card title="Accounts List">
<template if:true={accounts}>
<ul>
<template for:each={accounts} for:item="account">
<li key={account.Id}>{account.Name}</li>
</template>
</ul>
</template>
<template if:false={accounts}>
No accounts found.
</template>
</lightning-card>
</template>
// accountsList.js
import { LightningElement, wire } from 'lwc';
import { getListUi } from 'lightning/uiListApi';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
export default class AccountsList extends LightningElement {
@wire(getListUi, { objectApiName: ACCOUNT_OBJECT, listViewApiName: 'All' })
wiredAccounts({ error, data }) {
if (data) {
this.accounts = data.records.records;
} else if (error) {
console.error(error);
}
}
}
In this example, we use the getListUi
wire adapter from lightning/uiListApi
to fetch a list of accounts. The result is automatically cached by LDS, and subsequent requests for the same data will be served from the cache, resulting in improved performance and responsiveness.
The base component of Lightning Data Service are
lightning-record-edit-form
— Displays an editable form.
lightning-record-view-form
— Displays a read-only form.
lightning-record-form
— Supports edit, view, and read-only modes.
In Salesforce we got the System.limitException: Too many SOQL Queries 101 very often.
In Salesforce we can encounter the Salesforce Governor Limits system limit exception too many soql queries 101 very often. This means we can only have up to 100 SOQL in a single context. This is a hard limit, which means you can’t increase it by contacting Salesforce support.
The following error appears when we exceed the Governors Limit.
System.LimitException: Too many SOQL queries: 101 error
As per the governor limit, the Total number of SOQL queries issued is 100 in Synchronous and 200 in Asynchronous context.
Let see the all other reason for SOQL 101 Error.
- Soql inside the for Loop
- A Trigger is not bulkified.
- Trigger is recursive
- Multiple processes are executing at the same time and updating the same record in the same transection.
Let see how to resolve this System.LimitException: Too many SOQL queries: 101 error.
- Avoid SOQL queries inside For Loop
- Bulkify Apex Trigger and Recursion Handling
- Move some business logic into @future
- Minimize the No.of SOQLs by merging the queries
To get data from Apex to Lightning Web Components (LWC), you generally use the @wire
decorator or imperative Apex calls. Here’s a brief overview of each method:
The @wire
decorator is used for reactive data binding. It automatically handles data retrieval and refreshes the component when the data changes.
-
Create an Apex Method
Define an Apex method in a class with
@AuraEnabled
annotation. Ensure the method ispublic
andstatic
.public with sharing class AccountController { @AuraEnabled(cacheable=true) public static List<Account> getAccounts() { return [SELECT Id, Name FROM Account LIMIT 10]; } }
-
Use
@wire
in LWCImport the Apex method and use the
@wire
decorator to call it in your LWC.HTML (myComponent.html):
<template> <template if:true={accounts.data}> <ul> <template for:each={accounts.data} for:item="account"> <li key={account.Id}>{account.Name}</li> </template> </ul> </template> <template if:true={accounts.error}> <p>Error: {accounts.error.message}</p> </template> </template>
JavaScript (myComponent.js):
import { LightningElement, wire } from 'lwc'; import getAccounts from '@salesforce/apex/AccountController.getAccounts'; export default class MyComponent extends LightningElement { @wire(getAccounts) accounts; }
Here,
accounts.data
contains the retrieved data, andaccounts.error
will handle any errors.
Imperative Apex calls are used when you need more control over when and how data is fetched. This method is useful for scenarios where the data retrieval is not reactive or is based on user actions.
-
Create an Apex Method
Same as above, define an Apex method with
@AuraEnabled
. -
Call Apex Imperatively in LWC
HTML (myComponent.html):
<template> <lightning-button label="Get Accounts" onclick={handleGetAccounts}></lightning-button> <template if:true={accounts}> <ul> <template for:each={accounts} for:item="account"> <li key={account.Id}>{account.Name}</li> </template> </ul> </template> <template if:true={error}> <p>Error: {error.message}</p> </template> </template>
JavaScript (myComponent.js):
import { LightningElement, track } from 'lwc'; import getAccounts from '@salesforce/apex/AccountController.getAccounts'; export default class MyComponent extends LightningElement { @track accounts; @track error; handleGetAccounts() { getAccounts() .then(result => { this.accounts = result; this.error = undefined; }) .catch(error => { this.error = error; this.accounts = undefined; }); } }
In this example,
handleGetAccounts
is called when a button is clicked, fetching the data imperatively and updating the component state.
@wire
Decorator: Use for reactive data binding. Automatically fetches and updates data when the data source changes.- Imperative Apex Calls: Use for more control over data fetching, typically in response to user actions or specific events.
Both methods are effective for retrieving data from Apex to LWC, and the choice between them depends on the specific requirements of your component.
https://github.com/therishabh/salesforce-lwc?tab=readme-ov-file#wire-service
In Lightning Web Components (LWC), Promises are a fundamental part of asynchronous programming and are used to handle operations that may take some time to complete, such as fetching data from a server, performing API calls, or executing other asynchronous tasks. Promises are a native JavaScript feature, and they are used extensively in LWC for handling asynchronous operations.
A Promise is an object representing the eventual completion or failure of an asynchronous operation and its resulting value. Promises provide a way to write asynchronous code that is easier to read and maintain compared to traditional callback-based approaches.
A Promise has three states:
- Pending: The initial state, where the promise has not yet been resolved or rejected.
- Fulfilled: The state when the asynchronous operation is completed successfully.
- Rejected: The state when the asynchronous operation fails.
In JavaScript, you create a Promise by using the Promise
constructor, which takes a function with two arguments: resolve
and reject
. The resolve
function is called when the operation completes successfully, while the reject
function is called when the operation fails.
Here's a basic example of creating and using a Promise:
// Create a Promise
const myPromise = new Promise((resolve, reject) => {
// Asynchronous operation
setTimeout(() => {
const success = true; // This would be your condition
if (success) {
resolve('Operation successful!');
} else {
reject('Operation failed!');
}
}, 1000); // Simulate async operation with 1 second delay
});
// Use the Promise
myPromise
.then(result => {
console.log(result); // Outputs: 'Operation successful!'
})
.catch(error => {
console.error(error); // Outputs: 'Operation failed!'
});
In LWC, Promises are commonly used for handling asynchronous operations such as:
- Making Apex Calls: When calling Apex methods from JavaScript, the returned
Promise
allows you to handle the success or failure of the call. - Fetching Data: Using
fetch
API or other asynchronous operations where Promises are used to handle the response.
Apex Method (MyApexClass.apxc)
public with sharing class MyApexClass {
@AuraEnabled(cacheable=true)
public static String getServerData() {
return 'Data from the server';
}
}
LWC JavaScript (myComponent.js)
import { LightningElement, wire } from 'lwc';
import getServerData from '@salesforce/apex/MyApexClass.getServerData';
export default class MyComponent extends LightningElement {
serverData;
connectedCallback() {
this.fetchData();
}
fetchData() {
getServerData()
.then(result => {
this.serverData = result;
console.log('Data received:', result);
})
.catch(error => {
console.error('Error:', error);
});
}
}
Promises can be chained to perform multiple asynchronous operations sequentially. Each then
returns a new Promise, allowing you to chain multiple operations.
Example
fetchData()
.then(result => {
return processResult(result); // Returns a new Promise
})
.then(processedData => {
return saveData(processedData); // Returns a new Promise
})
.then(finalResult => {
console.log('Final result:', finalResult);
})
.catch(error => {
console.error('Error:', error);
});
catch
method is used to handle errors that occur during the execution of the Promise chain. It catches any errors that occur in the then
methods prior to it.
Example
fetchData()
.then(result => {
// Process the result
return processResult(result);
})
.catch(error => {
// Handle errors that occurred in fetchData or processResult
console.error('Error:', error);
});
The async
and await
syntax provides a more concise way to work with Promises and is often preferred for readability. async
functions return a Promise, and await
is used to pause execution until the Promise is resolved or rejected.
Example
async function fetchData() {
try {
const result = await getServerData();
console.log('Data received:', result);
} catch (error) {
console.error('Error:', error);
}
}
Using async
in LWC
import { LightningElement } from 'lwc';
import getServerData from '@salesforce/apex/MyApexClass.getServerData';
export default class MyComponent extends LightningElement {
serverData;
async connectedCallback() {
try {
this.serverData = await getServerData();
console.log('Data received:', this.serverData);
} catch (error) {
console.error('Error:', error);
}
}
}
- Promises represent asynchronous operations and are used to handle the eventual completion (or failure) of these operations.
- In LWC, Promises are used for handling asynchronous operations like Apex calls or data fetching.
- Chaining Promises allows for sequential asynchronous operations, while error handling is done using
catch
. async
andawait
provide a more readable way to work with Promises, simplifying asynchronous code.
By leveraging Promises effectively, you can manage asynchronous operations in your LWC applications more efficiently, improving code readability and maintainability.
https://github.com/therishabh/salesforce-apex/blob/main/README.md#platform-event
In Lightning Web Components (LWC), styling is managed using CSS. The framework provides several methods to style your components, including the use of CSS custom properties, shadow DOM styling, and global styles. While there isn't a direct concept called "styling hooks" like in some other frameworks, you can leverage various techniques to effectively style your components.
Each LWC component has its own scoped CSS file. Styles defined in a component’s CSS file only apply to that component, thanks to Shadow DOM encapsulation.
HTML (myComponent.html):
<template>
<div class="my-class">Styled Text</div>
</template>
CSS (myComponent.css):
.my-class {
color: blue;
font-size: 16px;
}
You can use CSS custom properties to define variables that can be used across components. These can be defined in a global CSS file and used in component CSS files.
Global CSS (styles.css):
:root {
--primary-color: #3498db;
}
.my-class {
color: var(--primary-color);
}
Component CSS (myComponent.css):
.my-class {
background-color: var(--primary-color);
}
Scoped CSS variables are defined within a component and can be used within that component’s stylesheet.
Component CSS (myComponent.css):
:host {
--component-background: #f0f0f0;
}
.my-class {
background-color: var(--component-background);
}
Global styles are applied to the entire Lightning Experience. You can use them to define shared styles across multiple components. This is typically done in a styles.css
file in your Salesforce org or using static resources.
Static Resource (globalStyles.css):
.my-global-class {
font-family: Arial, sans-serif;
}
Using Global Styles in Component:
<template>
<div class="my-global-class">Global Styled Text</div>
</template>
For dynamic styling, you can use JavaScript to add or remove CSS classes based on conditions.
HTML (myComponent.html):
<template>
<div class={dynamicClass}>Dynamic Styled Text</div>
</template>
JavaScript (myComponent.js):
import { LightningElement, api } from 'lwc';
export default class MyComponent extends LightningElement {
@api isHighlighted = false;
get dynamicClass() {
return this.isHighlighted ? 'highlight' : 'normal';
}
}
CSS (myComponent.css):
.highlight {
background-color: yellow;
}
.normal {
background-color: white;
}
LWC uses Shadow DOM to encapsulate component styles, meaning styles do not bleed through to or from other components. However, you can still apply styles within the component’s shadow DOM.
Component CSS (myComponent.css):
:host {
display: block;
padding: 10px;
}
h1 {
color: green;
}
While LWC doesn't have specific "styling hooks," you can style components effectively using:
- Component-Level Styling: Scoped CSS files.
- CSS Custom Properties: For reusable variables.
- Scoped CSS Variables: Within components.
- Global Styles: For consistent styling across components.
- Dynamic Styling: Using JavaScript for condition-based styles.
- Shadow DOM: Encapsulation ensures styles are contained within the component.
These techniques allow you to create well-styled, maintainable Lightning Web Components.
https://github.com/therishabh/salesforce-apex/blob/main/README.md#deployment
https://github.com/therishabh/salesforce-apex/blob/main/README.md#deployment
In Lightning Web Components (LWC), you can use multiple decorators on a single property, but there are specific rules and scenarios where this is appropriate. The most common decorators in LWC are @api
, @track
, and @wire
.
Here's a breakdown of each decorator and their usage:
- Purpose: Marks a property or method as public, making it accessible to other components.
- Purpose: Makes a property reactive, meaning changes to the property will trigger a re-render of the component.
- Purpose: Connects a property or method to a Salesforce data source or an imperative Apex call.
You can use multiple decorators on a single property if it makes sense for the specific use case. However, they are generally used in distinct contexts, and applying them simultaneously requires understanding their combined effects.
-
@api
and@track
If you want a property to be both exposed to parent components and reactive within the component, you can combine
@api
and@track
.import { LightningElement, api, track } from 'lwc'; export default class MyComponent extends LightningElement { @api @track myProperty = 'Initial Value'; handleChange(event) { this.myProperty = event.target.value; // This change will be tracked and exposed to parent components } }
In this example,
myProperty
is exposed to parent components and will trigger re-renders when its value changes. -
@api
and@wire
You typically don't use
@wire
with@api
on the same property because@wire
is generally used for retrieving data and@api
is for public properties. Instead,@wire
is often used with a method or a private property.import { LightningElement, api, wire } from 'lwc'; import getAccount from '@salesforce/apex/AccountController.getAccount'; export default class MyComponent extends LightningElement { @api accountId; @wire(getAccount, { id: '$accountId' }) account; // The account property is automatically updated with data from the wire service }
Here,
@api
exposesaccountId
to parent components, and@wire
retrieves account data based onaccountId
.
@api
: Makes properties and methods public.@track
: Makes properties reactive within the component.@wire
: Connects properties or methods to Salesforce data sources.
You can use @api
and @track
together if you need a property to be public and reactive. However, combining @api
with @wire
directly on the same property is uncommon and typically not required because @wire
handles data binding and reactivity on its own.
Lightning Web Components (LWC) is preferred over Aura for the following reasons:
- Modern Standards: LWC uses modern web standards (like Web Components and ES6+), making it more efficient and familiar to web developers.
- Performance: LWC has better performance due to its lightweight nature and efficient rendering.
- Simplified Development: LWC offers a simpler development model with standard HTML, CSS, and JavaScript.
- Encapsulation: LWC uses Shadow DOM for better style and script encapsulation.
- Reactivity: LWC provides a reactive data-binding model for easier state management.
- Tooling: LWC integrates well with modern development tools and practices.
Aura is still used for existing components and applications but LWC is encouraged for new development due to its advantages.
To overcome Salesforce governor limits:
- Use Batch Apex, Queueable Apex, Future Methods, and Scheduled Apex for asynchronous processing.
- Optimize use of collections and avoid SOQL and DML in loops.
- Efficiently query data and use the Limits class to monitor governor limits.
- Utilize Custom Settings and Custom Metadata to reduce repetitive queries.
- Optimize trigger code and follow best practices for bulk processing.
By applying these strategies, you can manage and work around Salesforce governor limits effectively.
In Salesforce, Apex callouts are always asynchronous because of how Salesforce is designed to work efficiently for many users at the same time. Here’s a simple explanation:
-
Shared Resources: Salesforce servers are shared by many customers. If one customer makes a long call to an external system and waits for the response, it can slow down the system for everyone else. Asynchronous callouts help avoid this problem by not making anyone wait.
-
Avoiding Delays: Callouts to external systems can take a long time. If the system had to wait for these callouts to finish, it would slow down everything else. By making callouts asynchronous, Salesforce allows other tasks to continue while waiting for the external response.
-
Limits on Processing Time: Salesforce has rules to prevent any single operation from taking too long (usually no more than 10 seconds). If a callout took longer than this, it could cause errors. Asynchronous callouts help stay within these time limits.
-
Better User Experience: Users can keep using Salesforce without delays. When callouts are done in the background, users don’t have to wait for them to finish before doing other tasks.
Think of it like this: If you call a friend for help and have to wait on hold for a long time, you can’t do anything else until they answer. Instead, you send them a message (asynchronous) and continue with your tasks. When they reply, you can read the message and act on it without having wasted time waiting.
Here’s a simple example using an @future method, which is a way to tell Salesforce to do something in the background:
public class CalloutExample {
@future(callout=true)
public static void makeCallout() {
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint('https://api.example.com/data');
request.setMethod('GET');
try {
HttpResponse response = http.send(request);
if (response.getStatusCode() == 200) {
// Process the response
System.debug(response.getBody());
} else {
System.debug('Callout failed with status: ' + response.getStatusCode());
}
} catch (Exception e) {
System.debug('Callout failed: ' + e.getMessage());
}
}
}
In this example, the makeCallout
method will run in the background, allowing other tasks to continue without waiting for the external call to finish.
In summary, asynchronous callouts help Salesforce work smoothly and efficiently for everyone by handling external communications in the background.
In Salesforce Apex, the WITH SECURITY_ENFORCED
clause is used in SOQL queries to ensure that the query respects the user's field-level security (FLS) and object-level security (OLS) settings. This means that the query will only return fields and objects that the user has permission to access. This feature enhances security by preventing unauthorized access to sensitive data.
Here’s an example of how to use WITH SECURITY_ENFORCED
in a SOQL query:
public List<Account> getAccounts() {
try {
List<Account> accounts = [
SELECT Id, Name, Phone, Industry
FROM Account
WITH SECURITY_ENFORCED
];
return accounts;
} catch (Exception e) {
System.debug('Exception: ' + e.getMessage());
return null;
}
}
When using WITH SECURITY_ENFORCED
, if the user doesn't have access to the fields or objects specified in the query, a QueryException
will be thrown. You should handle this exception appropriately:
public List<Account> getAccounts() {
try {
List<Account> accounts = [
SELECT Id, Name, Phone, Industry
FROM Account
WITH SECURITY_ENFORCED
];
return accounts;
} catch (QueryException qe) {
System.debug('Query Exception: ' + qe.getMessage());
// Handle lack of permissions
return null;
} catch (Exception e) {
System.debug('General Exception: ' + e.getMessage());
return null;
}
}
To handle security in Apex triggers, follow these key strategies:
-
Field-Level and Object-Level Security (FLS and OLS)
- Check if the user has access to specific fields and objects before performing operations.
- Example:
if (!Schema.sObjectType.Account.isAccessible()) { System.debug('Access to Account object is denied.'); return; } for (Account acc : Trigger.new) { if (Schema.sObjectType.Account.fields.Name.isAccessible()) { System.debug('Account Name: ' + acc.Name); } }
-
Use
with sharing
andwithout sharing
Keywords- Use
with sharing
to enforce sharing rules andwithout sharing
to bypass them in Apex classes called by triggers. - Example:
public with sharing class AccountTriggerHandler { public static void handleBeforeUpdate(List<Account> newAccounts) { for (Account acc : newAccounts) { System.debug('Handling Account: ' + acc.Name); } } } trigger AccountTrigger on Account (before update) { AccountTriggerHandler.handleBeforeUpdate(Trigger.new); }
- Use
-
Custom Permissions
- Check if the user has specific custom permissions to control feature access.
- Example:
if (!FeatureManagement.checkPermission('Manage_Accounts')) { System.debug('User does not have Manage Accounts permission.'); return; }
-
Context Awareness
- Understand when your code runs in system context (ignoring user permissions) versus user context (respecting user permissions). Use
without sharing
sparingly.
- Understand when your code runs in system context (ignoring user permissions) versus user context (respecting user permissions). Use
By using these strategies, you ensure that your triggers respect user permissions and maintain data security.
PMD (Programming Mistake Detector) can also be used to analyze Apex code, the programming language used in Salesforce. Apex PMD helps identify common programming flaws specific to Apex development. Here are some common types of PMD violations in Apex and how to fix them:
-
ApexUnitTestClassShouldHaveAsserts
- Violation: Test classes should have assertions to verify the behavior of the code.
- Fix: Add assertions to your test methods.
// Before @IsTest private class MyTestClass { @IsTest static void testMethod() { // Test code without assertions } } // After @IsTest private class MyTestClass { @IsTest static void testMethod() { // Test code System.assertEquals(expectedValue, actualValue); } }
-
AvoidLogicInTrigger
- Violation: Triggers should not contain complex logic. Instead, use helper classes.
- Fix: Move the logic to a handler or helper class.
// Before trigger MyTrigger on Account (before insert, before update) { for (Account acc : Trigger.new) { if (acc.Name == 'Test') { acc.Description = 'This is a test account'; } } } // After trigger MyTrigger on Account (before insert, before update) { MyTriggerHandler.handleTrigger(Trigger.new); } public class MyTriggerHandler { public static void handleTrigger(List<Account> accounts) { for (Account acc : accounts) { if (acc.Name == 'Test') { acc.Description = 'This is a test account'; } } } }
-
AvoidUnusedLocalVariables
- Violation: Declaring variables that are not used.
- Fix: Remove the unused variables.
// Before public class MyClass { public void myMethod() { Integer unusedVar = 10; // Some code } } // After public class MyClass { public void myMethod() { // Some code } }
-
AvoidSoqlInLoops
- Violation: Performing SOQL queries inside loops can cause governor limits to be exceeded.
- Fix: Move the SOQL query outside the loop.
// Before public class MyClass { public void myMethod(List<Id> accountIds) { for (Id accId : accountIds) { Account acc = [SELECT Name FROM Account WHERE Id = :accId]; // Some code } } } // After public class MyClass { public void myMethod(List<Id> accountIds) { Map<Id, Account> accounts = new Map<Id, Account>([SELECT Name FROM Account WHERE Id IN :accountIds]); for (Id accId : accountIds) { Account acc = accounts.get(accId); // Some code } } }
-
CyclomaticComplexity
- Violation: Methods that are too complex.
- Fix: Refactor the method to reduce complexity.
// Before public class MyClass { public void complexMethod() { if (condition1) { // Some code } else if (condition2) { // Some code } else { // Some code } } } // After public class MyClass { public void complexMethod() { if (condition1) { handleCondition1(); } else if (condition2) { handleCondition2(); } else { handleOtherConditions(); } } private void handleCondition1() { // Some code } private void handleCondition2() { // Some code } private void handleOtherConditions() { // Some code } }
-
ApexCRUDViolation
- Violation: Not checking for CRUD permissions before performing DML operations.
- Fix: Use
Schema.sObjectType
methods to check for permissions.
// Before public class MyClass { public void myMethod() { Account acc = new Account(Name = 'Test'); insert acc; } } // After public class MyClass { public void myMethod() { if (Schema.sObjectType.Account.isCreateable()) { Account acc = new Account(Name = 'Test'); insert acc; } else { // Handle lack of permissions } } }
Fixing these PMD violations in Apex helps improve code quality, readability, performance, and ensures compliance with Salesforce best practices. Regularly running PMD on your Apex codebase is a good practice to maintain high-quality code.
Imperative Apex is essential for scenarios requiring detailed control over the application's behavior, enabling developers to implement complex, custom functionality beyond the capabilities of declarative tools.
There is one field - Type Large Text area on Account Object. we need to get first name of all the contacts related to that account. Write a trigger for above scenario.