Creating Custom API Endpoints in Salesforce with Apex
Tags: salesforce, webdev, api
Published: 2023-01-31 21:41:53

## Part One: SOAP-based APIs

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.

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.


### A Brief Introduction to SOAP

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:


```
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:enterprise.soap.sforce.com">
   <soapenv:Header>
      <urn:LoginScopeHeader>
         <urn:organizationId>12345</urn:organizationId>
         <urn:portalId>abcde</urn:portalId>
      </urn:LoginScopeHeader>
   </soapenv:Header>
   <soapenv:Body>
      <urn:login>
         <urn:username>johndoe</urn:username>
         <urn:password>mypassword</urn:password>
      </urn:login>
   </soapenv:Body>
</soapenv:Envelope>
```


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.

Though the message can be sent using SMTP or HTTP, SOAP messages should ideally be sent with HTTPS, with either simple or mutual authentication.

SOAP is mostly used to create an _API_ or _web service_ 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.


#### The relationship between SOAP and WSDL

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.


### How to Create a SOAP-based API in Salesforce

Now that we have covered the fundamentals, let’s begin implementing our SOAP API.


#### Set up a Salesforce org

First, you need a Salesforce org. Salesforce provides free Developer edition orgs, and signing up for one is straightforward. Go to the [sign-up page](https://developer.salesforce.com/signup) and provide your details. You should receive an invite link within a few minutes.


#### Creating a global class with Apex

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 `webservice` keyword and `static` definition modifier for every method we want to expose. Adding the `webservice` keyword automatically gives global access to the method it is added to.

In order to create this class in our shiny, new org, we go to **Setup**, and then find **Custom Code -> Apex Classes**. In the table listing existing classes, we click **New**.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0bvrzvvziqbsolyk7vf9.png)

To learn more about Apex and how to create classes, check out this [Trailhead page](https://trailhead.salesforce.com/en/content/learn/modules/apex_database?trail_id=force_com_dev_beginner) and the [Apex documentation](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_understanding.htm).

Below, we have a sample `ContactAPI` class that exposes a `createContact` method used to create new Contact records in Salesforce. The method accepts a Contact’s details as parameters and inserts a new Contact record.

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.


```
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();
    }
  }
}
```


Next, we click **Save**.

That’s it! Our web service is ready for operations. We have successfully created a SOAP API in Salesforce.


#### Generate the WSDL

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 `createContact` method.

Salesforce provides two out-of-the-box WSDL files to integrate client applications with Salesforce:



1. The **Enterprise WSDL** 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).
2. The **Partner WSDL** is optimized for use with many Salesforce orgs. It’s loosely typed, and it doesn’t change based on an org’s specific configuration.

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.

After creating our `ContactAPI` class, we see it show up in the list of Apex classes. In the row for ContactAPI, we click on the **WSDL** link to generate a WSDL.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d4z4wuw98tgbhf28kfnp.png)

This will open a new tab with our WSDL, which will look similar to this:


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uawsoq5p45d4a9l3yb65.png)

We save this file to our local machine, naming it `contactAPI-wsdl.xml`.

To generate the Enterprise WSDL for our Salesforce org, we go to the Setup page, and then find **Integrations -> API**. Then, we click on **Generate Enterprise WSDL**.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jvnptznbiai8dx4avm29.png)

Your generated WSDL should look similar to this:


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/elrqs8h92sa0jf2yfrjp.png)

We save this file to our local machine, naming it `enterprise_wsdl.xml`.

To generate the WSDL for your Apex Class:


1. Go to the Setup page.
2. Type and select Apex Classes.
3. Find your class. In our case it’s ContactAPI. Click on the name.
4. On the next screen you will have the class page where you have the **Generate WSDL** button.


### Test and validate

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.


#### Consume the API

For the purpose of this demo, we’ll use [SoapUI](https://www.soapui.org/tools/soapui/), 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 `enterprise_wsdl.xml` file as the initial WSDL.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sd5hosq6uama5oa7kg5j.png)  

We now have all available Salesforce SoapBindings listed on the left.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yirwq7v1b0e0acrrifq8.jpg)

First, we need to authenticate our web application to Salesforce. We do this by using the `login` call and providing our login credentials.


#### Use a security token

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


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uecknxvifhpqmpw0al8w.jpg)

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i5w9nlglp0qj9ri76ay3.jpg)

#### Log in with the token and credentials


With our token in hand, we select the `login` 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 [https://login.salesforce.com](https://login.salesforce.com). If you are testing this in a Sandbox, then the URL should be [https://test.salesforce.com](https://login.salesforce.com). Ideally, the generated Enterprise WSDL will already have the correct URL.

For demo testing, we can comment out all other parameters except username and password.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vd3hhook1q2p0m8qb0nc.jpg)

We press the green **Play** button on the top left of the window to send the request. We receive a response that looks like this:


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j92rxaazirq5ojwmimth.jpg)

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


#### Call our SOAP API

Let’s call our `ContactAPI` class and create a Contact in Salesforce. To do this, we need to add our ContactAPI WSDL file, `contactAPI-wsdl.xml`, to our project. We right-click on the project folder and select **Add WSDL**.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1geauuvlvf2vuqsvyi0f.jpg)

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


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tvgw99fht83k01ifpl5w.jpg)

After removing the unwanted nodes, we provide the `sessionId` that we copied from our login call earlier. Then, in the `Body` section of the envelope, we provide the details of the contact to be created. 


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vbic2obppa3uvco4nmik.jpg)

We press the green **Play** button on the top left. Vóila! We have successfully created a Contact in Salesforce and received its `Id`.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wfanfi0sx8ygg94169f3.jpg)

#### Check Salesforce org for new contact

Let’s look inside our Salesforce org to see if everything was created correctly. We go to our Salesforce org, click the **App Launcher** waffle icon on the left and then type `Contact` in the search bar to find and select **Contacts**. 


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gf9hc8bhd4q2wkcwwayq.jpg)

By default, we land on the Contacts page with the _Recently Viewed Contacts_ list view. Click the down arrow and select either **All Contacts** or **My Contacts** list view. 


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2werc53jqvr9gciidf0r.jpg)

**Note**: 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.

We type the name of our newly created contact in the list search box on the right, and we see our new contact!


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/64zqi5wwn5wyw8y4vskp.jpg)

When we click on the Contact name, we end up on the Details page for that Contact.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/csuxiky7l5c7kcb2yhox.jpg)

#### Call our SOAP API with invalid data

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 `.` in the email address with a `,` instead.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fk161dpn5o7j739h94z9.jpg)

This time, we receive an _error message_ (as we programmed in the API). 


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gjnwac0mokk8hft7lbp0.jpg)

The entire error message reads like this:


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


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.


### Unit Testing

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.

Let’s create some unit tests for our web service.


```
@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');
  }
}
```


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 [Trailhead on unit testing in Apex](https://trailhead.salesforce.com/en/content/learn/modules/apex_testing/apex_testing_intro).


### Conclusion

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. 

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.

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.