Serverless & Salesforce 101

This post is also available in: Español (Spanish)

Salesforce platform has multiple ways to allow admins and developers do their work. Most often you use visual tools to perform the task, like Process Builder or Flows, and APEX code as a last resort. But what if I tell you that there are some other things, like using Salesforce REST API and Lambda for our functions?

This way has a clear advantage, external development teams that feel confident working with more than standard languages like Javascript, Java, Python or C# (but never used APEX) can help Salesforce teams developing new features. But in the other hand, we should keep in mind that Salesforce APIs are limited and metered, so we will be charged for each call.

Depending on which license are we using, on production instances we will have concurrency limit (About 25 req/s), and an allocation per day (Usually it depends on how many licenses you have)

Functions as a Service

We will be using the serverless framework, in an effort to avoid vendor-lock, and Amazon Web Services to host our cloud functions. Serverless allows you to deploy your app to other multiple providers, like Google Cloud, Azure, OpenWhisk or Kubeless.

Also we have lots of plugins in order to integrate other services, or even using webpack or Typescript to code your functions.

Let’s get our hands dirty

As a first step, we should have an active AWS account, so log in using the console, and go to IAM section, as we will need a secret and an access token for our command line.

We will not dive deeper about configuring roles or policies, so we will be using AdministratorAccess for this example. Remember to use more fine grained permissions in production, once you know the scope of your serverless application.

Once we download our access token and secret, store them in a safe place, and point your browser to the download page in the serverless site. Depending on which OS are we using, we have multiple choices to download the serverless binary. The simpliest way is using npm:

$ sudo npm install -g serverless

After this, the serverless version command will output something like this:

$ serverless -v

Framework Core: 1.77.1
Plugin: 3.6.18
SDK: 2.3.1
Components: 2.33.1

Configuring AWS keys

Open the CSV file containing your access key and secret for AWS, and use them to configure your serverless environment, using the following command. Replace key and secret parameters with the ones you downloaded from AWS

$ serverless config credentials --provider aws --key AKIAIOSFODNN7EXAMPLE --secret wJalrXUtnFEMIK7MDENGbPxRfiCYEXAMPLEKEY

After completing this step, we are ready to start coding our first function using the framework and Javascript.

The serverless.yml file

Okay, all the functions and events should be defined in the serverless.yml file, as the framework will use this info to deploy our application to the cloud provider. So let’s create it with the default details like this one, and one example function. It will be listening to the /contact/{id} path, and will use the get function in the file src/contact.js:

provider:
    name: aws
    runtime: nodejs12.x
    region: eu-west-1

functions:
    hi:
        name: getContact
        handler: src/contact.get
        events:
            -
                http:
                    path: '/contact/{id}'
                    method: get

It’s time to write some Javascript code

Now the funny part, start coding!

Remember we are using nodejs environment in AWS, so we can use its package manager (npm) to manage our application dependencies. In this case, we will rely on the jsforce package to manage our connection to Salesforce, so type the following in the root folder, where you put the serverless.yml file:

$ npm init

Now complete the name of your project, and install the jsfoce library:

$ npm install jsforce --save

After this, create a new file called src/contact.js, import the jsforce library, and write the function:

const jsforce = require('jsforce');

module.exports.get = async function (event) {
    // Code...
}

OK! We will now create the Salesforce connection using the jsforce library. You can use environment variables to store the user/password, or even use other way to login to salesforce using jsforce Connection.

const jsforce = require('jsforce');

module.exports.get = async function (event) {
    const connection = new jsforce.Connection({});
    await connection.login(process.env.SF_USER, process.env.SF_PASSWORD);
}

We now have an available connection variable, containing a Jsforce instance, which we can ask for SObjects, make SOQL queries, etc. As sure you assumed, we are going to get info about a Contact given its Salesforce ID, so we will be using the Retrieve operation, defined in the CRUD section. But first we need its ID, that should be in a path variable. The event variable has multiple keys, one of them is pathParameters, which will contain all the parameters defined in the URL, so we will look for our id parameter and ask Salesforce.

const jsforce = require('jsforce');

module.exports.get = async function (event) {
    let contact;
    const connection = new jsforce.Connection({});
    await connection.login(process.env.SF_USER, process.env.SF_PASSWORD);

    if (!('id' in event.pathParameters) || event.pathParameters.id === null) {
        return {
            statusCode: 500, // or whatever status code you want
            body: JSON.stringify({
                error: 'User id not found in parameters'
            })
        };
    }

    try {
        contact = await conn.sobject("Contact").retrieve(event.pathParameters.id);
    } catch (error) {
        return {
            statusCode: 404,
            body: JSON.stringify({
                error: 'User not found in Salesforce'
            })
        };
    }
}

Sure you noticed we returned an object with statusCode and body key. This is because of serverless is using Lambda Proxy Integration, which uses the statusCode key to return a valid HTTP Status Code. so if everything worked, we should have a valid Contact record in the contact variable, let’s print its name.

const jsforce = require('jsforce');

module.exports.get = async function (event) {
    let contact;
    const connection = new jsforce.Connection({});
    await connection.login(process.env.SF_USER, process.env.SF_PASSWORD);

    if (!('id' in event.pathParameters) || event.pathParameters.id === null) {
        return {
            statusCode: 500, // or whatever status code you want
            body: JSON.stringify({
                error: 'User id not found in parameters'
            })
        };
    }

    try {
        contact = await conn.sobject("Contact").retrieve(event.pathParameters.id);
    } catch (error) {
        return {
            statusCode: 404,
            body: JSON.stringify({
                error: 'User not found in Salesforce'
            })
        };
    }

    return {
        statusCode: 200,
        body: JSON.stringify({
            name: contact.FirstName
        })
    };
}

Woah! Everything looks nice, so we will deploy our app to AWS. Open again your terminal and type:

$ serverless deploy

Now you will see how serverless prepares your CloudFormation template, uploads your function to an S3 bucket, and prepares it to go live.

Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service getContact.zip file to S3 (7.47 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
endpoints:
  GET - https://8a51ra4jcd.execute-api.eu-west-1.amazonaws.com/v1/contact/{id}

Now our service is live! We can try it using one of our Salesforce ID, example:

https://8a51ra4jcd.execute-api.eu-west-1.amazonaws.com/v1/contact/5003000000D8cuI
{
    "name": "Jorge"
}

In future chapters we will refactor this code and add more functions and plugins, like delaying a task using SQS, or sending an e-mail if something went wrong. You have all the code in this example repository.

Happy coding!