<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Salesforce Developers</title>
    <description>The latest articles on Forem by Salesforce Developers (@salesforcedevs).</description>
    <link>https://forem.com/salesforcedevs</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F2309%2F9501ddff-9d0b-483d-8db3-a343080c20e8.png</url>
      <title>Forem: Salesforce Developers</title>
      <link>https://forem.com/salesforcedevs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/salesforcedevs"/>
    <language>en</language>
    <item>
      <title>Creating Custom API Endpoints in Salesforce with Apex</title>
      <dc:creator>Michael Bogan</dc:creator>
      <pubDate>Tue, 31 Jan 2023 21:41:53 +0000</pubDate>
      <link>https://forem.com/salesforcedevs/creating-custom-api-endpoints-in-salesforce-with-apex-2kj3</link>
      <guid>https://forem.com/salesforcedevs/creating-custom-api-endpoints-in-salesforce-with-apex-2kj3</guid>
      <description>&lt;h2&gt;
  
  
  Part One: SOAP-based APIs
&lt;/h2&gt;

&lt;p&gt;Simple Object Access Protocol (SOAP) is a messaging protocol based on requests and responses using an XML format. Although some legacy systems still use SOAP over SMTP, the transport method typically used for SOAP requests is HTTP. As an API protocol, SOAP is platform- and language-agnostic, allowing for two applications running on completely different systems to communicate with one another.&lt;/p&gt;

&lt;p&gt;This post is part of a two-part series covering how to create custom API endpoints in Salesforce with APEX. In Part One, we’ll walk through how to create a SOAP-based API endpoint for other applications to use when communicating with your Salesforce org.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Brief Introduction to SOAP
&lt;/h3&gt;

&lt;p&gt;Each SOAP message is contained in an “envelope” which has a header and a body. The header contains application-related information, such as the date when the message was generated or ids related to the message context. The body contains the actual message. Here is an example SOAP message with a format that would be used for authenticating to Salesforce:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:enterprise.soap.sforce.com"&amp;gt;
   &amp;lt;soapenv:Header&amp;gt;
      &amp;lt;urn:LoginScopeHeader&amp;gt;
         &amp;lt;urn:organizationId&amp;gt;12345&amp;lt;/urn:organizationId&amp;gt;
         &amp;lt;urn:portalId&amp;gt;abcde&amp;lt;/urn:portalId&amp;gt;
      &amp;lt;/urn:LoginScopeHeader&amp;gt;
   &amp;lt;/soapenv:Header&amp;gt;
   &amp;lt;soapenv:Body&amp;gt;
      &amp;lt;urn:login&amp;gt;
         &amp;lt;urn:username&amp;gt;johndoe&amp;lt;/urn:username&amp;gt;
         &amp;lt;urn:password&amp;gt;mypassword&amp;lt;/urn:password&amp;gt;
      &amp;lt;/urn:login&amp;gt;
   &amp;lt;/soapenv:Body&amp;gt;
&amp;lt;/soapenv:Envelope&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Since each message has the same structure, SOAP is system agnostic. For example, an application running on a Windows 11 machine can send a message to another application running on a Unix-based web server.&lt;/p&gt;

&lt;p&gt;Though the message can be sent using SMTP or HTTP, SOAP messages should ideally be sent with HTTPS, with either simple or mutual authentication.&lt;/p&gt;

&lt;p&gt;SOAP is mostly used to create an &lt;em&gt;API&lt;/em&gt; or &lt;em&gt;web service&lt;/em&gt; to exchange server-to-server information. Therefore, it is very important for the two systems to establish trust. Salesforce provides multiple ways to accomplish authentication.&lt;/p&gt;

&lt;h4&gt;
  
  
  The relationship between SOAP and WSDL
&lt;/h4&gt;

&lt;p&gt;Although all SOAP messages have the same structure, the SOAP specification doesn’t define other aspects, such as message format or transport protocol. The Web Services Description Language (WSDL) describes the message formats, transport details, and operations of the web service. The WSDL file can be considered a contract between the client and the service.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Create a SOAP-based API in Salesforce
&lt;/h3&gt;

&lt;p&gt;Now that we have covered the fundamentals, let’s begin implementing our SOAP API.&lt;/p&gt;

&lt;h4&gt;
  
  
  Set up a Salesforce org
&lt;/h4&gt;

&lt;p&gt;First, you need a Salesforce org. Salesforce provides free Developer edition orgs, and signing up for one is straightforward. Go to the &lt;a href="https://developer.salesforce.com/signup" rel="noopener noreferrer"&gt;sign-up page&lt;/a&gt; and provide your details. You should receive an invite link within a few minutes.&lt;/p&gt;

&lt;h4&gt;
  
  
  Creating a global class with Apex
&lt;/h4&gt;

&lt;p&gt;To create a SOAP web service in Salesforce, we will need to create a globally defined Apex custom class. In our class, we’ll use the &lt;code&gt;webservice&lt;/code&gt; keyword and &lt;code&gt;static&lt;/code&gt; definition modifier for every method we want to expose. Adding the &lt;code&gt;webservice&lt;/code&gt; keyword automatically gives global access to the method it is added to.&lt;/p&gt;

&lt;p&gt;In order to create this class in our shiny, new org, we go to &lt;strong&gt;Setup&lt;/strong&gt;, and then find &lt;strong&gt;Custom Code -&amp;gt; Apex Classes&lt;/strong&gt;. In the table listing existing classes, we click &lt;strong&gt;New&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0bvrzvvziqbsolyk7vf9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0bvrzvvziqbsolyk7vf9.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To learn more about Apex and how to create classes, check out this &lt;a href="https://trailhead.salesforce.com/en/content/learn/modules/apex_database?trail_id=force_com_dev_beginner" rel="noopener noreferrer"&gt;Trailhead page&lt;/a&gt; and the &lt;a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_understanding.htm" rel="noopener noreferrer"&gt;Apex documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Below, we have a sample &lt;code&gt;ContactAPI&lt;/code&gt; class that exposes a &lt;code&gt;createContact&lt;/code&gt; method used to create new Contact records in Salesforce. The method accepts a Contact’s details as parameters and inserts a new Contact record.&lt;/p&gt;

&lt;p&gt;If successful, it returns the ID of the newly created Contact along with a message. In case of a failure, it returns an error message. Please copy the code and paste it into the newly created empty class.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

global class ContactAPI {
  webservice static String createContact (String firstName, String lastName, String contactEmail, String salutation) { 
    try {
      Contact newContact = new Contact();
      newContact.Salutation = salutation;
      newContact.FirstName = firstName;
      newContact.LastName = lastName;
      newContact.Email = contactEmail;

      insert newContact;
      return 'Successfully inserted new Contact! Id:' + String.valueOf(newContact.Id);
    } catch (Exception e) {
      return 'Error:' + e.getMessage();
    }
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Next, we click &lt;strong&gt;Save&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That’s it! Our web service is ready for operations. We have successfully created a SOAP API in Salesforce.&lt;/p&gt;

&lt;h4&gt;
  
  
  Generate the WSDL
&lt;/h4&gt;

&lt;p&gt;We now need to generate a WSDL file and provide it to the external application or third-party developers which can then consume it to call our &lt;code&gt;createContact&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Salesforce provides two out-of-the-box WSDL files to integrate client applications with Salesforce:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;strong&gt;Enterprise WSDL&lt;/strong&gt; is optimized for a single Salesforce org. It’s strongly typed, meaning it contains objects and field definitions for this particular org. Whenever there is a change in the metadata (object and/or fields) in the Salesforce org, a new WSDL needs to be generated and provided to the client (to consume again).&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Partner WSDL&lt;/strong&gt; is optimized for use with many Salesforce orgs. It’s loosely typed, and it doesn’t change based on an org’s specific configuration.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In order to utilize our web service, we’ll use the WSDL for our Apex class (to actually call our web service), and we’ll use the Enterprise WSDL to authenticate to Salesforce.&lt;/p&gt;

&lt;p&gt;After creating our &lt;code&gt;ContactAPI&lt;/code&gt; class, we see it show up in the list of Apex classes. In the row for ContactAPI, we click on the &lt;strong&gt;WSDL&lt;/strong&gt; link to generate a WSDL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd4z4wuw98tgbhf28kfnp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd4z4wuw98tgbhf28kfnp.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will open a new tab with our WSDL, which will look similar to this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuawsoq5p45d4a9l3yb65.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuawsoq5p45d4a9l3yb65.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We save this file to our local machine, naming it &lt;code&gt;contactAPI-wsdl.xml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To generate the Enterprise WSDL for our Salesforce org, we go to the Setup page, and then find &lt;strong&gt;Integrations -&amp;gt; API&lt;/strong&gt;. Then, we click on &lt;strong&gt;Generate Enterprise WSDL&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjvnptznbiai8dx4avm29.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjvnptznbiai8dx4avm29.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your generated WSDL should look similar to this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Felrqs8h92sa0jf2yfrjp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Felrqs8h92sa0jf2yfrjp.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We save this file to our local machine, naming it &lt;code&gt;enterprise_wsdl.xml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To generate the WSDL for your Apex Class:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to the Setup page.&lt;/li&gt;
&lt;li&gt;Type and select Apex Classes.&lt;/li&gt;
&lt;li&gt;Find your class. In our case it’s ContactAPI. Click on the name.&lt;/li&gt;
&lt;li&gt;On the next screen you will have the class page where you have the &lt;strong&gt;Generate WSDL&lt;/strong&gt; button.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Test and validate
&lt;/h3&gt;

&lt;p&gt;To ensure that the web service works, we’ll consume it by calling it from our web application and see if it responds correctly. We’ll also cover how to validate our API with unit testing.&lt;/p&gt;

&lt;h4&gt;
  
  
  Consume the API
&lt;/h4&gt;

&lt;p&gt;For the purpose of this demo, we’ll use &lt;a href="https://www.soapui.org/tools/soapui/" rel="noopener noreferrer"&gt;SoapUI&lt;/a&gt;, which is an open-source tool to test SOAP (and other protocols) web services. With SoapUI installed and running, we create a new SOAP Project, providing a name and selecting our &lt;code&gt;enterprise_wsdl.xml&lt;/code&gt; file as the initial WSDL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsd5hosq6uama5oa7kg5j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsd5hosq6uama5oa7kg5j.png"&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;We now have all available Salesforce SoapBindings listed on the left.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyirwq7v1b0e0acrrifq8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyirwq7v1b0e0acrrifq8.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, we need to authenticate our web application to Salesforce. We do this by using the &lt;code&gt;login&lt;/code&gt; call and providing our login credentials.&lt;/p&gt;

&lt;h4&gt;
  
  
  Use a security token
&lt;/h4&gt;

&lt;p&gt;For added security, we also need to add a &lt;strong&gt;Security Token&lt;/strong&gt; to our password to authenticate. We can get a security token by going to the &lt;strong&gt;Settings&lt;/strong&gt; page and selecting &lt;strong&gt;Reset My Security Token&lt;/strong&gt; on the left. Then, press the button with the same name. We will then receive our security token by email.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuecknxvifhpqmpw0al8w.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuecknxvifhpqmpw0al8w.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi5w9nlglp0qj9ri76ay3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi5w9nlglp0qj9ri76ay3.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Log in with the token and credentials
&lt;/h4&gt;

&lt;p&gt;With our token in hand, we select the &lt;code&gt;login&lt;/code&gt; SoapBinding in SoapUI. Ensure you have the correct URL depending on your org. If you just signed up for a developer account, the URL should be &lt;a href="https://login.salesforce.com" rel="noopener noreferrer"&gt;https://login.salesforce.com&lt;/a&gt;. If you are testing this in a Sandbox, then the URL should be &lt;a href="https://login.salesforce.com" rel="noopener noreferrer"&gt;https://test.salesforce.com&lt;/a&gt;. Ideally, the generated Enterprise WSDL will already have the correct URL.&lt;/p&gt;

&lt;p&gt;For demo testing, we can comment out all other parameters except username and password.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvd3hhook1q2p0m8qb0nc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvd3hhook1q2p0m8qb0nc.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We press the green &lt;strong&gt;Play&lt;/strong&gt; button on the top left of the window to send the request. We receive a response that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj92rxaazirq5ojwmimth.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj92rxaazirq5ojwmimth.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The critical pieces that we are looking for in the response are &lt;code&gt;sessionId&lt;/code&gt; and &lt;code&gt;serverUrl&lt;/code&gt;. In order to call our web service, we need to provide the &lt;code&gt;sessionId&lt;/code&gt; together with other parameters in our call. We copy down this &lt;code&gt;sessionId&lt;/code&gt;, as we will need this later.&lt;/p&gt;

&lt;h4&gt;
  
  
  Call our SOAP API
&lt;/h4&gt;

&lt;p&gt;Let’s call our &lt;code&gt;ContactAPI&lt;/code&gt; class and create a Contact in Salesforce. To do this, we need to add our ContactAPI WSDL file, &lt;code&gt;contactAPI-wsdl.xml&lt;/code&gt;, to our project. We right-click on the project folder and select &lt;strong&gt;Add WSDL&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1geauuvlvf2vuqsvyi0f.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1geauuvlvf2vuqsvyi0f.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After adding the WSDL, it also appears in the left menu like below. Double-click on &lt;strong&gt;Request 1&lt;/strong&gt; to see the request. For this example, we can remove everything from the SOAP envelope header except for the &lt;code&gt;SessionHeader&lt;/code&gt; with &lt;code&gt;sessionId&lt;/code&gt; node.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftvgw99fht83k01ifpl5w.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftvgw99fht83k01ifpl5w.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After removing the unwanted nodes, we provide the &lt;code&gt;sessionId&lt;/code&gt; that we copied from our login call earlier. Then, in the &lt;code&gt;Body&lt;/code&gt; section of the envelope, we provide the details of the contact to be created. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvbic2obppa3uvco4nmik.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvbic2obppa3uvco4nmik.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We press the green &lt;strong&gt;Play&lt;/strong&gt; button on the top left. Vóila! We have successfully created a Contact in Salesforce and received its &lt;code&gt;Id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwfanfi0sx8ygg94169f3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwfanfi0sx8ygg94169f3.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Check Salesforce org for new contact
&lt;/h4&gt;

&lt;p&gt;Let’s look inside our Salesforce org to see if everything was created correctly. We go to our Salesforce org, click the &lt;strong&gt;App Launcher&lt;/strong&gt; waffle icon on the left and then type &lt;code&gt;Contact&lt;/code&gt; in the search bar to find and select &lt;strong&gt;Contacts&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgf9hc8bhd4q2wkcwwayq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgf9hc8bhd4q2wkcwwayq.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By default, we land on the Contacts page with the &lt;em&gt;Recently Viewed Contacts&lt;/em&gt; list view. Click the down arrow and select either &lt;strong&gt;All Contacts&lt;/strong&gt; or &lt;strong&gt;My Contacts&lt;/strong&gt; list view. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2werc53jqvr9gciidf0r.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2werc53jqvr9gciidf0r.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: If you are in a Developer org, you will see a bunch of dummy Contacts (and other records) that Salesforce creates for you to test the platform.&lt;/p&gt;

&lt;p&gt;We type the name of our newly created contact in the list search box on the right, and we see our new contact!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F64zqi5wwn5wyw8y4vskp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F64zqi5wwn5wyw8y4vskp.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we click on the Contact name, we end up on the Details page for that Contact.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcsuxiky7l5c7kcb2yhox.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcsuxiky7l5c7kcb2yhox.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Call our SOAP API with invalid data
&lt;/h4&gt;

&lt;p&gt;Lastly, in order to ensure that our API is working properly, let’s send a bad request to see what we get as a response. We’ll simply send another request to our SOAP web service, but this time replace the &lt;code&gt;.&lt;/code&gt; in the email address with a &lt;code&gt;,&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffk161dpn5o7j739h94z9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffk161dpn5o7j739h94z9.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This time, we receive an &lt;em&gt;error message&lt;/em&gt; (as we programmed in the API). &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgjnwac0mokk8hft7lbp0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgjnwac0mokk8hft7lbp0.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The entire error message reads like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Error:Insert failed. First exception on row 0; first error: INVALID_EMAIL_ADDRESS, Email: invalid email address: sheila@gmail,com: [Email]


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This shows that our SOAP-based API is working as designed. With this foundation, we can build other SOAP-based APIs to create records for different objects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unit Testing
&lt;/h3&gt;

&lt;p&gt;Besides consuming our SOAP-based API, another way to validate it works properly is through testing. Test Driven Development (TDD) is at the core of Salesforce development. To ensure stability and reliability of the platform, Salesforce requires your custom code to have a minimum of 75% code coverage via unit tests in order to deploy it to a production environment.&lt;/p&gt;

&lt;p&gt;Let’s create some unit tests for our web service.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

@isTest
private class TestContactWebService {
  static testMethod void testInsertNewContact() {
    // Create test data for positive test
    String salutation = 'Mr.';
    String firstName = 'Max';
    String lastName = 'Bond';
    String emailAddress = 'm.bond@mi5.gov.uk';

    Test.startTest();
    //Call the method with parameters
    String result = ContactAPI.createContact(firstName, lastName, emailAddress, salutation);
    Test.stopTest();

    Contact newContact = [SELECT FirstName FROM Contact WHERE LastName = 'Bond'];
    System.assertEquals(newContact.FirstName, 'Max', 'Assert Error: Please check the ContactAPI class');
  }

  static testMethod void testInsertNewContactFail() {
    // Create test data for failing test
    String salutation = 'Mr.';
    String firstName = 'Max';
    String lastName = 'Bond';
    // The email address has , instead of .
    String badEmailAddress = 'm.bond@mi5,gov,uk';

    Test.startTest();

    //Call the method with parameters
    String result = ContactAPI.createContact(firstName, lastName, badEmailAddress, salutation);
   Test.stopTest();

   // This should contain the Error substring that we added to the catch block above.
   System.assert(result.contains('Error:'), 'Fail Assert Error: Please check the ContactAPI class');
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In our unit test, we created two scenarios: a “happy path” that verifies valid inputs result in a newly created Contact record, and a “sad path” where we expect the API to return an error on invalid inputs. Salesforce also provides a &lt;a href="https://trailhead.salesforce.com/en/content/learn/modules/apex_testing/apex_testing_intro" rel="noopener noreferrer"&gt;Trailhead on unit testing in Apex&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In this post, we only scratched the surface in terms of the capabilities that Salesforce provides when it comes to SOAP. As we saw, the generated Enterprise WSDL makes a long list of SoapBindings available. In addition, we can create our own custom classes and SOAP-based web services. &lt;/p&gt;

&lt;p&gt;If you have a (legacy) application that uses SOAP to communicate with other systems, you now have the tools to connect that application to your Salesforce org. With enterprise-level security baked into the platform, you can be sure that your connection is safe and your data is protected.&lt;/p&gt;

&lt;p&gt;In Part Two of our series, we’ll cover REST APIs, demonstrating how to use Apex to create a custom REST API endpoint for our Salesforce org.&lt;/p&gt;

</description>
      <category>salesforce</category>
      <category>webdev</category>
      <category>api</category>
    </item>
    <item>
      <title>Salesforce Functions with Heroku Data for Redis</title>
      <dc:creator>Michael Bogan</dc:creator>
      <pubDate>Tue, 27 Dec 2022 18:33:06 +0000</pubDate>
      <link>https://forem.com/salesforcedevs/salesforce-functions-with-heroku-data-for-redis-4gj6</link>
      <guid>https://forem.com/salesforcedevs/salesforce-functions-with-heroku-data-for-redis-4gj6</guid>
      <description>&lt;h3&gt;
  
  
  Salesforce Functions and Heroku Data Series: Part Two of Three
&lt;/h3&gt;

&lt;p&gt;This article is part two of a three-part series on using &lt;a href="https://www.heroku.com/managed-data-services" rel="noopener noreferrer"&gt;Heroku Managed Data&lt;/a&gt; products from within a &lt;a href="https://developer.salesforce.com/docs/platform/functions/overview" rel="noopener noreferrer"&gt;Salesforce Function&lt;/a&gt;. In part one, we focused on Salesforce Functions with &lt;a href="https://www.heroku.com/postgres" rel="noopener noreferrer"&gt;Heroku Postgres&lt;/a&gt;. Here in part two, we’ll explore Salesforce Functions with &lt;a href="https://www.heroku.com/redis" rel="noopener noreferrer"&gt;Heroku Data for Redis&lt;/a&gt;. Finally, in part three, we’ll cover Salesforce Functions and &lt;a href="https://www.heroku.com/kafka" rel="noopener noreferrer"&gt;Apache Kafka on Heroku&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to Core Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is a Salesforce Function?
&lt;/h3&gt;

&lt;p&gt;A Salesforce Function is a custom piece of code used to extend your Salesforce apps or processes. The custom code can leverage the language and libraries you choose while being run in the secure environment of your Salesforce instance.&lt;/p&gt;

&lt;p&gt;For example, you could leverage a JavaScript library to calculate and cache a value based on a triggered process within Salesforce. If you are new to Functions in general, check out “&lt;a href="https://trailhead.salesforce.com/content/learn/modules/salesforce-functions-quick-look/get-to-know-salesforce-functions" rel="noopener noreferrer"&gt;Get to Know Salesforce Functions&lt;/a&gt;” to learn about what they are and how they work.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Heroku Data for Redis?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.heroku.com/redis" rel="noopener noreferrer"&gt;Heroku Data for Redis&lt;/a&gt; is a &lt;a href="https://redis.io/" rel="noopener noreferrer"&gt;Redis&lt;/a&gt; key-value datastore that is fully managed for you by Heroku. That means that Heroku takes care of things like security, backups, and maintenance. All you need to do is use it. Because Heroku is part of Salesforce, this makes access and security much easier. The &lt;a href="https://devcenter.heroku.com/categories/heroku-redis" rel="noopener noreferrer"&gt;Heroku Dev Center documentation&lt;/a&gt; is an excellent place to find more details on Heroku Data for Redis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples of Salesforce Functions + Heroku Data for Redis
&lt;/h2&gt;

&lt;p&gt;Redis is commonly used for ephemeral data that you want quick access to. Examples include cached values, a queue of tasks to be performed by workers, session or state data for a process, or users visiting a website. While Redis can persist data to disk, it is primarily used as an “in-memory” datastore. Let’s review several use cases to give you a better idea of how Salesforce Functions and Redis can fit together.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Use Case #1: Store state between function runs
&lt;/h3&gt;

&lt;p&gt;There may be times when a process has multiple stages, with each stage requiring a function run. When the next function runs, you want to capture the state of that function run to be used by the next function that runs. &lt;/p&gt;

&lt;p&gt;An example of this might be a price quoting process that requires some backend calculations at each stage. Different people or teams might perform the steps in the process. It’s possible they don’t even all belong within a single Salesforce Org. However, the function that runs at each stage needs to know about the previous outcome. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8nisagdwjau9xqebznlj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8nisagdwjau9xqebznlj.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Case #2: Managing a queue for worker processes
&lt;/h3&gt;

&lt;p&gt;This use case is concerned with flexibility around background jobs. Because applications built on Salesforce run on a &lt;a href="https://architect.salesforce.com/fundamentals/platform-multitenant-architecture" rel="noopener noreferrer"&gt;multitenant architecture&lt;/a&gt;, Salesforce places restrictions on CPU and memory usage for applications. Long-running programs are often out of bounds and restricted.&lt;/p&gt;

&lt;p&gt;Then how might you run a long or heavy task for your Salesforce Org? The answer is Salesforce Functions. You can wire up your function to gather the information needed and insert it into Redis. Then, your &lt;a href="https://devcenter.heroku.com/articles/background-jobs-queueing" rel="noopener noreferrer"&gt;Heroku worker processes&lt;/a&gt; can retrieve the information and perform the tasks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fckri8er5rj5y4ahd9zwg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fckri8er5rj5y4ahd9zwg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Case #3: Cache the results of expensive operations
&lt;/h3&gt;

&lt;p&gt;In this last use case, let’s assume that you have an expensive query or calculation. The result does not change often, but the report that needs the result runs frequently. For example, perhaps we want to match some criteria across a large number of records that seldom change. We can use a Salesforce Function to do the work and Redis to store the result. Subsequent executions of the function can simply grab the cached result.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F90hqc28jso4zjafitaf1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F90hqc28jso4zjafitaf1.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I get started?
&lt;/h2&gt;

&lt;p&gt;To get started, you’ll need to have a few pieces in place—both on the Salesforce Functions side and the Heroku side.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prerequisites

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.salesforce.com/docs/platform/functions/overview" rel="noopener noreferrer"&gt;Access to Salesforce Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://signup.heroku.com/" rel="noopener noreferrer"&gt;Access to Heroku Data services&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Getting started with Salesforce Functions

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.salesforce.com/docs/platform/functions/guide/index.html" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Accessing Heroku Data for Redis from a Salesforce Function
&lt;/h3&gt;

&lt;p&gt;Once you have covered the prerequisites and created your project, you can run the following commands to create a Function with Heroku Data for Redis access.&lt;/p&gt;

&lt;p&gt;To create the new JavaScript Function, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sf generate function -n yourfunction -l javascript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That will give you a &lt;code&gt;/functions&lt;/code&gt; folder with a Node.js application template.&lt;/p&gt;

&lt;h4&gt;
  
  
  Connecting to your Redis instance
&lt;/h4&gt;

&lt;p&gt;Your function code can use the &lt;code&gt;&lt;a href="https://www.npmjs.com/package/dotenv" rel="noopener noreferrer"&gt;dotenv&lt;/a&gt;&lt;/code&gt; package for specifying the Redis URL as an environment variable and the &lt;code&gt;&lt;a href="https://www.npmjs.com/package/redis" rel="noopener noreferrer"&gt;node-redis&lt;/a&gt;&lt;/code&gt; package as a Redis client. Connecting to Redis might look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "dotenv/config";
import { createClient } from 'redis';

async function redisConnect() {
  const redis = createClient({
    url: process.env.REDIS_URL,
    socket: {
      tls: true,
      rejectUnauthorized: false
    }
  });
  await redis.connect();
  return redis;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For local execution, using &lt;code&gt;process.env&lt;/code&gt; and &lt;code&gt;dotenv&lt;/code&gt; assumes that you have a &lt;code&gt;.env&lt;/code&gt; file that specifies your &lt;code&gt;REDIS_URL&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Store data in Redis
&lt;/h4&gt;

&lt;p&gt;The actual body of your Salesforce Function will involve performing some calculations or data fetches, followed by storing the result in Redis. An example may look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default async function (event, context) {
  const redis = await redisConnect();
  const CACHE_KEY = `my_cache_key`;
  const CACHE_TTL_SECONDS = 86400;

  // Check Redis for cached value
  let cached_value = await redis.get(CACHE_KEY);

  if (cached_value) {
    return { result: cached_value }
  } else {
    // Perform some calculation
    const calculated_value = await perform_long_running_computation();

    // Store in Redis
    redis.set(CACHE_KEY, calculated_value, {
      EX: CACHE_TTL_SECONDS,
      NX: true
    });
    // Return result
    return { result: calculated_value }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test your Salesforce Function locally
&lt;/h3&gt;

&lt;p&gt;To test your Function locally, you first run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sf run function start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, you can invoke the Function with a payload from another terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sf run function -l http://localhost:8080 -p '{"payloadID": "info"}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more information on running Functions locally, see &lt;a href="https://developer.salesforce.com/blogs/2021/11/getting-started-with-salesforce-functions-locally-no-license-required" rel="noopener noreferrer"&gt;this guide&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Associate your Salesforce Function and your Heroku environment
&lt;/h3&gt;

&lt;p&gt;After verifying locally that our Function runs as expected, we can associate our Saleseforce Function with a compute environment. (See this &lt;a href="https://developer.salesforce.com/docs/platform/functions/guide/deploy.html" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; for more information about how to create a compute environment and deploy a function.)&lt;/p&gt;

&lt;p&gt;Now, associate your functions and Heroku environments by adding your Heroku user as a collaborator to your function’s compute environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sf env compute collaborator add --heroku-user username@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The environments can now share Heroku data. Next, you will need the name of the compute environment so that you can attach the datastore to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sf env list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, attach the datastore.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ heroku addons:attach &amp;lt;your-heroku-redis&amp;gt; --app &amp;lt;your-compute-environment-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here are some additional resources that may be helpful for you as you begin implementing your Salesforce Function and accessing Heroku Data for Redis:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/trailheadapps/functions-recipes/tree/main/functions/03_Context_UnitOfWork_JS" rel="noopener noreferrer"&gt;JavaScript Unit of Work example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devcenter.heroku.com/articles/connecting-heroku-redis" rel="noopener noreferrer"&gt;Connecting to Heroku Data for Redis from JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;And just like that, you’re up and running with a Salesforce Function connecting to Heroku Data for Redis!&lt;/p&gt;

&lt;p&gt;Salesforce Functions give you the flexibility and freedom to work within your Salesforce application to access Heroku data—whether that data is in Postgres, Redis, or even Kafka. In this second part of our series, we’ve touched on using Salesforce Functions to work with Heroku Data for Redis. While this is a fairly high-level overview, you should be able to see the potential of combining these two features. In the final post for this series, we’ll integrate with Apache Kafka on Heroku.&lt;/p&gt;

</description>
      <category>salesforce</category>
      <category>heroku</category>
      <category>redis</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Salesforce Functions with Heroku Postgres</title>
      <dc:creator>Michael Bogan</dc:creator>
      <pubDate>Mon, 19 Dec 2022 15:16:06 +0000</pubDate>
      <link>https://forem.com/salesforcedevs/salesforce-functions-with-heroku-postgres-4dm</link>
      <guid>https://forem.com/salesforcedevs/salesforce-functions-with-heroku-postgres-4dm</guid>
      <description>&lt;h3&gt;
  
  
  Salesforce Functions and Heroku Data Series: Part One of Three
&lt;/h3&gt;

&lt;p&gt;This article is the first of a three-part series on utilizing &lt;a href="https://www.heroku.com/managed-data-services" rel="noopener noreferrer"&gt;Heroku Managed Data&lt;/a&gt; products from within a &lt;a href="https://developer.salesforce.com/docs/platform/functions/overview" rel="noopener noreferrer"&gt;Salesforce Function&lt;/a&gt;. In this article, we will focus on use cases for &lt;a href="https://www.heroku.com/postgres" rel="noopener noreferrer"&gt;Heroku Postgres&lt;/a&gt;. In parts two and three, we’ll discuss creating Salesforce Functions that use &lt;a href="https://www.heroku.com/redis" rel="noopener noreferrer"&gt;Heroku Data for Redis&lt;/a&gt; and &lt;a href="https://www.heroku.com/kafka" rel="noopener noreferrer"&gt;Apache Kafka on Heroku&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to Core Concepts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is a Salesforce Function?
&lt;/h3&gt;

&lt;p&gt;A Salesforce Function is a custom piece of code used to extend your Salesforce apps or processes. The custom code can leverage the language and libraries you choose while running in the secure environment of your Salesforce instance. &lt;/p&gt;

&lt;p&gt;For example, you could leverage a JavaScript library to update your Heroku Postgres Database based on a triggered process within Salesforce. If you are new to Salesforce Functions in general, “&lt;a href="https://trailhead.salesforce.com/content/learn/modules/salesforce-functions-quick-look/get-to-know-salesforce-functions" rel="noopener noreferrer"&gt;Get to Know Salesforce Functions&lt;/a&gt;” is a good place to learn more about what a Salesforce Function is and how it works.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Heroku Postgres?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.heroku.com/postgres" rel="noopener noreferrer"&gt;Heroku Postgres&lt;/a&gt; is a &lt;a href="https://www.postgresql.org/" rel="noopener noreferrer"&gt;Postgres&lt;/a&gt; database that Heroku manages for you. That means that Heroku takes care of things like security, backups, and maintenance. All you have to do is use it. The best place to learn the details of Heroku Postgres is in the &lt;a href="https://devcenter.heroku.com/categories/heroku-postgres" rel="noopener noreferrer"&gt;Heroku Devcenter documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples of Salesforce Function + Heroku Postgres
&lt;/h2&gt;

&lt;p&gt;Now that we know more about Functions and Heroku Postgres, what can we do with them together? There are several ways to think of Functions, but one of the most useful is to think about them as &lt;strong&gt;aiding integration or extension of your Org&lt;/strong&gt;(s). For example, when something happens in Salesforce, &lt;em&gt;do some other task&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;At a high level, you would want to use Heroku Postgres with your Salesforce Function if you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Need to access a relational database as part of your function’s operation&lt;/li&gt;
&lt;li&gt;Need to integrate with a Heroku Postgres instance that is part of another system&lt;/li&gt;
&lt;li&gt;Need to offload a “heavy” task from your Salesforce Org and store relational data to achieve it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The above patterns are not the only ones, but they are perhaps the most common. The use cases below are examples of these patterns in action.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Case #1: De-dupe account records for multiple Salesforce Orgs
&lt;/h3&gt;

&lt;p&gt;A common problem in a growing company is having multiple Salesforce Orgs with customers duplicated across them. Sometimes, this occurs because the company has acquired another company, and both companies share the customer. &lt;/p&gt;

&lt;p&gt;In other cases, the company just had different business lines, and the customer was part of both. In reality, if we know two customers are really the same customer, then we want to treat them as one customer. It often takes a long time (if ever) to merge this Org data, so what do we do?&lt;/p&gt;

&lt;p&gt;One approach is to sync account information from both Orgs via &lt;a href="https://devcenter.heroku.com/articles/heroku-connect" rel="noopener noreferrer"&gt;Heroku Connect&lt;/a&gt; into &lt;a href="https://devcenter.heroku.com/categories/heroku-postgres" rel="noopener noreferrer"&gt;Heroku Postgres&lt;/a&gt;, and trigger a Salesforce Function that can look for duplicates. We may run this function on demand for certain activities or set it up to run after creating new accounts. An example of this is whenever the system encounters “Customer’s email already exists in [Parent Company]”. The following diagram illustrates this use case:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhxgchqiqnfewqflgctvb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhxgchqiqnfewqflgctvb.png" width="730" height="563"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Case #2: Update customer-facing website when new inventory is added
&lt;/h3&gt;

&lt;p&gt;Many companies use Salesforce as the “source of truth” for critical information, but they have a customer-facing website on Heroku. In this use case, when an item (such as an inventory number) is updated in Salesforce, a Salesforce Function is triggered to update Heroku Postgres, which backs the website that displays this information for customers.&lt;/p&gt;

&lt;p&gt;This pattern could be extended to do other things, such as fetch additional information to store with the record in Heroku Postgres or purge the cache. This can be useful when the information is needed for the website, but you don’t need to store it in Salesforce.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Case #3: Generate reports for large data sets
&lt;/h3&gt;

&lt;p&gt;In a large organization that uses Salesforce heavily, there can be overlapping tasks by many users. Often, end users are unaware of their impact on the shared system. Moving heavy operations or reports off the Org can allow other operations to perform better. An easy way to do this is to sync the required data to Heroku Postgres via Heroku Connect and perform the operation via a Salesforce Function. This opens up options for optimizing and leveraging open source tools and libraries to process the data.&lt;/p&gt;

&lt;p&gt;This pattern also works well if the result needs to be written back to Salesforce. An example of that would be if you needed to calculate sales territories but store the results back in the Salesforce Org. &lt;/p&gt;

&lt;p&gt;Salesforce Functions are a powerful new feature in the Salesforce ecosystem. Heroku Postgres is a battle-tested market leader in managed Postgres. Together, they open up many use cases. In combination with additional services offered by Heroku, there are many options for getting the most out of your Salesforce data. In the next section, we’ll briefly examine how to get started with your own use cases. &lt;/p&gt;

&lt;h2&gt;
  
  
  How do I get started?
&lt;/h2&gt;

&lt;p&gt;There are some things you will need—some on the Salesforce Functions side and some on the Heroku side. These resources will point you in the right direction.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prerequisites

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.salesforce.com/docs/platform/functions/overview" rel="noopener noreferrer"&gt;Access to Salesforce Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://signup.heroku.com/" rel="noopener noreferrer"&gt;Access to Heroku Data services&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Getting started with Salesforce Functions

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.salesforce.com/docs/platform/functions/guide/index.html" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Accessing Heroku Postgres from a Salesforce Function
&lt;/h3&gt;

&lt;p&gt;Once you have covered the prerequisites and you create your project, you can run the following commands to create a Function with Heroku Postgres access. &lt;/p&gt;

&lt;p&gt;To create the new JavaScript Function, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sf generate function -n yourfunction -l javascript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That will give you a &lt;code&gt;/functions&lt;/code&gt; folder with a Node.js application template.&lt;/p&gt;

&lt;h4&gt;
  
  
  Connecting to your database
&lt;/h4&gt;

&lt;p&gt;Of course, your Salesforce function code will need to include some specific pieces to ensure a proper connection to the database. The simplest approach would be to include &lt;a href="https://www.npmjs.com/package/dotenv" rel="noopener noreferrer"&gt;dotenv&lt;/a&gt; package for specifying the database URL as an environment variable and the &lt;a href="https://www.npmjs.com/package/pg" rel="noopener noreferrer"&gt;pg&lt;/a&gt; package as a Postgres client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "dotenv/config";
import pg from "pg";
const { Client } = pg;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within your function code, you’ll need to connect to your Postgres database. Here is an example of a connection helper function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function pgConnect() {
  const client = new Client({
    connectionString: process.env.DATABASE_URL,
    ssl: {
      rejectUnauthorized: false
    }
  });
  await client.connect();
  return client;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This assumes that you have a &lt;code&gt;.env&lt;/code&gt; file that specifies your &lt;code&gt;DATABASE_URL&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Query and store
&lt;/h4&gt;

&lt;p&gt;In the following example function, we retrieve some data from our Salesforce Org, and then we store it in Postgres.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default async function (event, context, logger) {
  // Send request to Salesforce data API to retrieve data
  const sfid = await context.org.dataApi.query(
    `SELECT Id FROM Account WHERE Name = 'Example Company'`
  );

  // Connect to postgres
  const pg = await pgConnect();

  // Store data result in postgres
  client.query(`INSERT INTO companies (sfid) VALUES ($1)`, [sfid]);

  // Close connection
  pg.end()
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test your Salesforce Function locally
&lt;/h3&gt;

&lt;p&gt;To test your Function locally, you first run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sf run function start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, you can invoke the Function with a payload from another terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sf run function -l http://localhost:8080 -p '{"payloadID": "info"}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more information on running Functions locally, see &lt;a href="https://developer.salesforce.com/blogs/2021/11/getting-started-with-salesforce-functions-locally-no-license-required" rel="noopener noreferrer"&gt;this guide&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Associating your Salesforce Function and your Heroku environment
&lt;/h3&gt;

&lt;p&gt;Now that everything is working as expected, let's associate the Function with a compute environment. (For more information about how to create a compute environment and deploy a function, take a look at the &lt;a href="https://developer.salesforce.com/docs/platform/functions/guide/deploy.html" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;You can associate your Salesforce Function and Heroku environments by adding your Heroku user as a collaborator to your function’s compute environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sf env compute collaborator add --heroku-user username@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The environments can now share Heroku data.&lt;/p&gt;

&lt;p&gt;Next, you will need the name of the compute environment so that you can attach the datastore to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sf env list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, you can attach the datastore.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ heroku addons:attach &amp;lt;your-heroku-postgres-database&amp;gt; --app &amp;lt;your-compute-environment-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here are some additional resources that may be helpful for you as you begin implementing your Salesforce Function and accessing Heroku Postgres:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/trailheadapps/functions-recipes/tree/main/functions/03_Context_UnitOfWork_JS" rel="noopener noreferrer"&gt;JavaScript Unit of Work example&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://devcenter.heroku.com/articles/connecting-heroku-postgres#connecting-in-node-js" rel="noopener noreferrer"&gt;Connecting to Heroku Postgres from JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, you’re off to the races!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Salesforce Functions open up many possibilities for working within your Salesforce app to access and manipulate Heroku data. In this first part of our series, we’ve touched on using Salesforce Functions to work with Heroku Postgres. In our next part of the series, we’ll integrate Salesforce Functions with Heroku Data for Redis. Then, in our final post for this series, we’ll integrate with Apache Kafka on Heroku. Stay tuned!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>github</category>
      <category>programming</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Light DOM and Lightning Web Components in Salesforce</title>
      <dc:creator>Michael Bogan</dc:creator>
      <pubDate>Mon, 12 Dec 2022 17:59:17 +0000</pubDate>
      <link>https://forem.com/salesforcedevs/light-dom-and-lightning-web-components-in-salesforce-4d66</link>
      <guid>https://forem.com/salesforcedevs/light-dom-and-lightning-web-components-in-salesforce-4d66</guid>
      <description>&lt;p&gt;&lt;a href="https://lwc.dev/" rel="noopener noreferrer"&gt;Lightning Web Components&lt;/a&gt; (LWC) from Salesforce are based on standard &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components" rel="noopener noreferrer"&gt;Web Components&lt;/a&gt; built using HTML and JavaScript. They are lightweight, easy to build, and perform well in modern browsers. When building LWCs, you’ll become familiar with the concept of &lt;a href="https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.create_components_compose_intro" rel="noopener noreferrer"&gt;composition&lt;/a&gt;: piecing together simple building-block components within the body of a more complex component.&lt;/p&gt;

&lt;p&gt;Regarding composition, LWC leverages the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM" rel="noopener noreferrer"&gt;Shadow Document Object Model&lt;/a&gt; (DOM) web standard, which encapsulates the internal structure of a Web Component and makes it inaccessible to code and components outside of the component. The alternative to this approach is &lt;a href="https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.create_light_dom" rel="noopener noreferrer"&gt;Light DOM&lt;/a&gt;, which Salesforce makes available in beta.&lt;/p&gt;

&lt;p&gt;In this post, we’ll explore in detail the concept of Light DOM in Salesforce development. We’ll begin with an overview of the DOM concept, followed by the differences between Shadow DOM and Light DOM. Finally, we’ll look at how this plays out in LWC, along with how to implement Light DOM in LWC.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the DOM?
&lt;/h2&gt;

&lt;p&gt;Taking the definition from &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction" rel="noopener noreferrer"&gt;MDN&lt;/a&gt;, the DOM is &lt;em&gt;“the data representation of the objects that comprise the structure and content of a document on the web&lt;/em&gt;.” That means: The DOM provides a way to represent a web page in a tree-like structure as nodes and objects. This makes it easier for developers to manipulate and build out any logic as required.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9h2b4kbu9ygwwat0e7vl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9h2b4kbu9ygwwat0e7vl.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.w3schools.com/whatis/whatis_htmldom.asp" rel="noopener noreferrer"&gt;Image Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s important to note that the DOM is independent of any programming language. A web application built using any language or framework would typically provide access to the DOM and its components in one way or another. &lt;/p&gt;

&lt;h3&gt;
  
  
  The Shadow DOM
&lt;/h3&gt;

&lt;p&gt;Perhaps you’ve come across the “Shadow DOM”, which is basically an extension of the DOM that allows hidden DOM trees to be attached as elements in the regular DOM tree. MDN uses this graphic to represent the Shadow DOM:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo5qtbk3uz2kq6sf2d14p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo5qtbk3uz2kq6sf2d14p.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM" rel="noopener noreferrer"&gt;Image Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The intent of the Shadow DOM is to provide encapsulation for an individual component in the DOM, keeping the markup structure, style, and behavior of that component hidden and separate from other code on the page. Developers can share that component as a full DOM tree (“shadow tree”), ensuring that no outside code can manipulate that tree. This helps keep the code well segregated and avoids clashing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Light DOM versus Shadow DOM
&lt;/h3&gt;

&lt;p&gt;The term “Light DOM” is just another name used for the normal or regular DOM, to distinguish it from the Shadow DOM. The “Shadow Boundary” separates the Light DOM from the Shadow DOM. LWC, just like any Web Component, enforces the &lt;a href="https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.create_dom" rel="noopener noreferrer"&gt;Shadow DOM&lt;/a&gt; on every component. By using the &lt;a href="https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.create_light_dom" rel="noopener noreferrer"&gt;Light DOM&lt;/a&gt; approach, which is currently a feature in beta Service and generally available only for Salesforce Experience Builder sites, components are attached to the host element instead of its shadow tree.&lt;/p&gt;

&lt;p&gt;Let’s look at what this means in greater detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shadow DOM in LWC
&lt;/h2&gt;

&lt;p&gt;Because the Shadow DOM web standard is not implemented in all browsers that Salesforce supports, LWC uses a Shadow DOM polyfill named Synthetic Shadow DOM. The following markup shows a sample shadow tree structure:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;c-my-app&amp;gt;
  #shadow-root
    &amp;lt;div&amp;gt;
        &amp;lt;p&amp;gt;My Tasks&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;c-my-item&amp;gt;
      #shadow-root
        &amp;lt;div&amp;gt;
            &amp;lt;p&amp;gt;Item 1&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/c-my-item&amp;gt;
&amp;lt;/c-my-app&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Shadow DOM prevents us from extracting DOM elements by using references to &lt;code&gt;document&lt;/code&gt; or &lt;code&gt;document.body&lt;/code&gt;. Instead, an LWC component needs to access the tree by using &lt;code&gt;this.template.querySelector()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;CSS is also affected by Shadow DOM encapsulation. Style from outside of the component can only affect the top level of the component and cannot affect parent, child, or sibling CSS. &lt;/p&gt;

&lt;p&gt;The LWC architecture leverages Lightning Locker Service to enforce DOM access containment. Components are segregated from one another by using namespaces, and each component can only traverse its DOM and access elements in the same namespace. This is a significant limitation (albeit intentional), making the component inaccessible to programmatic code. This measure prevents script injections and style leaks. Light DOM allows developers to circumvent this security restriction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Light DOM in LWC
&lt;/h2&gt;

&lt;p&gt;To lift DOM access restrictions imposed by the Shadow DOM, Salesforce has introduced the concept of working with Light DOM in LWC. Even though the feature is currently in beta, it’s clearly the way forward for DOM manipulation for LWCs.&lt;/p&gt;

&lt;p&gt;The idea is this: &lt;strong&gt;Since the LWC resides outside the Shadow DOM, none of the Shadow DOM limitations apply to the component.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Below is a brief comparison of how Shadow DOM and Light DOM impact an LWC.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tr&gt;
   &lt;td&gt;
&lt;strong&gt;Feature&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;
&lt;strong&gt;Light DOM&lt;/strong&gt;
   &lt;/td&gt;
   &lt;td&gt;
&lt;strong&gt;Shadow DOM&lt;/strong&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Global CSS Theming with a master style sheet
   &lt;/td&gt;
   &lt;td&gt;Full Support
   &lt;/td&gt;
   &lt;td&gt;No Support, CSS is defined at component level and does not cascade.
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Third-Party Tools
   &lt;/td&gt;
   &lt;td&gt;Full Support for DOM traversal
   &lt;/td&gt;
   &lt;td&gt;Limited support as third-party tools can only access their parent component. 
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Programmatic DOM Access
   &lt;/td&gt;
   &lt;td&gt;Not limited by namespace
   &lt;/td&gt;
   &lt;td&gt;Limited by namespace
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Portability
   &lt;/td&gt;
   &lt;td&gt;Portable but cause breaking changes
   &lt;/td&gt;
   &lt;td&gt;Portable with secure access
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Highly Customizable UI
   &lt;/td&gt;
   &lt;td&gt;Good fit
   &lt;/td&gt;
   &lt;td&gt;Not a good fit
   &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Light DOM in LWC: An Example
&lt;/h2&gt;

&lt;p&gt;Let’s look at a simple example of how Light DOM works with an LWC component. This is a simple template directive for a MyApp LWC that uses the Shadow DOM by default.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;template&amp;gt;
    &amp;lt;my-header&amp;gt;
        &amp;lt;p&amp;gt;My Tasks&amp;lt;/p&amp;gt;
    &amp;lt;/my-header&amp;gt;
&amp;lt;/template&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To enable light DOM, we simply need to set the &lt;code&gt;renderMode&lt;/code&gt; property as follows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import { LightningElement } from 'lwc';

export default class MyApp extends LightningElement {
    static renderMode = 'light';
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Next, we change our template directive to use Light DOM instead of Shadow DOM:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;&amp;lt;template lwc:render-mode='light'&amp;gt;&lt;br&gt;
    &amp;lt;my-header&amp;gt;&lt;br&gt;
        &amp;lt;p&amp;gt;My Tasks&amp;lt;/p&amp;gt;&lt;br&gt;
    &amp;lt;/my-header&amp;gt;&lt;br&gt;
&amp;lt;/template&amp;gt;&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Accessing Elements using Light DOM in LWC&lt;br&gt;
&lt;/h3&gt;

&lt;p&gt;Elements can be accessed in Light DOM using the &lt;code&gt;document.querySelector()&lt;/code&gt; method. This is a powerful way for developers to access and manipulate elements as needed.&lt;/p&gt;

&lt;p&gt;In Light DOM, we would use &lt;code&gt;this.querySelector&lt;/code&gt;. In Shadow DOM, we would need to use &lt;code&gt;this.template.QuerySelector&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Event Propagation using Light DOM in LWC
&lt;/h3&gt;

&lt;p&gt;When it comes to event propagation, Light DOM is a big uplift for developers. Shadow DOM retargets any events which cross the shadow boundary. There is no such event retargeting in Light DOM, and it’s easy for developers to identify the exact UI element that triggered an event instead of getting a reference to the underlying component.&lt;/p&gt;

&lt;h3&gt;
  
  
  Guidelines for using Light DOM in LWC
&lt;/h3&gt;

&lt;p&gt;Salesforce has provided a set of guidelines and limitations for developers using Light DOM, which you can find &lt;a href="https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.create_light_dom" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;LWC provides a simple and powerful approach to crafting complex web components. Up to this point, while Salesforce has used the Shadow DOM standard in LWC for the purposes of encapsulation and secure access, developers may encounter many scenarios in which the Light DOM approach is fitting and appropriate. With Light DOM now offered in beta, Salesforce developers can now make the choice for themselves based on their use case.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>salesforce</category>
      <category>opensource</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Scaling Your Compute Resources on Salesforce</title>
      <dc:creator>Michael Bogan</dc:creator>
      <pubDate>Mon, 05 Dec 2022 13:43:21 +0000</pubDate>
      <link>https://forem.com/salesforcedevs/scaling-your-compute-resources-on-salesforce-59jh</link>
      <guid>https://forem.com/salesforcedevs/scaling-your-compute-resources-on-salesforce-59jh</guid>
      <description>&lt;p&gt;The Salesforce development platform offers many powerful features to provide your team with apps that can unlock new workflows. For many years, the platform has run on a trifecta of technologies: &lt;a href="https://developer.salesforce.com/docs/atlas.en-us.pages.meta/pages/pages_intro_what_is_it.htm" rel="noopener noreferrer"&gt;Visualforce&lt;/a&gt; (to view the data), &lt;a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_intro_what_is_apex.htm" rel="noopener noreferrer"&gt;Apex&lt;/a&gt; (to handle the data), and Salesforce itself (to store the data).&lt;/p&gt;

&lt;p&gt;Apex is like many other object-oriented languages, and it has a Java-like syntax. However, &lt;strong&gt;Apex runs directly on Salesforce servers&lt;/strong&gt;, allowing developers to build and deploy an app without worrying about where to host it or how to secure their data.&lt;/p&gt;

&lt;h3&gt;
  
  
  The challenge of Apex and long-running commands
&lt;/h3&gt;

&lt;p&gt;Applications built on Salesforce run on its &lt;a href="https://architect.salesforce.com/fundamentals/platform-multitenant-architecture" rel="noopener noreferrer"&gt;multitenant architecture&lt;/a&gt;, so &lt;strong&gt;server resources like CPU and memory are shared among different organizations&lt;/strong&gt;. With shared resources, however, wouldn’t it be possible for the code for one org’s app to consume so much processor time and memory space that it affects other users in that same org? An example of this could be a command that takes several minutes to complete.&lt;/p&gt;

&lt;p&gt;Salesforce prevents situations like this from occurring by restricting how apps on its platform run. &lt;strong&gt;If any app has the potential to interfere with other users, it is restricted from completing its command execution.&lt;/strong&gt; Long-running programs can potentially interrupt other work by hogging the limited resources available.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If so, how might Salesforce developers build apps that execute long-running commands which can scale with user growth while not impacting performance?&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Salesforce Functions to the rescue
&lt;/h3&gt;

&lt;p&gt;Enter &lt;a href="https://developer.salesforce.com/docs/platform/functions/overview" rel="noopener noreferrer"&gt;Salesforce Functions&lt;/a&gt;, which are small programs &lt;strong&gt;written in JavaScript, TypeScript, or Java&lt;/strong&gt; and deployed onto Salesforce’s servers. Functions can perform the resource-intensive work and be invoked directly from Apex. When the Salesforce Function completes the request, it can send its results back to the originating Apex application, which then performs any additional tasks on those results.&lt;/p&gt;

&lt;p&gt;Salesforce Functions &lt;strong&gt;run in their own container&lt;/strong&gt; and &lt;strong&gt;don’t affect other tenants&lt;/strong&gt; on the Salesforce platform. The &lt;strong&gt;memory and CPU limits are much higher&lt;/strong&gt;, allowing you to execute longer running and more complex tasks. Because Salesforce Functions run on the Salesforce platform, security is baked in; there’s &lt;strong&gt;no risk of privacy loss&lt;/strong&gt; as everything runs within the same trust boundary.&lt;/p&gt;

&lt;p&gt;In this article, we’ll take a closer look at how to migrate a long-running Apex operation into a Salesforce Function, along with some general tips around the Salesforce DX suite.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;This article will require you to have some understanding of both Apex and TypeScript, but don’t worry if you’re not an expert. We’ll go through every piece of code to explain what is happening.&lt;/p&gt;

&lt;p&gt;Although the completed version of our code is available as &lt;a href="https://github.com/gjtorikian/ApexToSalesforceFunctionsMigration" rel="noopener noreferrer"&gt;a GitHub repository&lt;/a&gt;, you may also want to download some of the CLI tools which Salesforce provides to follow along (or for your own future projects).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First, install &lt;a href="https://developer.salesforce.com/tools/sfdxcli" rel="noopener noreferrer"&gt;the sfdx CLI&lt;/a&gt;, which is a tool that helps simplify common operations for Salesforce developers when building applications.&lt;/li&gt;
&lt;li&gt;It’s important that whatever you build and deploy doesn’t affect your “real” production Salesforce organization. So, create a separate &lt;a href="https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_scratch_orgs_create.htm" rel="noopener noreferrer"&gt;scratch org&lt;/a&gt; that this app can interact with. To get your own scratch org, sign up for &lt;a href="https://developer.salesforce.com/signup" rel="noopener noreferrer"&gt;a free Developer Edition&lt;/a&gt; account.&lt;/li&gt;
&lt;li&gt;To use Salesforce Functions, you’ll need to &lt;a href="https://developer.salesforce.com/docs/platform/functions/guide/index.html" rel="noopener noreferrer"&gt;register for a Salesforce Functions trial&lt;/a&gt; by contacting your Salesforce account executive.&lt;/li&gt;
&lt;li&gt;Finally, you can also install &lt;a href="https://developer.salesforce.com/tools/vscode" rel="noopener noreferrer"&gt;the Salesforce VS Code extension&lt;/a&gt; to make deploying your code a little bit easier. This step is optional.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Touring the existing code
&lt;/h2&gt;

&lt;p&gt;Open up the file at &lt;code&gt;force-app/main/default/classes/Dogshow.cls&lt;/code&gt; and take a look at its contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Dogshow {
   public static void updateAccounts() {
       // Get the 5 oldest accounts
       Account[] accounts = [SELECT Id, Description FROM Account ORDER BY CreatedDate ASC LIMIT 5];

       // Set HTTP request
       HttpRequest req = new HttpRequest();
       req.setEndpoint('https://dog.ceo/api/breeds/image/random');
       req.setMethod('GET');

       // Create a new HTTP object to send the request object
       Http http = new Http();
       HTTPResponse res = http.send(req);

       String body = res.getBody();

       Map&amp;lt;String, String&amp;gt; m = (Map&amp;lt;String, String&amp;gt;) JSON.deserialize(jsonStr, Map&amp;lt;String, String&amp;gt;.class);
       String dogUrl = m.get('message');

       // loop through accounts and update the Description field
       for (Account acct : oldAccounts) {
           acct.Description += ' And their favorite dog can be found at ' + dogUrl;
       }

       // save the change you made
       update accounts;
     }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These 30 lines perform a fairly simple function:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It grabs the five oldest Salesforce accounts using SOQL.&lt;/li&gt;
&lt;li&gt;It issues an HTTP request to &lt;a href="https://dog.ceo/dog-api/" rel="noopener noreferrer"&gt;https://dog.ceo/dog-api/&lt;/a&gt;, which is a site that returns a JSON object with a random URL link to a very cute dog.&lt;/li&gt;
&lt;li&gt;The Apex code then updates the description for those five accounts to indicate that &lt;em&gt;that&lt;/em&gt; dog is their favorite.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This code is fairly innocuous, but it introduces two situations that present some real problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When issuing an HTTP request, several factors can affect your program. For example, the server you issue requests to could be overloaded and slow to respond. While your own code may be performant, you’re at the mercy of anything outside of your control, such as an external data source. &lt;/li&gt;
&lt;li&gt;Although we only iterate on five accounts here, what if we built an app that needed to iterate over 500 or 5,000? Looping through them one by one would be a slow, albeit unavoidable, process.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is an opportunity for a Salesforce Function to take over some of this work. Our Apex class can provide a list of accounts to update to a Salesforce Function. That Salesforce Function can then issue HTTP requests and update the accounts.&lt;/p&gt;

&lt;p&gt;Before seeing what that full migration might look like, let’s first write that Salesforce Function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Developing the Salesforce Function
&lt;/h2&gt;

&lt;p&gt;Using the sfdx CLI, we can easily &lt;a href="https://developer.salesforce.com/docs/platform/functions/guide/create-function.html" rel="noopener noreferrer"&gt;create a new Salesforce Function in TypeScript&lt;/a&gt; using a single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sf generate function -n dogshowfunction -l typescript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a set of files under &lt;code&gt;functions/dogshowfunction&lt;/code&gt;. The most important of these is &lt;code&gt;index.ts&lt;/code&gt;, which is where our main Salesforce Function code will reside. However, the other files are also important, dealing with testing and linting code, as well as defining TypeScript generation and the Salesforce deployment process. &lt;/p&gt;

&lt;p&gt;Let’s focus on &lt;code&gt;index.ts&lt;/code&gt;. In this file, you’ll note that there’s one function exported, and it takes three parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;event&lt;/code&gt;: This describes the payload of data that is coming in from your Apex code.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;context&lt;/code&gt;: This contains the authorization logic necessary to communicate with Salesforce.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;logger&lt;/code&gt;: This is a simple logger that also &lt;a href="https://developer.salesforce.com/docs/platform/functions/guide/function-logging.html" rel="noopener noreferrer"&gt;integrates with Salesforce&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The template code which the CLI generates shows how powerful Salesforce Functions are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They can accept any JSON data sent over from Apex.&lt;/li&gt;
&lt;li&gt;They can work directly with Salesforce data.&lt;/li&gt;
&lt;li&gt;They integrate directly with the platform in such a way as to handle all the authentication and security for you. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Best of all, since this particular function runs on Node.js, you can &lt;a href="https://www.npmjs.com" rel="noopener noreferrer"&gt;install and use any NPM package&lt;/a&gt; to supplement your code. Let’s do that right now by installing &lt;a href="https://github.com/node-fetch/node-fetch" rel="noopener noreferrer"&gt;node-fetch&lt;/a&gt; to issue our HTTP request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm i node-fetch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our Salesforce Function will be responsible for issuing our HTTP requests and updating our five accounts. To implement that functionality, our function might look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default async function execute(event: InvocationEvent&amp;lt;any&amp;gt;, context: Context, logger: Logger): Promise&amp;lt;RecordQueryResult&amp;gt; {
   const accounts = event.data.accounts;

   accounts.forEach(async (account) =&amp;gt; {
       const response = await fetch('https://dog.ceo/api/breeds/image/random');
       const data = await response.json();
       const message = ` And their favorite dog is ${data.message}`

       const recordForUpdate = {
           type: 'Account',
           fields: {
             id: account.id,
             Description: `${account.Description} ${message}`
           }
         }
       context.org.dataApi.updateRecord(recordForUpdate);
   });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s break down what’s going on in the code snippet above.&lt;/p&gt;

&lt;p&gt;Our event argument is essentially a JSON object which we will define in our Apex code. Although this JSON object doesn’t exist yet, we can assume that it will have the &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;Description&lt;/code&gt; of the account we want to update, based on the behavior of our previous Apex code.&lt;/p&gt;

&lt;p&gt;From here, it’s important to note that the context argument is essentially &lt;a href="https://github.com/forcedotcom/sf-fx-sdk-nodejs" rel="noopener noreferrer"&gt;an instantiation of the Node.js SDK for Salesforce Functions&lt;/a&gt;. Since we can assume that this account data is provided as an array, we can simply iterate through each item, plugging in the record data as part of our update command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrate the Apex Code
&lt;/h2&gt;

&lt;p&gt;With our Salesforce Function defined, we can now replace the previous Apex logic with a call to this new function. Issuing a call to a Salesforce Function is surprisingly simple: We only need to know the name of our function and provide the data we want to send it, as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Dogshow {
   public static void updateAccounts() {
       // Get the 5 oldest accounts
       Account[] accounts = [SELECT Id, Description FROM Account ORDER BY CreatedDate ASC LIMIT 5];

       // Set up the function
       functions.Function dogshowFunction = functions.Function.get('ApexToSalesforceFunctionsMigration.dogshowfunction');

       // Create a list to hold the record data
       List&amp;lt;Map&amp;lt;String, String&amp;gt;&amp;gt; jsonObj = new List&amp;lt;Map&amp;lt;String, String&amp;gt;&amp;gt;();
       for (Account account : accounts) {
           Map&amp;lt;String, Object&amp;gt; obj = new Map&amp;lt;String, Object&amp;gt;();

           obj.put('id', account.Id);
           obj.put('Description', account.Description);

           // Add an object to the record list
           jsonObj.add(obj);
       }
       // Send the record list to the function
       functions.FunctionInvocation invocation = dogshowFunction.invoke(JSON.Serialize(jsonObj));

       // if the function had a return value, it would be available here
       invocation.getResponse();
     }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just like that, we’ve taken the long-running operation out of our Apex code and offloaded it to a Salesforce Function. Now, we can be certain that our operation will continue to run even though it uses resources more heavily.&lt;/p&gt;

&lt;p&gt;The implications of Salesforce Functions cannot be overstated. With Salesforce Functions handling our need for time-consuming, resource-heavy operations, apps built on the Salesforce platform now have much more potential for scaling up seamlessly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Salesforce Functions provide a way to improve workflows while building effective and durable projects. Without the memory and CPU limits you would experience in Apex, you can leverage Salesforce Functions for the execution of long-running commands, bringing scalability without a negative impact on performance.&lt;/p&gt;

&lt;p&gt;We’ve only begun to discuss the amazing capabilities provided by Salesforce Functions. To learn more about them, Salesforce Functions provides plenty of &lt;a href="https://developer.salesforce.com/docs/platform/functions/guide/solutions_and_samples.html" rel="noopener noreferrer"&gt;guides detailing specific tasks&lt;/a&gt;, along with various &lt;a href="https://github.com/trailheadapps/functions-recipes/" rel="noopener noreferrer"&gt;Trailhead recipes&lt;/a&gt; that are worth looking into.&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>Salesforce Functions for Caching Expensive Queries</title>
      <dc:creator>Michael Bogan</dc:creator>
      <pubDate>Mon, 14 Nov 2022 13:40:02 +0000</pubDate>
      <link>https://forem.com/salesforcedevs/salesforce-functions-for-caching-expensive-queries-2i0j</link>
      <guid>https://forem.com/salesforcedevs/salesforce-functions-for-caching-expensive-queries-2i0j</guid>
      <description>&lt;p&gt;Caching is a strategy that can help you conserve resources and improve performance. When you have an oft-run expensive query with a result that seldom changes, caching is an ideal solution. By caching the result of that query, you can return the cached result when necessary. The result is the same, but you save the need to run the expensive query. Everybody wins.&lt;/p&gt;

&lt;p&gt;In this article, we will walk through the use of Salesforce Functions to cache expensive queries. For instance, we want to query for some value across a large number of records, and the page requiring this query is often loaded. However, the result will not change from one query execution to the next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to the Problem
&lt;/h2&gt;

&lt;p&gt;In our demo, the example query will be “&lt;em&gt;the number of companies with more than 10,000 employees&lt;/em&gt;.” We imagine a use case in which there’s a page that our sales team loads often, and part of the page shows the total number of companies in our Salesforce org that match this condition. Ideally, the expensive query is not run every time the page loads, but instead we would implement a caching mechanism.&lt;/p&gt;

&lt;p&gt;To solve this, we will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Leverage &lt;a href="https://www.heroku.com/connect" rel="noopener noreferrer"&gt;Heroku Connect&lt;/a&gt; to sync our list of companies from Salesforce to &lt;a href="https://www.heroku.com/postgres" rel="noopener noreferrer"&gt;Heroku Postgres&lt;/a&gt; (or use a table already in Postgres).&lt;/li&gt;
&lt;li&gt;Create a &lt;a href="https://developer.salesforce.com/docs/platform/functions/overview" rel="noopener noreferrer"&gt;Salesforce Function&lt;/a&gt; to query Postgres and return that count.&lt;/li&gt;
&lt;li&gt;Store the resulting value in &lt;a href="https://www.heroku.com/redis" rel="noopener noreferrer"&gt;Heroku Redis&lt;/a&gt; for a specified amount of time.&lt;/li&gt;
&lt;li&gt;Use a Salesforce Function to check for the value in Redis. If the value exists in Redis, then return it. If not, then run the query and store the result in Redis.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The data flow looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F45o7qx2a9q3niunuwd3i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F45o7qx2a9q3niunuwd3i.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, Apex does have a &lt;a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_cache_namespace_overview.htm" rel="noopener noreferrer"&gt;Platform Cache API&lt;/a&gt; for facilitating caching for certain use cases. For our use case—and for demo purposes—we’ll explore this caching solution that uses Salesforce Functions.&lt;/p&gt;

&lt;p&gt;Before we proceed, let’s briefly introduce each of the pieces in our system.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developer.salesforce.com/docs/platform/functions/overview" rel="noopener noreferrer"&gt;Salesforce Function&lt;/a&gt;: A feature of Salesforce that allows you to run some code (JavaScript or Java are currently the supported languages) that is still in the secure area of your Salesforce org, but is &lt;em&gt;not running on your org&lt;/em&gt;. This allows you to offload workloads that are heavy or may cause you to exceed limits.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.heroku.com/connect" rel="noopener noreferrer"&gt;Heroku Connect&lt;/a&gt;: A tool within the Salesforce family for bidirectional data syncing between your Salesforce org and &lt;a href="https://www.heroku.com/postgres" rel="noopener noreferrer"&gt;Heroku Postgres&lt;/a&gt;. Similar to Salesforce Functions, you can leverage this tool without impacting your Salesforce limits. &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.heroku.com/postgres" rel="noopener noreferrer"&gt;Heroku Postgres&lt;/a&gt;: A fully managed instance of &lt;a href="https://www.postgresql.org/" rel="noopener noreferrer"&gt;PostgreSQL&lt;/a&gt; (a relational database) on Heroku.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.heroku.com/redis" rel="noopener noreferrer"&gt;Heroku Redis&lt;/a&gt;: A fully managed instance of &lt;a href="https://redis.io/" rel="noopener noreferrer"&gt;Redis&lt;/a&gt; (an in-memory key-value store) on Heroku.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;To use all of the above components, you must have the following pieces in place:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://signup.heroku.com/" rel="noopener noreferrer"&gt;Heroku account&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;A &lt;a href="https://devcenter.heroku.com/articles/creating-apps" rel="noopener noreferrer"&gt;Heroku app&lt;/a&gt; created, with &lt;a href="https://devcenter.heroku.com/articles/heroku-postgresql#provisioning-heroku-postgres" rel="noopener noreferrer"&gt;Postgres&lt;/a&gt; and &lt;a href="https://devcenter.heroku.com/articles/heroku-redis#create-an-instance" rel="noopener noreferrer"&gt;Redis&lt;/a&gt; add-ons attached&lt;/li&gt;
&lt;li&gt;A Salesforce org with &lt;a href="https://developer.salesforce.com/docs/platform/functions/guide/index.html" rel="noopener noreferrer"&gt;Functions enabled&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A local Salesforce Functions &lt;a href="https://developer.salesforce.com/docs/platform/functions/guide/set-up.html" rel="noopener noreferrer"&gt;development environment&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[Optional] &lt;a href="https://devcenter.heroku.com/articles/quick-start-heroku-connect-cli" rel="noopener noreferrer"&gt;Heroku Connect&lt;/a&gt; syncing to Postgres (a sample dataset is provided)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the prerequisites in place, we can start connecting them. First, we’ll walk through connecting the environments. Then, we’ll look at the code needed to make the magic happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessing Heroku Data Services from a Salesforce Function
&lt;/h2&gt;

&lt;p&gt;With your accounts and access in place, we can move forward with the Function itself. Let’s assume that you are starting a fresh project and have an empty Postgres database.&lt;/p&gt;

&lt;p&gt;If you are new to Functions, then we recommend going through this basic process to get a feel for things before you involve additional parts. If you already have a Salesforce project or are syncing the data via Heroku Connect, then you can modify the following commands to suit your needs. &lt;/p&gt;

&lt;p&gt;First, create a Salesforce DX project to give your function a home.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sfdx force:project:create -n MyDataProj
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, navigate to the project directory and run the following command to create a fresh JavaScript function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sf generate function -n yourfunction -l javascript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a &lt;code&gt;/functions&lt;/code&gt; folder with a Node.js application template.&lt;/p&gt;

&lt;p&gt;Next, associate your Salesforce Function and your Heroku environments by adding your Heroku user as a collaborator to your Function’s compute environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sf env compute collaborator add --heroku-user &amp;lt;yourherokuaccount@email.com&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The environments can now share Heroku data services.&lt;/p&gt;

&lt;p&gt;Next, you will need to obtain the name of the compute environment so that you can attach the datastores to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sf env list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To attach the Heroku datastores, you also need the names of the add-ons. You can get the name of the add-ons with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku addons -a &amp;lt;yourherokuapp&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output will look similar to the following. The “name” of each add-on is shown in purple (for example, &lt;code&gt;postgresql-closed-46065&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxfa96onnwqm26j9tnhas.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxfa96onnwqm26j9tnhas.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the names of the compute environment and your add-ons, run the following commands to attach Postgres and Redis to your compute environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku addons:attach &amp;lt;your-heroku-postgres-name&amp;gt; --app &amp;lt;your-compute-environment-name&amp;gt;

heroku addons:attach &amp;lt;your-heroku-redis-name&amp;gt; --app &amp;lt;your-compute-environment-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have connected our Salesforce Function environment with our Heroku datastores, we can write our Salesforce Function code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing the Salesforce Function
&lt;/h2&gt;

&lt;p&gt;Before we begin writing the JavaScript for this Salesforce Function, let's set up our local development environment with the necessary libraries. In the deployed environment, our Function will get the data connection information from environment variables. For our local environment, we will use the &lt;a href="https://www.npmjs.com/package/dotenv" rel="noopener noreferrer"&gt;dotenv&lt;/a&gt; node package to read a file named &lt;code&gt;.env&lt;/code&gt; with this information. We can create that file with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku config -a &amp;lt;yourherokuapp&amp;gt; --shell &amp;gt; .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let’s install the packages we need to interact with Postgres and Redis, along with dotenv:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install dotenv pg redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our project setup is done. Let’s write our function code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect to Heroku Postgres and Redis
&lt;/h3&gt;

&lt;p&gt;Let’s start by adding the code that allows us to read and store data in Heroku Postgres and Heroku Redis. (Note: The &lt;a href="https://devcenter.heroku.com/" rel="noopener noreferrer"&gt;Heroku Devcenter&lt;/a&gt; has some helpful documentation on connecting to &lt;a href="https://devcenter.heroku.com/articles/connecting-heroku-postgres#connecting-in-node-js" rel="noopener noreferrer"&gt;Postgres&lt;/a&gt; and &lt;a href="https://devcenter.heroku.com/articles/connecting-heroku-redis#connecting-in-node-js" rel="noopener noreferrer"&gt;Redis&lt;/a&gt; from Node.js.)&lt;/p&gt;

&lt;p&gt;Our function code will live in the &lt;code&gt;index.js&lt;/code&gt; file of the &lt;code&gt;functions&lt;/code&gt; folder in our project (for example, &lt;code&gt;MyDataProj/functions/index.js&lt;/code&gt;). We open that file and add the following lines to the top. These lines will import the modules we just installed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "dotenv/config";
import pg from "pg";
const { Client } = pg;
import { createClient } from 'redis';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main part of the function is the section that is being exported. The value returned from this block will be returned to the caller of the function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default async function (event, context, logger) {

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To keep our code clean and modular, let’s first write several helper functions at the bottom of the file. We need functions that we can call to manage our connection to Postgres and Redis. Under (and outside of) the exported function, we add the following two helper functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* Helper functions */

// Connect to PostgreSQL
async function pgConnect() {
   const DATABASE_URL = process.env.DATABASE_URL;
   if (!DATABASE_URL) {
     throw new Error("DATABASE_URL is not set");
   }
    const client = new Client({
     connectionString: DATABASE_URL,
     ssl: {
       rejectUnauthorized: false
     }
   });
    await client.connect();
   return client;
 }

// Connect to Redis
async function redisConnect() {
   const REDIS_URL = process.env.REDIS_URL;
   if (!REDIS_URL) {
     throw new Error("REDIS_URL is not set");
   }
    const redis = createClient({
       url: process.env.REDIS_URL,
       socket: {
           tls: true,
           rejectUnauthorized: false
       }
       });
    await redis.connect();
   redis.on('error', err =&amp;gt; {
       console.log('Error ' + err);
   });
   return redis;
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Load in a sample dataset
&lt;/h3&gt;

&lt;p&gt;To keep our example simple, let’s load a small dataset into Postgres. We can create a table called “company” by running the database commands found in the following &lt;a href="https://gist.github.com/alvinslee/3a7233eab951ec269f3d3fdf9d1e2283" rel="noopener noreferrer"&gt;gist&lt;/a&gt;. Download the contents of that gist to a file called &lt;code&gt;company.sql&lt;/code&gt;. To run the database commands from the Heroku CLI, do the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;heroku pg:psql -a &amp;lt;yourherokuapp&amp;gt;

DATABASE=&amp;gt; \i /path/to/company.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can verify that your sample dataset has been loaded by running the following query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE=&amp;gt; select * from company;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Write the main function
&lt;/h3&gt;

&lt;p&gt;We’re all set up! Now, we just have a little bit of code to write for our actual function. The code for our function is available as a &lt;a href="https://gist.github.com/alvinslee/62cb4ad3390740ed8a15e2da11d0d020" rel="noopener noreferrer"&gt;gist&lt;/a&gt; and looks like this. You can copy this into your &lt;code&gt;index.js&lt;/code&gt; file. We’ll step through and explain each section of the code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default async function (event, context, logger) {
   logger.info(`Invoking Datafunction with payload ${JSON.stringify(event.data || {})}`);

   const redis = await redisConnect();
   let cached = {};

   // Check Redis for cached entry first
   let big_biz_count = await redis.get(`big_biz`);

    if (big_biz_count) {
       // If cached entry found, return it and be done.
       logger.info(`Found cache entry = ${big_biz_count}`);
       cached = "true"
       redis.quit();

       return { big_biz_count, cached }
   }  else {
      // If cached entry not found, then:
      // 1. Run the Postgres query
      // 2. Store the result in Redis
      // 3. Return the result and be done
       logger.info(`did not find in cache, returned ${big_biz_count}`);
       cached = "false"
       const pg = await pgConnect();
       const { rows } = await pg.query('SELECT COUNT(*) FROM company WHERE employees&amp;gt;10000;');
       big_biz_count = rows[0].count.toString();

       redis.set(`big_biz`, big_biz_count, {
         EX: 30, // seconds to keep before expiring
         NX: true
       });

       // Close the connections   
       redis.quit();
       pg.end();

       // Return the value from Postgres, now stored in Redis
       return { big_biz_count, cached }
   }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  An explanation of the code
&lt;/h3&gt;

&lt;p&gt;As mentioned at the beginning of this article, we want to find out how many companies have more than 10,000 employees and return that number. We want to cache the number because it is an “expensive query”. In our example, the table is small, so it is not that expensive. However, it represents an “expensive query” that we may want to run in real life. You get the idea. &lt;/p&gt;

&lt;p&gt;Let’s walk through the main sections of our function code.&lt;/p&gt;

&lt;p&gt;1) Connect to Redis and see if the value is there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  const redis = await redisConnect();
  let cached = {};
  let big_biz_count = await redis.get(`big_biz`);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) If the value is there, meaning it has been cached, then we can return the cached value and be done.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  if (big_biz_count) {
    cached = "true"
    redis.quit();
    return { big_biz_count, cached }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) If no cached value was found, then we have no choice but to run the query on our Postgres database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  }  else {
    cached = "false"
    const pg = await pgConnect();
    const { rows } = await pg.query('SELECT COUNT(*) FROM company WHERE employees&amp;gt;10000;');
    big_biz_count = rows[0].count.toString();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4) Then, we store the value returned from our query in Redis.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    redis.set(`big_biz`, big_biz_count, {
      EX: 30, // seconds to keep before expiring
      NX: true
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5) Finally, we close our datastore connections, and we return the query result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    redis.quit();
    pg.end();
    return { big_biz_count, cached }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might prefer to refactor the code a little bit or add some error handling. However, at this most basic level, that’s all there is to it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test the function
&lt;/h3&gt;

&lt;p&gt;Now that we have a Salesforce Function, we can test it locally. First, we start up the function server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sf run function start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we invoke the Function with a payload from another terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sf run function -l http://localhost:8080 -p '{"payloadID": "info"}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you invoke the function against the test database for the first time, you should see the following output because there was no value in the cache:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzth4jn0ov9xtiny866t2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzth4jn0ov9xtiny866t2.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After this first run, however, the value is stored in our Heroku Redis instance. A subsequent run of the Salesforce Function returns the same value, but this time, &lt;code&gt;cached&lt;/code&gt; is true.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwfl8cclj65mv01ayyp4g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwfl8cclj65mv01ayyp4g.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we added the value to Redis, we set the cache expiration to 10 seconds. This makes it easier to test. However, in a real-world environment, the lifespan of your cache values should make sense for your business use case. For example, if the result changes after a nightly run of a report, then you could set the cache value to expire every 24 hours. Better yet, you can create another Salesforce Function that updates the cache with the new value as soon as the report finishes.&lt;/p&gt;

&lt;p&gt;The entire contents of index.js can be downloaded &lt;a href="https://gist.github.com/alvinslee/62cb4ad3390740ed8a15e2da11d0d020" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We did it. Caching is an excellent strategy for reducing resource load while providing faster responses. With Salesforce Functions, we can connect our Salesforce orgs to Heroku datastores (such as Postgres and Redis) and build mechanisms for caching. Salesforce Functions allow us to perform tasks that might ordinarily be load-heavy, cause timeouts, or exceed other limits imposed by Salesforce. Caching is just one use case, but it can yield tremendous benefits, and it’s easy to implement. Now, go have fun with it!&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>database</category>
    </item>
    <item>
      <title>Build a Java Backend that Connects with Salesforce</title>
      <dc:creator>Michael Bogan</dc:creator>
      <pubDate>Wed, 02 Nov 2022 12:50:47 +0000</pubDate>
      <link>https://forem.com/salesforcedevs/build-a-java-backend-that-connects-with-salesforce-3km2</link>
      <guid>https://forem.com/salesforcedevs/build-a-java-backend-that-connects-with-salesforce-3km2</guid>
      <description>&lt;h3&gt;
  
  
  Part One: Java Calling Salesforce
&lt;/h3&gt;

&lt;p&gt;The Salesforce ecosystem is pretty massive. Developers need to work hard to stay up-to-date with the latest and greatest features and tools coming from Salesforce. In this article, we’ll mix a little bit of the &lt;em&gt;old&lt;/em&gt; and the &lt;em&gt;new&lt;/em&gt;. We’ll combine Java-based applications with Salesforce, and we’ll do so via web services.&lt;/p&gt;

&lt;p&gt;Java is a programming language and computing platform first released by Sun Microsystems back in 1995. Since then, we’ve seen the rise of Web 2.0 and web programming languages, containers, microservices, and cloud-native architectures. Yet even now, many new products and digital services designed for the future continue to rely on Java. Java will be around for the foreseeable future.&lt;/p&gt;

&lt;p&gt;Meanwhile, Salesforce has established itself as the world’s top CRM and customer success platform. With today’s enterprises relying on multi- and hybrid-cloud setups with applications that need to integrate seamlessly with one another, it’s not surprising that Java applications and the Salesforce Cloud need to intersect. So how do we get our Java and Salesforce.com (SFDC) applications integrated and talking with one another?&lt;/p&gt;

&lt;p&gt;In this article, we’ll discuss how to implement this integration using the Web Services Connector (WSC). We’ll use an example use case—a business that wants to use its Java application to manipulate Salesforce org data—and show you how to set up your developer environment and connect the pieces.&lt;/p&gt;

&lt;p&gt;However, before we start, let’s talk briefly about Salesforce and its API-first approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Salesforce and the API-first Approach
&lt;/h2&gt;

&lt;p&gt;If you’re familiar with the SFDC ecosystem, then you will know that Salesforce takes an API-first approach when building features on its platform. In fact, Salesforce was &lt;a href="https://www.google.com/url?q=https://blog.postman.com/intro-to-apis-history-of-apis&amp;amp;sa=D&amp;amp;source=docs&amp;amp;ust=1665513405618360&amp;amp;usg=AOvVaw1aHWllLUfwdN90JcxSUynU" rel="noopener noreferrer"&gt;one of the first companies&lt;/a&gt; to deploy web APIs! While the platform has many great built-in capabilities, Salesforce wants to enable its customers to create the capabilities and custom experiences they need for their own platforms. By offering APIs, Salesforce ensures that SFDC can be customized and connected to any external application that can interact with web services. Some of the APIs from Salesforce (and there are &lt;em&gt;&lt;a href="https://developer.salesforce.com/docs/apis" rel="noopener noreferrer"&gt;a lot&lt;/a&gt;&lt;/em&gt; of them) include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_quickstart_intro.htm" rel="noopener noreferrer"&gt;SOAP API&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_rest.htm" rel="noopener noreferrer"&gt;REST API&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.salesforce.com/docs/atlas.en-us.224.0.api_asynch.meta/api_asynch/asynch_api_intro.htm" rel="noopener noreferrer"&gt;Bulk API&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_intro.htm" rel="noopener noreferrer"&gt;Metadata API&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.salesforce.com/docs/atlas.en-us.216.0.chatterapi.meta/chatterapi/intro_what_is_chatter_connect.htm" rel="noopener noreferrer"&gt;Chatter REST API&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.salesforce.com/docs/atlas.en-us.api_streaming.meta/api_streaming/intro_stream.htm" rel="noopener noreferrer"&gt;Streaming API&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.salesforce.com/docs/atlas.en-us.api_tooling.meta/api_tooling/intro_api_tooling.htm" rel="noopener noreferrer"&gt;Tooling API&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Suffice it to say that Salesforce has its platform pretty well-covered by APIs, opening up access to its capabilities for countless different use cases. That being said, let’s dive into our use case. &lt;/p&gt;

&lt;p&gt;In our example use case, we have a Java application that helps a business generate leads for their sales organization. That business would like the Java application to push the qualified leads directly to their Salesforce CRM, avoiding the need for manually entering lead data into Salesforce.&lt;/p&gt;

&lt;p&gt;To integrate Java and Salesforce, we’ll use the Salesforce SOAP API and the &lt;a href="https://github.com/forcedotcom/wsc" rel="noopener noreferrer"&gt;Web Services Connector (WSC)&lt;/a&gt; library, which serves as a wrapper layer that simplifies working with the API in Java.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial Setup
&lt;/h2&gt;

&lt;p&gt;The initial setup of our Java developer environment requires taking several steps. Fortunately, we had guidance from this &lt;a href="https://resources.docs.salesforce.com/latest/latest/en-us/sfdc/pdf/salesforce_developer_environment_tipsheet.pdf" rel="noopener noreferrer"&gt;Salesforce tipsheet&lt;/a&gt;. Nonetheless, we’ll provide an overview of the major steps here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install the Java Developer Kit (JDK)
&lt;/h3&gt;

&lt;p&gt;To use Salesforce APIs in our Java application, we’ll install the Java Developer Kit (JDK) at version 11.0 or later. You can do that by visiting &lt;a href="https://www.oracle.com/java/technologies/downloads/" rel="noopener noreferrer"&gt;this page&lt;/a&gt; and finding the appropriate binary for your local machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install Eclipse
&lt;/h3&gt;

&lt;p&gt;Next, you’ll need a Java development IDE. While there are several options available to you, the steps for our walk-through will go with the Eclipse IDE. You can find the latest Eclipse IDE packages &lt;a href="https://www.eclipse.org/downloads/packages/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Download and install the IDE on your local machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install the WSC
&lt;/h3&gt;

&lt;p&gt;Since we’ll be using the Salesforce SOAP API, we need to install the WSC.&lt;/p&gt;

&lt;h4&gt;
  
  
  Download WSC pre-built .jar files
&lt;/h4&gt;

&lt;p&gt;The &lt;a href="https://mvnrepository.com/artifact/com.force.api/force-wsc" rel="noopener noreferrer"&gt;Maven repository&lt;/a&gt; for the WSC shows all of the versions available:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa42wixm76jtohqde34ah.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa42wixm76jtohqde34ah.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the page for the version that &lt;strong&gt;matches the API version of Salesforce&lt;/strong&gt; that you’re using. In our example, we’ll use &lt;a href="https://mvnrepository.com/artifact/com.force.api/force-wsc/56.0.0" rel="noopener noreferrer"&gt;56.0.0&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftdaumjp0pv5sdnh22hvz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftdaumjp0pv5sdnh22hvz.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under &lt;strong&gt;Files&lt;/strong&gt;, we click on &lt;strong&gt;View All&lt;/strong&gt;. This takes us to the &lt;a href="https://repo1.maven.org/maven2/com/force/api/force-wsc/54.0.0/" rel="noopener noreferrer"&gt;`&lt;/a&gt; of actual &lt;code&gt;.jar&lt;/code&gt; files we will need to download.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fljzxxqpcwtu0uyg3w0ut.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fljzxxqpcwtu0uyg3w0ut.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Download the following four files to your local machine:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;force-wsc-56.0.0-javadoc.jar&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;force-wsc-56.0.0-sources.jar&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;force-wsc-56.0.0-uber.jar&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;force-wsc-56.0.0.jar&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will use these files to generate stub files with the WSDLs from our Salesforce org.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generate and download the WSDL for your Salesforce org
&lt;/h3&gt;

&lt;p&gt;Next, we’ll generate an WSDL to use in generating our .jar stub files. We can’t use pre-built .jar files here because the WSDL is specific to our Salesforce Org. If there are custom objects and fields defined in our Org, then the WSDL will reflect those, and the generated .jar will contain them.&lt;/p&gt;

&lt;p&gt;We log into our Salesforce developer organization. Then, we navigate to &lt;strong&gt;Setup&lt;/strong&gt;, &lt;strong&gt;Platform Tools&lt;/strong&gt;, &lt;strong&gt;Integrations&lt;/strong&gt;, &lt;strong&gt;API&lt;/strong&gt;. We click on “Generate Enterprise WSDL.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2d0w9s1fw4krlu6zy4ic.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2d0w9s1fw4krlu6zy4ic.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will open a new tab in your browser, displaying your WSDL file. Save this file to your local machine with the name &lt;code&gt;sfdc.wsdl&lt;/code&gt;. Put it in the same folder where you downloaded the WSC .jar files.&lt;/p&gt;

&lt;h4&gt;
  
  
  Generate Java stub file
&lt;/h4&gt;

&lt;p&gt;To use the SOAP API with Java, we need to generate .jar stub files for use in our application project. We run the following command to generate the stub file:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`&lt;br&gt;
$ java -classpath force-wsc-56.0.0-uber.jar com.sforce.ws.tools.wsdlc sfdc.wsdl sfdc_stub.jar&lt;/p&gt;

&lt;p&gt;[WSC][wsdlc.main:72]Generating Java files from schema ...&lt;br&gt;
[WSC][wsdlc.main:72]Generating 1206 java files.&lt;br&gt;
[WSC][wsdlc.main:72]Compiled 1210 java files.&lt;br&gt;
[WSC][wsdlc.main:72]Generating jar file ... sfdc_stub.jar&lt;br&gt;
[WSC][wsdlc.main:72]Generated jar file sfdc_stub.jar&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Java Project in Eclipse
&lt;/h2&gt;

&lt;p&gt;In Eclipse, start a new Java project and a new Java class, &lt;code&gt;Main&lt;/code&gt;, using the following code:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`&lt;br&gt;
package wsc;&lt;br&gt;
// Depending on wsdl chosen, change this to enterprise or partner&lt;br&gt;
import com.sforce.soap.enterprise.&lt;em&gt;;&lt;br&gt;
import com.sforce.soap.enterprise.sobject.&lt;/em&gt;;&lt;/p&gt;

&lt;p&gt;import com.sforce.ws.*;&lt;/p&gt;

&lt;p&gt;public class Main { &lt;br&gt;
  public static void main(String[] args) {&lt;br&gt;
    ConnectorConfig credentials = new ConnectorConfig();&lt;br&gt;
    credentials.setPassword("YourPassword!apPenDdedSecurityToken);&lt;br&gt;
    credentials.setUsername("&lt;a href="mailto:yoursalesforceusername@yourdomain.com"&gt;yoursalesforceusername@yourdomain.com&lt;/a&gt;");&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EnterpriseConnection connection;
try {
  connection = Connector.newConnection(credentials);
  Lead le = new Lead();
  le.setLastName("AcmeEnterprises");
  le.setCompany("JavaPush");
  le.setIndustry("Hospitality"); 
  connection.create(new Lead[]{ le });
} catch (ConnectionException e) {
  e.printStackTrace();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Add .jar files to project
&lt;/h3&gt;

&lt;p&gt;Next, we need to connect the generated four .jar files with the build path for our project. Right-click on your project and select &lt;strong&gt;Build Path&lt;/strong&gt;, &lt;strong&gt;Configure Build Path&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4hhgxoz3orybvg6synx4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4hhgxoz3orybvg6synx4.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the modal that comes up, navigate to &lt;strong&gt;Java Build Path&lt;/strong&gt; in the left side panel. Click on the &lt;strong&gt;Libraries&lt;/strong&gt; tab. Select &lt;strong&gt;Modulepath&lt;/strong&gt;, and then click the &lt;strong&gt;Add External JARs&lt;/strong&gt; button. Find the four .jar files that you downloaded from the Maven repository and the generated stub file (&lt;code&gt;sfdc_stub.jar&lt;/code&gt;), and add them. Then, click on &lt;strong&gt;Apply and Close&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo5l0x0a88wihc43sak17.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo5l0x0a88wihc43sak17.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the Application
&lt;/h2&gt;

&lt;p&gt;We are all set up, with our code and our .jar files all in place. Now, right-click on your &lt;code&gt;Main.java&lt;/code&gt; file in Eclipse. Select &lt;strong&gt;Run As&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Java Application&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3b2a1qg192578xojo6k9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3b2a1qg192578xojo6k9.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After we run our application, we navigate to Sales &lt;strong&gt;Leads&lt;/strong&gt; in our Salesforce org, and we see that we have a new lead!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fokfwgk2eibxsb4efzaf1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fokfwgk2eibxsb4efzaf1.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our Java application has successfully tapped into our Salesforce org through the SOAP API, using the WSC to insert a new lead!&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Now that we have verified the initial proof of concept, you can begin expanding your Java application to perform other operations, including reads, updates, and deletions. You can incorporate your own custom business logic to create a Java application that handles your Salesforce org data for you!&lt;/p&gt;

&lt;p&gt;For more information on many of the core concepts we covered, here are links to helpful resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/forcedotcom/wsc" rel="noopener noreferrer"&gt;Web Services Connector (GitHub)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/forcedotcom" rel="noopener noreferrer"&gt;Salesforce Master GitHub Repo (a treasure trove of SFDC repositories)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>api</category>
      <category>salesforce</category>
      <category>ecosystem</category>
    </item>
    <item>
      <title>Swift and Pure Functions</title>
      <dc:creator>Adrian Ruvalcaba</dc:creator>
      <pubDate>Fri, 08 Jul 2022 14:51:59 +0000</pubDate>
      <link>https://forem.com/salesforcedevs/swift-and-pure-functions-176j</link>
      <guid>https://forem.com/salesforcedevs/swift-and-pure-functions-176j</guid>
      <description>&lt;p&gt;I worked on the Salesforce Events iOS team from 2016 to 2020. During this time, we used functional reactive principles to improve our codebase. The benefits that stood out to me the most were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Crash rate dropped from one crash in every 100 launches to one in every 40 thousand.&lt;/li&gt;
&lt;li&gt;Bug rate dropped and bug resolution times decreased.&lt;/li&gt;
&lt;li&gt;Increased development velocity.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What are Pure Functions?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;A function is pure if it always returns the same value for a given set of inputs.&lt;/li&gt;
&lt;li&gt;A pure function is free of side effects.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Side effects are: reads or writes to state outside of the function's set of input params.&lt;/p&gt;

&lt;p&gt;Here's a contrived example of an impure function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//not pure
//has side effects
//doesn't always return same values for input
struct Adder {
    var sum: Int = 0
    var left: Int = 0
    var right: Int = 0

    func add () {
        self.sum = self.left + self.right
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a pure example which meets our two criteria above: returns same output for given input; and is free of side effects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func pureAdd (left: Int, right: Int) → Int {
    return left + right
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cost Per Test
&lt;/h2&gt;

&lt;p&gt;Writing tests that assert that a given set of inputs produce the correct output is as straightforward as a test can get. No mocking or state initialization are required.&lt;/p&gt;

&lt;p&gt;Given the example functions above, testing pureAdd can be done compactly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assertEqual(pureAdd(1, 99), 100)
assertEqual(pureAdd(1, 0), 1)
assertEqual(pureAdd(2, 2), 4)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While the non pure example requires more setup per assertion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let adder = Adder()
adder.left = 1
adder.right = 0
adder.add()
assertEqual(adder.sum, 1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In general, the more side effects a function has, the higher the cost per test&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Effectiveness of Pure functions
&lt;/h2&gt;

&lt;p&gt;The goal of unit tests should be to prove that the function under test produces the correct output for the input space.&lt;/p&gt;

&lt;p&gt;For the function isTrue(input: Bool) -&amp;gt; Bool the input space has two possibilities: true and false. So, to prove correctness we'll need two assertions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assertEqual(isTrue(true), true)
assertEqual(isTrue(false), false)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we add a second Bool param, we double the input space and double the number of assertions needed to prove correctness:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assertEqual(areTrue(true, true), true)
assertEqual(areTrue(true, false), false)
assertEqual(areTrue(false, false), false)
assertEqual(areTrue(false, true), false)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For pure functions, the number of assertions needed to prove correctness is a product of the input space. This means that limiting the input space helps lower testing effort for proving correct output for all input.  &lt;/p&gt;

&lt;p&gt;To lower the input space we should favor using input parameters with a discrete number of possible values.  For example, consider using enum vs String when possible because an enum has a finite set of values while a String has an unbounded number of possible values.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Effectiveness of Non-Pure functions
&lt;/h2&gt;

&lt;p&gt;For non-pure functions proving correct output for the input space is considerably more challenging for a few reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The input into a non-pure function may include object and global state. &lt;/li&gt;
&lt;li&gt;This expanded input space increases the number of assertions needed to prove correctness.&lt;/li&gt;
&lt;li&gt;As seen above, the scaffolding cost per test is often higher for these functions.&lt;/li&gt;
&lt;li&gt;Shared state can change externally before the function completes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Given the increased effort required, proving correctness may become intractable for these functions and we instead target code coverage.&lt;/p&gt;

&lt;p&gt;While coverage is a good metric to help make sure functions have some level of unit testing, I don't believe having code coverage is equivalent to ensuring correctness of the function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;Thanks for reading.  This post was focused on how pure functions can benefit us with regard to testing.  However, there are many more benefits to be gained from functional programming and Swift like concurrency, reactive and composition that are worth exploring.&lt;/p&gt;

</description>
      <category>swift</category>
      <category>functional</category>
    </item>
    <item>
      <title>Working with Salesforce APIs? Of CORS!</title>
      <dc:creator>Michael Bogan</dc:creator>
      <pubDate>Mon, 06 Jun 2022 14:07:08 +0000</pubDate>
      <link>https://forem.com/salesforcedevs/working-with-salesforce-apis-of-cors-85d</link>
      <guid>https://forem.com/salesforcedevs/working-with-salesforce-apis-of-cors-85d</guid>
      <description>&lt;p&gt;The fundamental capabilities of the internet can be boiled down to one simple interaction: a call and a response. One machine (the client) sends &lt;em&gt;requests&lt;/em&gt; to another machine (the server), which &lt;em&gt;responds&lt;/em&gt; with a reply. This back-and-forth request-and-response cycle is how every phone, television, smart fridge, or computer sends and receives data.&lt;/p&gt;

&lt;p&gt;However, there are several security risks inherent in this model, from application vulnerabilities to DDoS attacks. In this post, we’ll take a closer look at how Cross-Origin Resource Sharing, or &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" rel="noopener noreferrer"&gt;CORS&lt;/a&gt;, mitigates these risks and how it operates. By working with a small Node.js app that interacts with several Salesforce APIs, we will see the differences in responses when CORS is enabled and disabled, as well as what effect it has on our client.&lt;/p&gt;

&lt;p&gt;First, let’s take a deeper look at what CORS is and why it exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web Pages Call External Resources
&lt;/h2&gt;

&lt;p&gt;Whenever a browser requests a web page, the server sends back a chunk of HTML. The browser then draws that HTML and, crucially, continues to ask the server for more information when necessary. For example, if there’s any content that can’t be parsed, such as images or videos, or code that needs to be executed (such as JavaScript), then the browser continues making requests to the server, and the server happily fulfills them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security Risks
&lt;/h3&gt;

&lt;p&gt;Suppose you’re browsing around at salesforce.com, and your browser encounters some JavaScript that executes a request to trailhead.com. For example, say a piece of code wants to get a list of a user’s completed trails using the &lt;a href="https://javascript.info/fetch" rel="noopener noreferrer"&gt;fetch&lt;/a&gt; API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let response = await fetch('https://api.trailhead.com/user/1/trails');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the surface, this JavaScript code is pretty innocuous, but it presents a fundamental question about how the web operates: If a browser is loading salesforce.com, should it also be able to load data from trailhead.com? One’s instinct might be to say, “Yes, of course.” But if we allow salesforce.com to load from trailhead.com, why not allow loading code from heroku.com, github.com, or google.com? Why not just load code from anywhere? &lt;/p&gt;

&lt;p&gt;Walking the slippery slope of making requests from any site in the world also introduces the security risks mentioned above. In this example, without any boundaries, trailhead.com can’t control who is requesting data from them. What would be &lt;em&gt;ideal&lt;/em&gt; is if a server could make a list of whom it trusts to be given access out on the web. Perhaps trailhead.com will allow users on salesforce.com to load scripts, but not from anywhere else.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enter CORS!
&lt;/h3&gt;

&lt;p&gt;CORS is designed to plug this hole. A server dictates not only which domains can access its resources, but the types of HTTP verbs as well. If a client isn’t able to satisfy the server’s CORS requirements, then data cannot be executed or delivered. CORS is a concept that extends beyond any one programming language and is configured on a web server, such as nginx or Apache. Most crucially, it’s enforced by the browser: there’s no way around it, and data enforced by CORS can’t be accessed by browser clients outside of that context. CORS is designed to walk the line between enabling a client’s productivity and enforcing a server’s security.&lt;/p&gt;

&lt;p&gt;CORS is enforced through &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" rel="noopener noreferrer"&gt;HTTP Headers&lt;/a&gt;, which can be thought of as additional metadata attached to HTTP requests and responses. You might be familiar with adding headers for &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization" rel="noopener noreferrer"&gt;authentication&lt;/a&gt; (such as &lt;code&gt;Authorization: token xxx&lt;/code&gt;) or to specify the data types the client understands (via &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept" rel="noopener noreferrer"&gt;Accept&lt;/a&gt;). CORS has &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#cors" rel="noopener noreferrer"&gt;its own category of headers&lt;/a&gt;, which a server uses to state where a browser may load its resources from.&lt;/p&gt;

&lt;p&gt;To demonstrate what this means in practice, let’s take a look at how a demo app using the Salesforce platform interacts with CORS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo prerequisites
&lt;/h2&gt;

&lt;p&gt;Before getting started, make sure that your &lt;a href="https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_rest_compatible_editions.htm" rel="noopener noreferrer"&gt;Salesforce instance supports access to its API&lt;/a&gt;. If you don’t have access to the Salesforce platform, then you can &lt;a href="https://developer.salesforce.com/signup" rel="noopener noreferrer"&gt;create a free Developer Edition account&lt;/a&gt;. You can use this org to test what developing on the Salesforce platform looks like.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requesting Salesforce resources
&lt;/h3&gt;

&lt;p&gt;Open a browser tab and navigate to &lt;a href="https://jsfiddle.net" rel="noopener noreferrer"&gt;https://jsfiddle.net&lt;/a&gt;. In the box that says JavaScript, paste these lines of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const run = async () =&amp;gt; {
const response = await fetch('https://myInstance-dev-ed.my.salesforce.com/services/data');
const responseJson = await response.json();
responseJson.forEach( (e) =&amp;gt; document.write(e.label + "&amp;lt;br/&amp;gt;"));
document.body.style.background = "white";
}
run();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;myInstance-dev-ed&lt;/code&gt;with the actual name of your Salesforce instance. Click &lt;strong&gt;Run&lt;/strong&gt; in the top menu bar. You should see….nothing. &lt;/p&gt;

&lt;p&gt;However, if you open &lt;a href="https://developer.mozilla.org/en-US/docs/Tools/Browser_Console" rel="noopener noreferrer"&gt;the Browser Console&lt;/a&gt;, you’ll see a message indicating that CORS has blocked access:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Access to fetch at 
'https://myInstance-dev-ed.my.salesforce.com/services/data' from origin 
'https://fiddle.jshell.net' has been blocked by CORS policy: No 
'Access-Control-Allow-Origin' header is present on the requested resource. 
If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What’s going on here? Well, Salesforce provides semi-open access to its data and assets. In this case, we tried to use JavaScript’s &lt;a href="https://javascript.info/fetch" rel="noopener noreferrer"&gt;fetch&lt;/a&gt; function to make a request and retrieve some JSON data. However, due to CORS, we were blocked from doing so. Unauthorized access is exactly what CORS was designed to prevent. &lt;/p&gt;

&lt;p&gt;Specifically, we were blocked by the lack of a proper &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin" rel="noopener noreferrer"&gt;Access-Control-Allow-Origin&lt;/a&gt; definition. This header states which URLs a browser is permitted to load resources from. If this information is missing, then the browser simply cannot load the external resource, which in this case was the content at the &lt;code&gt;/services/data&lt;/code&gt; path of our Salesforce instance. There are many other types of CORS headers, such as &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods" rel="noopener noreferrer"&gt;Access-Control-Allow-Methods&lt;/a&gt;, which defines which HTTP methods a browser is permitted to make.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enabling CORS in Salesforce
&lt;/h3&gt;

&lt;p&gt;Fortunately, it doesn’t take much to enable CORS on the Salesforce APIs. Follow the short steps &lt;a href="https://developer.salesforce.com/docs/atlas.en-us.204.0.api_rest.meta/api_rest/extend_code_cors.htm" rel="noopener noreferrer"&gt;in this guide&lt;/a&gt;, and enter &lt;code&gt;https://fiddle.jshell.net&lt;/code&gt;as the allowed domain:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa4ecnvg9fhke53ppr2ju.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa4ecnvg9fhke53ppr2ju.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure there’s no trailing slash at the end of this URL.&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Save&lt;/strong&gt;, then run that code snippet one more time. Voila! It’s not much, but you should see a list of some Salesforce editions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other CORS Considerations
&lt;/h2&gt;

&lt;p&gt;While Salesforce takes the utmost effort to provide a way to securely retrieve data, other platforms might not be as secure. Fortunately, CORS also provides the ability to pass in &lt;a href="https://javascript.info/fetch-crossorigin#credentials" rel="noopener noreferrer"&gt;credentials&lt;/a&gt;&lt;strong&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Credentials are useful if you’re navigating on a site that, unlike Salesforce, does not use an authentication mechanism. By setting an additional option in your client-side JavaScript request (&lt;code&gt;credentials: "include"&lt;/code&gt;), your browser’s cookies are sent over along with the request. Cookies have been used as an identifying mechanism since the very early days of the internet. Although they’ve largely been abandoned as an authentication method, some websites still do use them as such. It’s the server’s responsibility to receive the cookies and perform any additional validations on them.&lt;/p&gt;

&lt;p&gt;Since CORS support is largely a consideration for servers (that is, servers should have CORS enabled by default), there isn’t much else for the client to do. There are different &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Request/mode" rel="noopener noreferrer"&gt;modes&lt;/a&gt; that a client can set, which have a variety of purposes. For example, if you’re building a series of microservices, then you may find that the &lt;code&gt;same-origin&lt;/code&gt;’s strict domain matching may be useful for doubly ensuring that requests and responses come from the same domain.&lt;/p&gt;

&lt;p&gt;We’ve largely been talking about CORS and JavaScript, but there’s another important component to web development where CORS can step in: the humble &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe" rel="noopener noreferrer"&gt;&amp;lt;iframe&amp;gt;&lt;/a&gt;. The iframe element allows you to embed the contents from another URL into a web page. What keeps me from registering &lt;a href="//www.not-salesforce.com"&gt;www.not-salesforce.com&lt;/a&gt; and just setting the page to the contents of Salesforce? CORS, of course!&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Ultimately, not every server supports open CORS access, and it’s at the server’s discretion. Improperly configured CORS settings have the potential to impede legitimate user workflows, but disabling CORS entirely will certainly make your application more vulnerable to attacks from malicious clients. In general, as a developer, if an action can be done on the server, it’s more likely to be safer than performing work on the client, so make sure you are disabling CORS for a good reason if you’re thinking of it.&lt;/p&gt;

&lt;p&gt;We’ve just taken a brief look at Salesforce APIs here. You can also manipulate data or retrieve all sorts of Apex data. We encourage you to take a look at &lt;a href="https://developer.salesforce.com/docs/atlas.en-us.204.0.api_rest.meta/api_rest/intro_what_is_rest_api.htm" rel="noopener noreferrer"&gt;the Force.com REST API Developer Guide&lt;/a&gt; for more information on what the APIs allow you to do. You can also find &lt;a href="https://trailhead.salesforce.com/" rel="noopener noreferrer"&gt;a bevy of tutorials on Trailhead&lt;/a&gt; to help you build your integrations with the platform.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cors</category>
      <category>salesforce</category>
      <category>api</category>
    </item>
    <item>
      <title>Building a Kotlin Mobile App with the Salesforce SDK, Part 3: Synchronizing Data</title>
      <dc:creator>Michael Bogan</dc:creator>
      <pubDate>Mon, 02 May 2022 18:49:22 +0000</pubDate>
      <link>https://forem.com/salesforcedevs/building-a-kotlin-mobile-app-with-the-salesforce-sdk-part-3-synchronizing-data-3dco</link>
      <guid>https://forem.com/salesforcedevs/building-a-kotlin-mobile-app-with-the-salesforce-sdk-part-3-synchronizing-data-3dco</guid>
      <description>&lt;p&gt;This is our final post in our three-part series demonstrating how to use the Salesforce Mobile SDK to build an Android app that works with the Salesforce platform. &lt;a href="https://dev.to/salesforcedevs/building-a-kotlin-mobile-app-with-the-salesforce-sdk-part-1-getting-started-4o4o"&gt;In our first post&lt;/a&gt;, we showed you how to connect to your org. &lt;a href="https://dev.to/mbogan/building-a-kotlin-mobile-app-with-the-salesforce-sdk-editing-and-creating-data-1pb6"&gt;Our second post&lt;/a&gt; showed you how to edit and add data to your org from your app. This post will show you how to synchronize data from your Salesforce org to your mobile device and handle scenarios such as network loss. Let’s get right into it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with Mobile Sync
&lt;/h2&gt;

&lt;p&gt;One of the hardest aspects of mobile development is dealing with data synchronization. How do you handle the situation when you need to add a new broker, but you’re offline? Or what if two agents are updating the same broker—how can you handle the merging of those two changes?&lt;/p&gt;

&lt;p&gt;With the Salesforce Mobile SDK, these real-world issues are handled for you by a system called &lt;a href="https://developer.salesforce.com/docs/atlas.en-us.mobile_sdk.meta/mobile_sdk/entity_framework_native_using.htm" rel="noopener noreferrer"&gt;Mobile Sync&lt;/a&gt;. Mobile Sync has you map your phone’s local data to the data in Salesforce; it also requires you to define operations for fetching and pushing data—what it calls &lt;code&gt;syncDown&lt;/code&gt; and &lt;code&gt;syncUp&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Define the shape of data to be synced
&lt;/h2&gt;

&lt;p&gt;To get started with Mobile Sync, create a file in &lt;code&gt;res/raw&lt;/code&gt; called &lt;code&gt;brokerstore.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
 "soups": [
   {
     "soupName": "brokers",
     "indexes": [
       { "path": "Id", "type": "string"},
       { "path": "Name", "type": "string"},
       { "path": "Title__c", "type": "string"},
       { "path": "Phone__c", "type": "string"},
       { "path": "Mobile_Phone__c", "type": "string"},
       { "path": "Email__c", "type": "string"},
       { "path": "Picture__c", "type": "string"},
       { "path": "__local__", "type": "string"},
       { "path": "__locally_created__", "type": "string"},
       { "path": "__locally_updated__", "type": "string"},
       { "path": "__locally_deleted__", "type": "string"},
       { "path": "__sync_id__", "type": "integer"}
     ]
   }
 ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file defines the shape of the data on your phone, as well as some additional metadata that’s necessary for the sync.&lt;/p&gt;

&lt;p&gt;Next, create a file called &lt;code&gt;brokersync.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
 "syncs": [
   {
     "syncName": "syncDownBrokers",
     "syncType": "syncDown",
     "soupName": "brokers",
     "target": {"type":"soql", "query":"SELECT Name, Title__c, Phone__c, Mobile_Phone__c, Email__c, Picture__c FROM Broker__c LIMIT 10000"},
     "options": {"mergeMode":"OVERWRITE"}
   },
   {
     "syncName": "syncUpBrokers",
     "syncType": "syncUp",
     "soupName": "brokers",
     "target": {"createFieldlist":["Name", "Title__c", "Phone__c", "Mobile_Phone__c", "Email__c", "Picture__c"]},
     "options": {"fieldlist":["Id", "Name", "Title__c", "Phone__c", "Mobile_Phone__c", "Email__c", "Picture__c"], "mergeMode":"LEAVE_IF_CHANGED"}
   }
 ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are the operations that Mobile Sync will use when syncing data down and up.&lt;/p&gt;

&lt;p&gt;The code to complete the Mobile Sync process depends on several factors, such as when you want to perform a synchronization, as well as hooking into the Android event cycle when a device loses (and regains) connectivity. &lt;/p&gt;

&lt;p&gt;The following code samples will show you a complete example of what needs to happen to get synchronization working, but they should be considered high-level concepts, and not necessarily production-ready enterprise-grade code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up periodic sync
&lt;/h2&gt;

&lt;p&gt;With that said, let’s take a look at how to implement synchronization. First, add this line to the end of our &lt;code&gt;onResume(client: RestClient)&lt;/code&gt; method in &lt;code&gt;MainActivity.kt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;setupPeriodicSync();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we’ll add a new variable and a new function to the &lt;code&gt;MainActivity&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private val SYNC_CONTENT_AUTHORITY =
   "com.salesforce.samples.mobilesyncexplorer.sync.brokersyncadapter"

private fun setupPeriodicSync() {
   val account = MobileSyncSDKManager.getInstance().userAccountManager.currentAccount

   ContentResolver.setSyncAutomatically(account, SYNC_CONTENT_AUTHORITY, true)
   ContentResolver.addPeriodicSync(
       account, SYNC_CONTENT_AUTHORITY,
       Bundle.EMPTY, 10
   )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we’re using &lt;code&gt;ContentResolver&lt;/code&gt; in our function, let’s make sure to import it by adding this line alongside the other import statements near the top of &lt;code&gt;MainActivity.kt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import.android.content.ContentResolver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have defined two methods that trigger synchronization. &lt;code&gt;setupPeriodicSync&lt;/code&gt; will run a sync every &lt;code&gt;10&lt;/code&gt; seconds. This is much too frequent for a production environment, but we’ll set it this way for demonstration purposes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mapping sync operations to our data and UI
&lt;/h2&gt;

&lt;p&gt;We will show the next few code samples all at once, and discuss what they’re doing afterward.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;app/java/com.example.sfdc&lt;/code&gt;, create a new file called &lt;code&gt;BrokerSyncAdapter.kt&lt;/code&gt; and paste these lines into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.example.sfdc

import android.accounts.Account
import android.content.AbstractThreadedSyncAdapter
import android.content.ContentProviderClient
import android.content.Context
import android.content.SyncResult
import android.os.Bundle
import com.salesforce.androidsdk.accounts.UserAccount
import com.salesforce.androidsdk.accounts.UserAccountManager
import com.salesforce.androidsdk.app.SalesforceSDKManager
import com.example.sfdc.BrokerListLoader

class BrokerSyncAdapter
   (
   context: Context?, autoInitialize: Boolean,
   allowParallelSyncs: Boolean
) :
   AbstractThreadedSyncAdapter(context, autoInitialize, allowParallelSyncs) {
   override fun onPerformSync(
       account: Account, extras: Bundle, authority: String,
       provider: ContentProviderClient, syncResult: SyncResult
   ) {
       val syncDownOnly = extras.getBoolean(SYNC_DOWN_ONLY, false)
       val sdkManager = SalesforceSDKManager.getInstance()
       val accManager = sdkManager.userAccountManager
       if (sdkManager.isLoggingOut || accManager.authenticatedUsers == null) {
           return
       }
       if (account != null) {
           val user = sdkManager.userAccountManager.buildUserAccount(account)
           val contactLoader = BrokerListLoader(context, user)
           if (syncDownOnly) {
               contactLoader.syncDown()
           } else {
               contactLoader.syncUp() // does a sync up followed by a sync down
           }
       }
   }

   companion object {
       // Key for extras bundle
       const val SYNC_DOWN_ONLY = "syncDownOnly"
   }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, in that same folder, create &lt;code&gt;BrokerListLoader.kt&lt;/code&gt; with these lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.example.sfdc

import android.content.AsyncTaskLoader
import android.content.Context
import android.content.Intent
import android.util.Log
import com.salesforce.androidsdk.accounts.UserAccount
import com.salesforce.androidsdk.app.SalesforceSDKManager
import com.salesforce.androidsdk.mobilesync.app.MobileSyncSDKManager
import com.salesforce.androidsdk.mobilesync.manager.SyncManager
import com.salesforce.androidsdk.mobilesync.manager.SyncManager.MobileSyncException
import com.salesforce.androidsdk.mobilesync.manager.SyncManager.SyncUpdateCallback
import com.salesforce.androidsdk.mobilesync.util.SyncState
import com.salesforce.androidsdk.smartstore.store.QuerySpec
import com.salesforce.androidsdk.smartstore.store.SmartSqlHelper.SmartSqlException
import com.salesforce.androidsdk.smartstore.store.SmartStore
import org.json.JSONArray
import org.json.JSONException
import java.util.ArrayList

class BrokerListLoader(context: Context?, account: UserAccount?) :
   AsyncTaskLoader&amp;lt;List&amp;lt;String&amp;gt;?&amp;gt;(context) {
   private val smartStore: SmartStore
   private val syncMgr: SyncManager
   override fun loadInBackground(): List&amp;lt;String&amp;gt;? {
       if (!smartStore.hasSoup(BROKER_SOUP)) {
           return null
       }
       val querySpec = QuerySpec.buildAllQuerySpec(
           BROKER_SOUP,
           "Name", QuerySpec.Order.ascending, LIMIT
       )
       val results: JSONArray
       val brokers: MutableList&amp;lt;String&amp;gt; = ArrayList&amp;lt;String&amp;gt;()
       try {
           results = smartStore.query(querySpec, 0)
           for (i in 0 until results.length()) {
               brokers.add(results.getJSONObject(i).getString("Name"))
           }
       } catch (e: JSONException) {
           Log.e(TAG, "JSONException occurred while parsing", e)
       } catch (e: SmartSqlException) {
           Log.e(TAG, "SmartSqlException occurred while fetching data", e)
       }
       return brokers
   }

   @Synchronized
   fun syncUp() {
       try {
           syncMgr.reSync(
               SYNC_UP_NAME
           ) { sync -&amp;gt;
               if (SyncState.Status.DONE == sync.status) {
                   syncDown()
               }
           }
       } catch (e: JSONException) {
           Log.e(TAG, "JSONException occurred while parsing", e)
       } catch (e: MobileSyncException) {
           Log.e(TAG, "MobileSyncException occurred while attempting to sync up", e)
       }
   }

   /**
    * Pulls the latest records from the server.
    */
   @Synchronized
   fun syncDown() {
       try {
           syncMgr.reSync(
               SYNC_DOWN_NAME
           ) { sync -&amp;gt;
               if (SyncState.Status.DONE == sync.status) {
                   fireLoadCompleteIntent()
               }
           }
       } catch (e: JSONException) {
           Log.e(TAG, "JSONException occurred while parsing", e)
       } catch (e: MobileSyncException) {
           Log.e(TAG, "MobileSyncException occurred while attempting to sync down", e)
       }
   }

   private fun fireLoadCompleteIntent() {
       val intent = Intent(LOAD_COMPLETE_INTENT_ACTION)
       SalesforceSDKManager.getInstance().appContext.sendBroadcast(intent)
   }

   companion object {
       const val BROKER_SOUP = "brokers"
       const val LOAD_COMPLETE_INTENT_ACTION =
           "com.salesforce.samples.mobilesyncexplorer.loaders.LIST_LOAD_COMPLETE"
       private const val TAG = "BrokerListLoader"
       private const val SYNC_DOWN_NAME = "syncDownBrokers"
       private const val SYNC_UP_NAME = "syncUpBrokers"
       private const val LIMIT = 10000
   }

   init {
       val sdkManager = MobileSyncSDKManager.getInstance()
       smartStore = sdkManager.getSmartStore(account)
       syncMgr = SyncManager.getInstance(account)
       // Setup schema if needed
       sdkManager.setupUserStoreFromDefaultConfig()
       // Setup syncs if needed
       sdkManager.setupUserSyncsFromDefaultConfig()
   }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What did we just do? Each file has a specific role, and while the objects and fields will certainly be different for your app, the functions and philosophies of these classes are the same:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;BrokerListLoader&lt;/code&gt; is responsible for mapping the sync operations you defined in &lt;code&gt;brokersync.json&lt;/code&gt; with the Kotlin code that will actually perform the work. Notice that there are &lt;code&gt;syncUp&lt;/code&gt; and &lt;code&gt;syncDown&lt;/code&gt; methods that use the Mobile SDK’s &lt;code&gt;SyncManager&lt;/code&gt; to load the JSON files and communicate back and forth with Salesforce. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BrokerSyncAdapter&lt;/code&gt; can perhaps best be thought of as the code that’s responsible for scheduling the synchronization. That is, it’s the entry point for &lt;code&gt;BrokerListLoader&lt;/code&gt;, and can be called by UI elements (such as during a refresh button click) or Android system events (such as connectivity loss).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lastly, we need to add one line to our &lt;code&gt;AndroidManifest.xml&lt;/code&gt; file (in &lt;code&gt;app/manifests&lt;/code&gt;). Our new sync functionality will need special Android permissions, which we ask the user to allow upon app installation at runtime. Add the following line with &lt;code&gt;WRITE_SYNC_SETTINGS&lt;/code&gt; to the end of your manifest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  &amp;lt;uses-permission android:name="com.example.sfdc.C2D_MESSAGE" /&amp;gt;
  &amp;lt;uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" /&amp;gt;
&amp;lt;/manifest&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing sync
&lt;/h2&gt;

&lt;p&gt;Our last step is to test that mobile sync works. With the emulator running, you should be logged in and see a list of brokers. This list should mirror the broker list that you see in your web browser on your local machine. In your web browser, edit one of the broker names, and save that change.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F68t09y71k8zme3ts3j3p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F68t09y71k8zme3ts3j3p.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, in your emulator, you can power off the phone or switch to another application and then switch back to your &lt;code&gt;sfdc-mobile-app&lt;/code&gt;. You should see your list of broker names updated with the change you made in your browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F772acvyw2b8tn3svehs6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F772acvyw2b8tn3svehs6.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that’s all! Again, this is just a foundational building block for you to learn from. In just about 150 lines of code, we’ve devised a solution for a rather complicated series of moving parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mapping Salesforce custom objects to JSON&lt;/li&gt;
&lt;li&gt;Setting up a timer to periodically sync data back and forth between a Salesforce org and a mobile device&lt;/li&gt;
&lt;li&gt;Handling errors (and recovering from issues) around network connectivity&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The Salesforce Mobile SDK makes it tremendously easy to connect mobile devices with Salesforce data. In this post, you learned how to query and manipulate data from your phone, and saw the results reflected instantaneously on Salesforce. You also learned about Mobile Sync and its role in anticipating issues with connectivity.&lt;/p&gt;

&lt;p&gt;And yet, with all of this information, there’s still &lt;em&gt;so much more&lt;/em&gt; that the Salesforce Mobile SDK can do. Take a look at the&lt;a href="https://developer.salesforce.com/docs/atlas.en-us.noversion.mobile_sdk.meta/mobile_sdk" rel="noopener noreferrer"&gt; complete documentation&lt;/a&gt; on working with the SDK. Or, if you prefer to do more coding, there are &lt;a href="https://trailhead.salesforce.com/en/search?keywords=salesforce+mobile+sdk" rel="noopener noreferrer"&gt;plenty of Trailhead tutorials &lt;/a&gt;to keep you busy. We can’t wait to see what you build!&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>mobile</category>
      <category>salesforce</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building a Slack App with Native SFDC Integration</title>
      <dc:creator>Michael Bogan</dc:creator>
      <pubDate>Wed, 13 Apr 2022 14:36:35 +0000</pubDate>
      <link>https://forem.com/salesforcedevs/building-a-slack-app-with-native-sfdc-integration-502e</link>
      <guid>https://forem.com/salesforcedevs/building-a-slack-app-with-native-sfdc-integration-502e</guid>
      <description>&lt;h3&gt;
  
  
  Synchronizing Data
&lt;/h3&gt;

&lt;p&gt;At last, we’ve arrived at the final part of our series on using Salesforce’s &lt;a href="https://github.com/developerforce/salesforce-slack-starter-kit" rel="noopener noreferrer"&gt;Slack Starter Kit&lt;/a&gt;  to quickly scaffold a deployable Slack App that interacts with Salesforce data. The Slack Starter Kit makes it tremendously easy to authenticate with Salesforce, organize your code into reusable chunks, and deploy the project to Heroku for live access. We’ve largely based this series on &lt;a href="https://www.youtube.com/playlist?list=PLgIMQe2PKPSKcl26YQoKCR7F3CwIc8kxi" rel="noopener noreferrer"&gt;these video tutorials showcasing how to build Slack Apps&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/salesforcedevs/building-a-slack-app-with-native-sfdc-integration-o94"&gt;In our first post&lt;/a&gt;, we got acquainted with the Slack Starter Kit and set up our development environment. &lt;a href="https://dev.to/salesforcedevs/building-a-slack-app-with-native-sfdc-integration-4m4m"&gt;In our second post&lt;/a&gt;, our Slack app issued a query to fetch data from a Salesforce org and then presented the result with UI components from Block Kit. Now, we’re going to extend that pattern to showcase how you can edit Salesforce data entirely within Slack. Let’s get started!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsmjc2xf1gr5pxyszvis3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsmjc2xf1gr5pxyszvis3.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@ymoran?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Edgar Moran&lt;/a&gt; on&lt;a href="https://unsplash.com/s/photos/salesforce?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt; Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a shortcut
&lt;/h3&gt;

&lt;p&gt;Outside of the Block Kit UI, Slack has support for two other interactivity systems: &lt;a href="https://api.slack.com/interactivity/slash-commands" rel="noopener noreferrer"&gt;Slash Commands&lt;/a&gt; and &lt;a href="https://api.slack.com/interactivity/shortcuts/" rel="noopener noreferrer"&gt;shortcuts&lt;/a&gt;. Slash Commands are entered by a user in Slack’s text input (available in every channel), while shortcuts are graphical in nature. Since they’re easier to visualize, we’ll demonstrate shortcuts by creating a shortcut that will fetch our list of contacts and give us the ability to edit them.&lt;/p&gt;

&lt;p&gt;Adding a shortcut (or a Slash Command, for that matter) first requires that you tell Slack the name of the command. Go to your app’s Overview page on Slack, and then click &lt;strong&gt;Interactivity &amp;amp; Shortcuts&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmjjmuisto15dcu3h9iku.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmjjmuisto15dcu3h9iku.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Create New Shortcut&lt;/strong&gt;, and select &lt;strong&gt;Global&lt;/strong&gt; as your shortcut type, then click &lt;strong&gt;Next&lt;/strong&gt;. On the next page, enter these values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt;: Edit contacts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Short Description&lt;/strong&gt;: Edits contacts on SFDC&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Callback ID&lt;/strong&gt;: &lt;code&gt;edit-contact-shortcut&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click on &lt;strong&gt;Create&lt;/strong&gt;, then click &lt;strong&gt;Save Changes&lt;/strong&gt;. Switch over to your Slack workspace, and click on the plus sign in the text area. You’ll be able to browse all of your workspace shortcuts. Here, you’ll also see the brand new shortcut you just created:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjgrmqs4728p4gbfxk40d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjgrmqs4728p4gbfxk40d.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This shortcut doesn’t do anything yet, but this process is necessary for Slack to know about your shortcut. Next, let’s add the code to handle the event that fires whenever someone clicks on this shortcut.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wiring up the shortcut
&lt;/h3&gt;

&lt;p&gt;In your code editor, navigate to &lt;code&gt;apps/slack-salesforce-starter-app/listeners/shortcuts/index.js&lt;/code&gt;. This is the spot where we connect shortcut events to the code that’s executed. There’s already one shortcut given to us by the Starter Kit: &lt;code&gt;whoami&lt;/code&gt;. The given line suggests to us what we need to do: We call a function called &lt;code&gt;shortcut&lt;/code&gt; and pass in a string and a function name. In this case, our string is the callback ID we previously defined, and our function name is the code we’ve yet to write. Change the file contents to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { whoamiCallback } = require('./whoami');
const { editContactCallback } = require('./edit-contact');

module.exports.register = (app) =&amp;gt; {
   app.shortcut('who_am_i', whoamiCallback);
   app.shortcut('edit-contact-shortcut', editContactCallback);
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re laying the groundwork here by saying: “&lt;em&gt;Slack App, if you get a shortcut with a callback ID of &lt;code&gt;edit-contact-shortcut&lt;/code&gt; run &lt;code&gt;editContactCallback&lt;/code&gt;&lt;/em&gt;.”&lt;/p&gt;

&lt;p&gt;In this same folder, create a file called &lt;code&gt;edit-contact.js&lt;/code&gt;, and paste these lines into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'use strict';

const {
  editContactResponse,
  authorize_sf_prompt
} = require('../../user-interface/modals');

const editContactCallback = async ({ shortcut, ack, client, context }) =&amp;gt; {
  try {
    await ack();
    if (context.hasAuthorized) {
      const conn = context.sfconnection;
      await client.views.open({
        trigger_id: shortcut.trigger_id,
        view: await editContactResponse(conn)
      });
    } else {
      // Get BotInfo
      const botInfo = await client.bots.info({ bot: context.botId });
      // Open a Modal with message to navigate to App Home for authorization
      await client.views.open({
        trigger_id: shortcut.trigger_id,
        view: authorize_sf_prompt(context.teamId, botInfo.bot.app_id)
      });
    }
  } catch (error) {
    // eslint-disable-next-line no-console
    console.error(error);
  }
};

module.exports = { editContactCallback };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, this might look intimidating, but most of it simply concerns the authentication boilerplate, ensuring that a user has an active SFDC connection. In the first logical path (if &lt;code&gt;context.hasAuthorized&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;), we execute a function called &lt;code&gt;editContactResponse&lt;/code&gt;, which accepts our open Salesforce &lt;code&gt;conn&lt;/code&gt;ection. In the negative case, we ask the user to go to the Home tab to reauthenticate, just as we did in Part 1 of this tutorial.&lt;/p&gt;

&lt;p&gt;Navigate to the &lt;code&gt;apps/slack-salesforce-starter-app/user-interface/modals&lt;/code&gt; folder, and create a file called &lt;code&gt;edit-contact-response.js&lt;/code&gt;. Here, we’ll pop open a modal with contact information similar to the rows we saw in the Home tab in Part 2 of this tutorial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'use strict';
const { Elements, Modal, Blocks } = require('slack-block-builder');

const editContactResponse = async (conn) =&amp;gt; {
  const result = await conn.query(
    `Select Id, Name, Description FROM Contact`
  );
  let records = result.records;

  let blockCollection = records.map((record) =&amp;gt; {
    return Blocks.Section({
      text: `*${record.Name}*\n${record.Description}`
    }).accessory(
      Elements.Button()
        .text(`Edit`)
        .actionId(`edit_contact`)
        .value(record.Id)
    );
  });

  return Modal({ title: 'Salesforce Slack App', close: 'Close' })
    .blocks(blockCollection)
    .buildToJSON();
};

module.exports = { editContactResponse };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main difference between the code in Part 2 and this block is that we’re using an array called &lt;code&gt;blockCollection&lt;/code&gt;, which lets us construct an array of blocks (in this case, Section blocks). &lt;code&gt;blocks&lt;/code&gt; knows how to take this array and transform it into a format that Slack understands, which makes it super simple to create data through a looped array, as we’ve done here. In Part 2 of our series, we constructed a giant string of data. By using &lt;code&gt;BlockCollection&lt;/code&gt;, however, we can attach other Slack elements—such as buttons—which we’ve done here.&lt;/p&gt;

&lt;p&gt;Lastly, in &lt;code&gt;apps/slack-salesforce-starter-app/user-interface/modals/index.js&lt;/code&gt;, we’ll need to export this function, so that it can be imported by our &lt;code&gt;edit-contact.js&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'use strict';
const { whoamiresponse } = require('./whoami-response');
const { editContactResponse } = require('./edit-contact-response');
const { authorize_sf_prompt } = require('./authorize-sf-prompt');

module.exports = { whoamiresponse, editContactResponse, authorize_sf_prompt };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After you’ve deployed this new code to Heroku via git push, switch to your Slack workspace and try executing the shortcut; you’ll be greeted with a dialog box similar to this one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1l4q9hu9r2pwht523y6s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1l4q9hu9r2pwht523y6s.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Updating Salesforce data
&lt;/h3&gt;

&lt;p&gt;We’re able to fetch and display Salesforce data. Now, it’s time to connect the Edit button to change Salesforce data!&lt;/p&gt;

&lt;p&gt;Many of Slack’s interactive components have an &lt;a href="https://api.slack.com/interactivity/handling#payloads" rel="noopener noreferrer"&gt;&lt;code&gt;action_id&lt;/code&gt;&lt;/a&gt;, which, like the &lt;code&gt;callback_id&lt;/code&gt;, serves to identify the element which a user acted upon. Just like everything else in the Starter Kit, there’s a special directory where you can define listeners for these action IDs: &lt;code&gt;apps/slack-salesforce-starter-app/listeners/actions&lt;/code&gt;. In the &lt;code&gt;index.js&lt;/code&gt; file there, let’s add a new line that ties together the action ID with our yet-to-be-written functionality:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'use strict';
const { appHomeAuthorizeButtonCallback } = require('./app-home-authorize-btn');
const { editContactButtonCallback } = require('./edit-contact-btn');

module.exports.register = (app) =&amp;gt; {
  app.action('authorize-with-salesforce', appHomeAuthorizeButtonCallback);
  app.action('edit_contact', editContactButtonCallback);
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this same folder, create a new file called &lt;code&gt;edit-contact-btn.js&lt;/code&gt;, and paste these lines into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'use strict';
const {
  editIndividualContact,
  authorize_sf_prompt
} = require('../../user-interface/modals');

const editContactButtonCallback = async ({ body, ack, client, context }) =&amp;gt; {
  const contactId = body.actions[0].value;
  try {
    await ack();
  } catch (error) {
    // eslint-disable-next-line no-console
    console.error(error);
  }

  if (context.hasAuthorized) {
    const conn = context.sfconnection;
    const result = await conn.query(
      `SELECT Id, Name, Description FROM Contact WHERE Id='${contactId}'`
    );
    let record = result.records[0];
    await client.views.push({
      trigger_id: body.trigger_id,
      view: editIndividualContact(record)
    });
  } else {
    // Get BotInfo
    const botInfo = await client.bots.info({ bot: context.botId });
    // Open a Modal with message to navigate to App Home for authorization
    await client.views.push({
      trigger_id: body.trigger_id,
      view: authorize_sf_prompt(context.teamId, botInfo.bot.app_id)
    });
  }
};

module.exports = { editContactButtonCallback };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The beginning and ending of this file should look familiar: We’re sending an &lt;code&gt;ack&lt;/code&gt; response back to Slack to let it know that our app received the event payload (in this case, from clicking on the Edit button). We’re also checking whether or not we’re still authenticated. Here, we’re doing a single DB lookup using the ID of the contact, which we attached as a value to the Edit button when constructing our UI.&lt;/p&gt;

&lt;p&gt;This chunk of code creates another modal design which we need to define. Back in &lt;code&gt;apps/slack-salesforce-starter-app/user-interface/modals&lt;/code&gt;, create a file called &lt;code&gt;edit-individual-contact.js&lt;/code&gt; and paste these lines into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'use strict';
const { Elements, Modal, Blocks } = require('slack-block-builder');

const editIndividualContact = (record) =&amp;gt; {
  return Modal({ title: 'Edit Contact', close: 'Close' })
    .blocks(
      Blocks.Input({ blockId: 'description-block', label: record.Name }).element(
        Elements.TextInput({
          placeholder: record.Description,
          actionId: record.Id
        })
     )
   )
   .submit('Save')
   .callbackId('edit-individual')
   .buildToJSON();
};

module.exports = { editIndividualContact };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we’ve created a modal with a single block: an input element. The element will be pre-populated with the contact’s description. We can edit this block and change the description to whatever we want. &lt;/p&gt;

&lt;p&gt;There are two important notes to point out in this code snippet:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Notice that we’re attaching an &lt;code&gt;actionId&lt;/code&gt; to the input element. This is analogous to the ID we attached to the Edit button earlier, except this time, it’s dynamically generated based on the ID of the record we’re editing.&lt;/li&gt;
&lt;li&gt;You’ll also notice that we have another ID, the &lt;code&gt;callbackID&lt;/code&gt;, which is attached to the modal itself. Keep the existence of these IDs in the back of your mind: we’ll address both of these in a moment. For now, open up the &lt;code&gt;index.js&lt;/code&gt; file in this same directory, and &lt;code&gt;require/export&lt;/code&gt; this new modal-creating function:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { editIndividualContact } = require('./edit-individual-contact');
// ...
module.exports = {
  whoamiresponse,
  editContactResponse,
  editIndividualContact,
  authorize_sf_prompt
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when you click the Edit button, you’ll be prompted to change the description:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fckktl7bqmuwr7fn7xteh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fckktl7bqmuwr7fn7xteh.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We now need to send this updated text to Salesforce. Click on the &lt;strong&gt;Save&lt;/strong&gt; button and…nothing happens. Why? Well, Slack has a different set of events for interactions like these, called &lt;a href="https://api.slack.com/reference/interaction-payloads/views" rel="noopener noreferrer"&gt;view submissions&lt;/a&gt;. The Starter Kit provides a good jumping off point when building an app, but it doesn’t handle every Slack use case, including this one. But that’s not a problem—we’ll add the functionality ourselves!&lt;/p&gt;

&lt;p&gt;Within the &lt;code&gt;apps/slack-salesforce-starter-app/user-interface folder&lt;/code&gt;, create a new folder called &lt;code&gt;views&lt;/code&gt;. Just like before, our &lt;strong&gt;Save&lt;/strong&gt; button here has an action ID to identify it: &lt;code&gt;edit-individual-contact&lt;/code&gt;. We’ll head back into &lt;code&gt;apps/slack-salesforce-starter-app/listeners/actions/index.js&lt;/code&gt; to configure this to a function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { editIndividualButtonCallback } = require('./edit-individual-btn);

// ... 

app.action('edit-individual-contact', editIndividualButtonCallback);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a new file called &lt;code&gt;edit-individual-contact.js&lt;/code&gt;, and paste these lines into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'use strict';

const { submitEditCallback } = require('./submit-edit');

module.exports.register = (app) =&amp;gt; {
  app.view('edit-individual', submitEditCallback);
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This format is identical to the other listeners provided by the Slack Starter Kit. The only difference is that we are calling the &lt;code&gt;view&lt;/code&gt; method. We also need to register this listener alongside the others. Open up &lt;code&gt;apps/slack-salesforce-starter-app/listeners/index.js&lt;/code&gt;, and &lt;code&gt;require/register&lt;/code&gt; the new view listener:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const viewListener = require('./views');

module.exports.registerListeners = (app) =&amp;gt; {
  // ...
  viewListener.register(app);
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, in &lt;code&gt;apps/slack-salesforce-starter-app/listeners/views&lt;/code&gt;, create another file called &lt;code&gt;submit-edit.js&lt;/code&gt; and paste these lines into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'use strict';
const { editContactResponse, authorize_sf_prompt } = require('../../user-interface/modals');

const submitEditCallback = async ({ view, ack, client, context }) =&amp;gt; {
  try {
    await ack();
  } catch (error) {
    // eslint-disable-next-line no-console
    console.error(error);
  }

  if (context.hasAuthorized) {
    const contactId = view.blocks[0].element.action_id;
    const newDescription =
      view.state.values['description-block'][contactId].value;
    const conn = context.sfconnection;
    await conn.sobject('Contact').update({
      Id: contactId,
      Description: newDescription
    });

    await client.views.open({
      trigger_id: view.trigger_id,
      view: await editContactResponse(conn)
    });
  } else {
    // Get BotInfo
    const botInfo = await client.bots.info({ bot: context.botId });
    // Open a Modal with message to navigate to App Home for authorization
    await client.views.push({
      trigger_id: view.trigger_id,
       view: authorize_sf_prompt(context.teamId, botInfo.bot.app_id)
    });
  }
};

module.exports = { submitEditCallback };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s discuss those IDs that we set before. When Slack sends event payloads over to our app, it automatically generates an ID for every input element by default. That’s because Slack doesn’t know what the underlying data &lt;em&gt;is&lt;/em&gt;. It’s your responsibility to name the elements via action IDs. Slack uses these IDs as keys to populate the payload. When you receive Slack’s payload, you can use the keys you provided to parse the data the user entered.&lt;/p&gt;

&lt;p&gt;Now, if you go through the flow to edit your contact’s description, you’ll notice that the modal will correctly save. To verify that the data on the Salesforce side was updated, run &lt;code&gt;sfdx force:org:open&lt;/code&gt; in your terminal and navigate to the Contacts tab.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;The Slack Starter Kit has made it an absolute breeze to build a Slack App that listens to user events. Beyond that, though, it also makes interacting with Salesforce and Heroku an absolute pleasure. We’ve covered just about everything that the Starter Kit can do. If you’d like to learn more about how Slack and Salesforce can work together, check out our blog post on &lt;a href="https://developer.salesforce.com/blogs/2021/11/slack-and-salesforce-building-better-together" rel="noopener noreferrer"&gt;building better together&lt;/a&gt;. Also, the backend framework that interacts with Salesforce is the wonderful &lt;a href="https://jsforce.github.io/" rel="noopener noreferrer"&gt;JSforce&lt;/a&gt; project. Be sure to check out both its documentation and &lt;a href="https://developer.salesforce.com/docs/apis" rel="noopener noreferrer"&gt;the Salesforce API Reference&lt;/a&gt; to learn more about what you can build!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building a Slack App with Native SFDC Integration</title>
      <dc:creator>Michael Bogan</dc:creator>
      <pubDate>Tue, 05 Apr 2022 13:38:35 +0000</pubDate>
      <link>https://forem.com/salesforcedevs/building-a-slack-app-with-native-sfdc-integration-4m4m</link>
      <guid>https://forem.com/salesforcedevs/building-a-slack-app-with-native-sfdc-integration-4m4m</guid>
      <description>&lt;h3&gt;
  
  
  Building a Slack App: Designing a UI
&lt;/h3&gt;

&lt;p&gt;This post is a continuation of our series based on &lt;a href="https://www.youtube.com/playlist?list=PLgIMQe2PKPSKcl26YQoKCR7F3CwIc8kxi" rel="noopener noreferrer"&gt;a video series explaining how to build Slack Apps&lt;/a&gt; that integrate with Salesforce APIs. With the &lt;a href="https://github.com/developerforce/salesforce-slack-starter-kit" rel="noopener noreferrer"&gt;Slack Starter Kit&lt;/a&gt; from Salesforce, Slack app developers can offload common concerns like Salesforce authentication, directory hierarchies, reusable code, and deployments to Heroku. The end result is less time spent wrangling code and more time building features for your app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/salesforcedevs/building-a-slack-app-with-native-sfdc-integration-o94"&gt;In our last post&lt;/a&gt;, we familiarized ourselves with the starter kit and set up our development environment to build a basic Slack app. In this post, we’ll continue building on that Slack app, focusing on how the Slack Starter Kit makes it easy to develop two essential components of every Slack app:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Listening for user interactions&lt;/li&gt;
&lt;li&gt;Drawing a user interface.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Responding to events
&lt;/h3&gt;

&lt;p&gt;If you’ve ever developed a front-end web app using JavaScript or CSS, you’re probably familiar with the basic tenets of event handling. Events in this context are defined as the actions a user takes on your website. For example, a mouse pointer clicking on a link emits an &lt;code&gt;onclick&lt;/code&gt; event; a pointer linking over a piece of text emits &lt;code&gt;onhover&lt;/code&gt;; a keyboard keystroke emits &lt;code&gt;onkeypressed&lt;/code&gt;; and so on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahqj3m9moouphx6lbvfp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahqj3m9moouphx6lbvfp.jpg" alt="Photo by [Stephen Phillips - Hostreviews.co.uk](https://unsplash.com/@hostreviews?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText) on[ Unsplash](https://unsplash.com/s/photos/slack?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@hostreviews?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Stephen Phillips - Hostreviews.co.uk&lt;/a&gt; on&lt;a href="https://unsplash.com/s/photos/slack?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt; Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Slack, there’s a &lt;a href="https://api.slack.com/interactivity/handling" rel="noopener noreferrer"&gt;multitude of events&lt;/a&gt; you can listen to, ranging from individual events (when a message is posted or pinned) to global changes (when channel names or visibilities are changed) to the administrative (when an app is installed or a new emoji is uploaded). &lt;a href="https://api.slack.com/events?filter=Events" rel="noopener noreferrer"&gt;The full list of emitted events&lt;/a&gt; is truly impressive. The process for listening to an event is as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, your app needs to define a webhook URL. This is a path on your server which can receive POST requests from Slack.&lt;/li&gt;
&lt;li&gt;Next, in your App Manifest, you identify the events which you’d like to capture.&lt;/li&gt;
&lt;li&gt;When an event occurs in a workspace where your app is installed, a JSON payload is sent to your app. Every event has its own unique payload.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When your app receives the event from Slack, you can do anything you want: respond to the user, pop open a dialog to ask for more information, or simply store some information away in your database. There’s only one important point to remember: &lt;a href="https://api.slack.com/interactivity/handling#acknowledgment_response" rel="noopener noreferrer"&gt;you must respond to Slack within three seconds&lt;/a&gt;. This is to let Slack know that your app received its payload and is working on a response. You can do that work in the background—perhaps on a different thread, taking as long as you need—but only after you let Slack know everything is &lt;code&gt;200 OK&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s take a look at how event handling works in practice. Head on over to your App Manifest, and scroll down to the bottom of the page, near the &lt;code&gt;settings&lt;/code&gt; key. It should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;settings:
  event_subscriptions:
    request_url: https://&amp;lt;your-app-name&amp;gt;.herokuapp.com/slack/events
    bot_events:
      - app_home_opened
  interactivity:
    is_enabled: true
    request_url: https://&amp;lt;your-app-name&amp;gt;.herokuapp.com/slack/events
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This section identifies where Slack should send its payload once an event occurs. The &lt;code&gt;bot_events&lt;/code&gt; key already defines one event to listen to, &lt;a href="https://api.slack.com/events/app_home_opened" rel="noopener noreferrer"&gt;app_home_opened&lt;/a&gt;, which is triggered once your app is opened in Slack.&lt;/p&gt;

&lt;p&gt;Now, open up the local copy of your Starter Kit in your IDE; navigate to &lt;code&gt;apps/slack-salesforce-starter-app/listeners/events/app-home-opened.js&lt;/code&gt;. As we saw in the last post, the Starter Kit has an opinionated directory structure to resolve any ambiguities as to where modifications should be made. In this case, the events folder is responsible for defining all of our event responses, just like the shortcuts folder defined our slack command in the previous article. Search for the first occurrence of the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;client.views.publish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As the method name implies, this function uses a Slack SDK to call &lt;a href="https://api.slack.com/methods/views.publish" rel="noopener noreferrer"&gt;an API named views.publish&lt;/a&gt;. The main view for this event is created by a function called &lt;code&gt;authorization_success_screen&lt;/code&gt;, which can be found in &lt;code&gt;apps/slack-salesforce-starter-app/user-interface/app-home/auth-success.js&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Let’s make a few changes to these files. On line 16, add this line to fetch the timestamp of the event:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let event_ts = event.event_ts;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change both &lt;code&gt;authorization_success_screen&lt;/code&gt; calls to include this variable as a new argument:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;view: authorization_success_screen(
  currentuser.username,
  event_ts
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, open up &lt;code&gt;auth-success.js&lt;/code&gt;, change the method signature to include event_ts, and modify the displayed string to include this information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'use strict';

const { HomeTab, Blocks } = require('slack-block-builder');

const authorization_success_screen = (username, event_ts) =&amp;gt; {

  // preceding code remains unchanged

       Blocks.Section({
           text: `It's ${event_ts}, and you are successfully authenticated to Salesforce as ${username}.`
       })
   );
// continued code remains unchanged
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Commit this change, and deploy it to Heroku as before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git add .
$ git commit -m "Add event timestamp"
$ git push heroku main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the deployment is done, navigate to your app’s tab in the left-hand Slack menu. You should see a different string, which shows the timestamp of when you opened the app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a UI
&lt;/h3&gt;

&lt;p&gt;Responding to &lt;code&gt;app_home_opened&lt;/code&gt; can be a useful event to listen to whenever your app needs a centralized location to fetch data. Because of this, our next step will be to fetch data from Salesforce and present it in our Slack UI.&lt;/p&gt;

&lt;p&gt;To design and present layouts, Slack provides a system known as &lt;a href="https://api.slack.com/block-kit" rel="noopener noreferrer"&gt;Block Kit&lt;/a&gt;. Just as HTML is a markup language for the web, and Visualforce is a markup language for Salesforce, Block Kit is a markup language for Slack—except it uses JSON. For example, here’s what a button designed in Block Kit might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "type": "button",
  "text": {
    "type": "plain_text",
    "text": "Save"
  },
  "style": "primary"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Block Kit ensures that every Slack App has a consistent user interface. Because of this, there’s a limited set of UI elements that you can use. &lt;/p&gt;

&lt;p&gt;When working with Block Kit, you design your layout in JSON. Then, you use the Slack API to POST that JSON to a channel, tab, or direct message.&lt;/p&gt;

&lt;p&gt;We already have a system in place for listening to an event and presenting text, so let’s build on that foundation. Before we work in Slack, though, let’s add some data to our fresh Salesforce org. To launch your browser directly to your Salesforce org, you can run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sfdx force:org:open
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the Setup menu, search for the &lt;a href="https://help.salesforce.com/s/articleView?id=sf.importing.htm&amp;amp;type=5" rel="noopener noreferrer"&gt;Data Import Wizard&lt;/a&gt;. Choose CSV as your data type, then copy-paste the following lines into a new file called data.csv:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Contact First Name, Contact Last Name, Contact Description
Arden, Isabela, Lorem ipsum dolor sit amet
Rowina, Arti, Proin a est sit amet quam varius efficitur.
Aislin, Oedipus, Praesent et euismod sem
Sindri, Filbert, Proin facilisis sit amet libero vulputate sodales
Cyril, Gratien, Nulla at massa eu turpis venenatis egestas
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Upload this file as your data source. Importing this data should only take a minute or two. You can navigate to the Contacts tab if you’d like to be extra sure and verify that these names exist.&lt;/p&gt;

&lt;p&gt;Next, we’re going to make changes similar to the ones we made before when adding the event timestamp. A variable called &lt;code&gt;conn&lt;/code&gt; is available to use, which serves as a connection to Salesforce. We can use it to query for Contact data, which is what we’re interested in displaying in Slack.&lt;/p&gt;

&lt;p&gt;Navigate to &lt;code&gt;apps/slack-salesforce-starter-app/user-interface/app-home/auth-success.js&lt;/code&gt;. We’ll &lt;strong&gt;await&lt;/strong&gt; the resolving of the promise in this function, and we’ll include the existing &lt;code&gt;conn&lt;/code&gt;variable as a new argument:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;view: await authorization_success_screen(
                   currentuser.username,
                   event_ts,
                   conn
               )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And once again, open up &lt;code&gt;auth-success.js&lt;/code&gt; and change the method signature to include &lt;code&gt;conn&lt;/code&gt;. You also need to declare the function as &lt;strong&gt;async&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'use strict';

const { HomeTab, Blocks } = require('slack-block-builder');

const authorization_success_screen = async (username, event_ts, conn) =&amp;gt; {

// continued code remains unchanged for now
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, with this connection, we’re going to issue a query to Salesforce to get our newly created Contact records. We’ll then display them in the Home tab of our Slack app. &lt;/p&gt;

&lt;p&gt;Since we already have a connection to Salesforce, fetching the data is as easy as issuing an SQL query. Place this code right before the place where the &lt;code&gt;homeTab&lt;/code&gt; variable is defined:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const result = await conn.query(
  `SELECT Name, Description FROM Contact`
);
let records = result.records;

let fields = records.map((record) =&amp;gt; {
  return `*${record.Name}*: ${record.Description}`;
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, at the bottom of the blocks method call, add these lines, which represent the UI you’re constructing out of Block Kit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Blocks.Section({
  text: `It's ${event_ts}, and you are successfully authenticated to Salesforce as ${username}.`
}),
Blocks.Header({ text: 'Contacts' }),
Blocks.Divider(),
Blocks.Section({
  text: fields.join('\n')
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you navigate back to your Slack App’s Home tab, you’ll see your organization’s list of contacts!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe0h9aadq69pq9hv7n6e4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe0h9aadq69pq9hv7n6e4.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Learning more
&lt;/h3&gt;

&lt;p&gt;We’ve built a Slack app that fetches data from Salesforce and presents that data in a UI using a framework called Block Kit. In our next post, we’ll send data from Slack &lt;em&gt;back&lt;/em&gt; to Salesforce!&lt;/p&gt;

&lt;p&gt;Before then, you might want to familiarize yourself with how Block Kit works. Slack provides the &lt;a href="https://www.google.com/url?q=https://app.slack.com/block-kit-builder&amp;amp;sa=D&amp;amp;source=docs&amp;amp;ust=1642619847313479&amp;amp;usg=AOvVaw2XYSjzDa5SC3NPKL7hyOgo" rel="noopener noreferrer"&gt;Block Kit Builder&lt;/a&gt;, which is a playground for their UI elements. Under the hood, the Slack Starter Kit uses the &lt;a href="https://www.blockbuilder.dev/" rel="noopener noreferrer"&gt;Block Builder&lt;/a&gt; to simplify the UI assemblage we worked with.&lt;/p&gt;

</description>
      <category>slack</category>
      <category>integration</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
