Skip to content

Latest commit

 

History

History
494 lines (415 loc) · 19.6 KB

README.md

File metadata and controls

494 lines (415 loc) · 19.6 KB

node-salesforce-connection

Build Status

node-salesforce-connection is a minimal library for connecting to Salesforce from Node.js. It provides an absolute minimal wrapper that allows you to call any Salesforce API. It tries hard to not get in the way between you and Salesforce. It has no dependencies.

  1. Introduction
  2. Logging in
  3. REST
  4. SOAP
  5. Error handling

This library works in Node.js. There is an almost identical library that works in browsers, but that it not yet published as a stand-alone package.

Introduction

This documentation explains how the node-salesforce-connection library works, but you need to read this in combination with the official Salesforce API documentation, since this document does not explain how the Salesforce APIs themselves work.

Create your own project using npm init and then add this library as a dependency using npm install node-salesforce-connection --save.

Use it like this in your JS file:

let SalesforceConnection = require("node-salesforce-connection");

(async () => {

  let sfConn = new SalesforceConnection();

  await sfConn.soapLogin({
    hostname: "login.salesforce.com",
    apiVersion: "39.0",
    username: "[email protected]",
    password: "MyPasswordMySecurityToken",
  });

  let recentAccounts = await sfConn.rest("/services/data/v39.0/query/?q="
    + encodeURIComponent("select Id, Name from Account where CreatedDate = LAST_WEEK"));

  for (let account of recentAccounts.records) {
    console.log("Account " + account.Name + " was created recently.");
  }

})().catch(ex => console.error(ex.stack));

The examples use the JavaScript await keyword. This assumes the examples are placed in an async function like the one above. You don't have to use async functions, if you prefer using the traditional promise.then(handler) syntax.

Logging in

The first thing you need to do is log in to Salesforce.

Logging in using the SOAP API

You can use the soapLogin function to log in using a username and password:

let sfConn = new SalesforceConnection();

await sfConn.soapLogin({
  hostname: "login.salesforce.com",
  apiVersion: "39.0",
  username: "[email protected]",
  password: "MyPasswordMySecurityToken",
});

The function calls the SOAP login method.

The function returns a promise that behaves identically to the promise returned from the soap function. When the promise resolves successfully, sfConn will be ready to use.

Logging in using OAuth

You can log in using OAuth. Here is an example of the Username-Password OAuth Authentication Flow:

let sfConn = new SalesforceConnection();
let tokenRequest = {
  grant_type: "password",
  client_id: "MyConsumerKey",
  client_secret: "MyConsumerSecret",
  username: "[email protected]",
  password: "MyPasswordMySecurityToken",
};
let hostname = "login.salesforce.com";
await sfConn.oauthToken(hostname, tokenRequest);

The function makes a POST request to /services/oauth2/token.

The function returns a promise that behaves identically to the promise returned from the rest function. When the promise resolves successfully, sfConn will be ready to use.

Use the oauthToken function with the Web Server, Username-Password and Refresh Token OAuth authentication flows. Use the manual approach described below for the User-Agent flow.

Logging in manually

If SOAP or OAuth login does not work for you, you can manually initialize the connection with your own questionably obtained session information:

let sfConn = new SalesforceConnection();
sfConn.instanceHostname = "na1.salesforce.com";
sfConn.sessionId = ".....";

REST

The best way to make API calls is using any Salesforce REST API. Use for example the Bulk, Chatter, REST, Tooling or Reports and Dashboards API.

Example using query:

let recentAccounts = await sfConn.rest("/services/data/v39.0/query/?q="
  + encodeURIComponent("select Id, Name from Account where CreatedDate = LAST_WEEK"));

for (let account of recentAccounts.records) {
  console.log("Account " + account.Name + " was created recently.");
}

Example creating a record:

let myNewAccount = {Name: "test"};

let result = await sfConn.rest("/services/data/v39.0/sobjects/Account",
  {method: "POST", body: myNewAccount});

console.log("Created Account with ID " + result.id
  + (result.success ? " successfully." : " failed."));

The rest function accepts the following parameters:

sfConn.rest(path, {method, api, body, bodyType, headers, responseType});
Parameter Type Default Description
path string (required) A path relative URL to request. E.g. /path/to/resource?param=value&other=two.
method string "GET" The HTTP method to use.
api string "normal" The type of REST API.
"normal"
Pass the Session ID in the format expected by most Salesforce REST APIs.
"bulk"
Pass the Session ID in the format expected by the Bulk API.
body Depends on bodyType (none) Used as the HTTP request body, formatted according to the bodyType parameter.
bodyType string "json" Indicates the type of the body parameter.
"json"
The body parameter is interpreted as a JavaScript object that will be converted to JSON.
"urlencoded"
The body parameter is interpreted as a JavaScript object that will be converted into URL encoded form data.
"raw"
The body parameter is interpreted as a string or Node.js Buffer and used directly in the HTTP request. Remember to set the Content-Type header.
headers object (none) A JavaScript object of additional HTTP headers. Example: {"Sforce-Query-Options": "batchSize=1000"}.
responseType string "json" Indicates the type of the HTTP response body.
"json"
The HTTP response body will be parsed as JSON.
"raw"
The HTTP response body will not be parsed, and a SalesforceResponse object will be returned, containing the HTTP response headers, response status and binary response body. This allows you to read binary data or work around bugs in the Salesforce API. Remember to set the Accept header if applicable.

The rest function returns a promise.

If the request succeeds, the promise will resolve with the HTTP response parsed according to the responseType parameter.

If the request fails because Salesforce returned an error response (such as HTTP 400), the promise will reject with an Error with these properties:

Property name Type Value
name string "SalesforceRestError"
message string A descriptive error message. A text version of the detail property, or the HTTP status message if we did not receive a HTTP response body.
detail Depends on responseType The HTTP response body parsed according to the responseType input parameter.
response SalesforceResponse An object containing the HTTP response headers, response status and binary response body.

If the request fails because Node.js could not connect to Salesforce, the promise will reject with a Node.js System Error.

A SalesforceResponse object has these properties:

Property name Type Value
headers object The HTTP response headers.
statusCode number The 3-digit HTTP response status code. E.g. 404.
statusMessage string The HTTP response status message (reason phrase). E.g. OK or Internal Server Error.
body Node.js Buffer The HTTP response body. Call response.body.toString() to get the response body as text.

SOAP

If the functionality you are looking for is not available via any of the Salesforce REST APIs, you might be able to find a Salesforce SOAP API that does what you need. However, this being JavaScript, you should probably avoid SOAP when possible.

Example using upsert:

let enterpriseWsdl = sfConn.wsdl("39.0", "Enterprise");

let contacts = [
  {$type: "Contact", FirstName: "John", LastName: "Smith", Email: "[email protected]"},
  {$type: "Contact", FirstName: "Jane", LastName: "Smith", Email: "[email protected]"},
];

let upsertResults = await sfConn.soap(enterpriseWsdl, "upsert",
  {externalIdFieldName: "Email", sObjects: contacts});

for (let r of sfConn.asArray(upsertResults)) {
  console.log((r.created == "true" ? "Created" : "Updated")
    + " Contact with ID " + r.id + " "
    + (r.success == "true" ? "successfully" : "failed") + ".");
}

Before you make a SOAP API request, you need a WSDL, which you get using the wsdl function. Well, you don't actually get the full WSDL file. You only get the absolute minimum information needed to make SOAP API calls from JavaScript.

The wsdl function accepts the following parameters:

sfConn.wsdl(apiVersion, apiName);
Parameter Type Default Description
apiVersion string (required) The Salesforce API version you want to use.
apiName string (required) The Salesforce SOAP API you want to use. Supported values are "Enterprise", "Partner", "Apex", "Metadata" and "Tooling".

The wsdl function returns an object containing information from the WSDL that is needed to make SOAP requests.

Alternatively, you can call the wsdl function with only one parameter to get an object with all the WSDL's we know:

let wsdlSet = sfConn.wsdl(apiVersion);
let myWsdl = wsdlSet[apiName];

With the WSDL information at hand, you can make your SOAP API request using the soap function.

The soap function accepts the following parameters:

sfConn.soap(wsdl, method, args, {headers});
Parameter Type Default Description
wsdl object (required) An object containing information from the WSDL that is needed to make SOAP requests. You can either obtain this object by calling sfConn.wsdl or create the object manually.
method string (required) The SOAP method to be called, as found in the Salesforce documentation. The example above uses upsert.
args object (required) The arguments to the called SOAP method, as found in the Salesforce documentation. Pass an object where each property corresponds to a SOAP method argument by name. Pass an empty object if the method does not require any arguments.
headers object (none) An optional object with Salesforce SOAP headers. Pass an object where each property corresponds to a SOAP header by name. The Salesforce Session ID is automatically added here, so you don't have to. Example: {AllOrNoneHeader: {allOrNone: false}, EmailHeader: {triggerAutoResponseEmail: true}}.

sObjects are a bit special in the SOAP API. You always need to specify the type of an sObject. In the Partner WSDL, use the type property (Example: {type: "Account", Name: "Example"}). In the Enterprise WSDL and others, use the $type property (Example: {$type: "Account", Name: "Example"}).

The soap function returns a promise.

If the request succeeds, the promise will resolve with the SOAP method's return value.

When you get the return value from a SOAP API call, you won't get the precise type of the returned data, since that information is only available in the WSDL. You have to convert the type yourself using these rules:

  • If you expect a string, you will get a string.
  • If you expect a number, you will get a string. Convert it to a number using for example let myNumber = Number(myValue).
  • If you expect a boolean, you will get a string. Convert it to a boolean using for example let myBoolean = myValue == "true".
  • If you expect null, you will get null.
  • If you expect an object, you will get an object.
  • If you expect an array, you will get different things if your array has zero, one or more elements. Convert it to an array using the asArray utility function, for example let myArray = sfConn.asArray(mvValue);.

If the request fails because Salesforce returned a SOAP fault, the promise will reject with an Error with these properties:

Property name Type Value
name string "SalesforceSoapError"
message string A descriptive error message. The faultstring part of the SOAP fault message returned by Salesforce.
detail object The SOAP fault message returned by Salesforce.
response SalesforceResponse An object containing the HTTP response headers, response status and binary response body.

If the request fails because Node.js could not connect to Salesforce, the promise will reject with a Node.js System Error.

Error handling

The rest and soap functions return promises you can use in error handling like any other promise.

Example:

try {
  let result = await sfConn.rest("/services/data/v39.0/query/?q="
    + encodeURIComponent("select Id from UnknownObject"));
  console.log(result);
} catch (ex) {
  if (ex.name == "SalesforceRestError") {
    console.log("Salesforce returned an error: " + ex.message);
  } else if (ex.syscall == "getaddrinfo") {
    console.log("Could not make request. Are you offline?");
  } else {
    throw ex; // Unknown type of error
  }
}

Same example with an older JavaScript syntax:

sfConn.rest("/services/data/v39.0/query/?q="
  + encodeURIComponent("select Id from UnknownObject"))
.then(function(result) {
  console.log(result);
}, function(ex) {
  if (ex.name == "SalesforceRestError") {
    console.log("Salesforce returned an error: " + ex.message);
  } else if (ex.syscall == "getaddrinfo") {
    console.log("Could not make request. Are you offline?");
  } else {
    throw ex; // Unknown type of error
  }
});

History

Before I made this library, I used JSForce. It is a nice library, but I ran into a few bugs, and I needed a specific new Salesforce APIs that was not yet added to JSForce at the time. So I made this library instead, which aims to give you access to any current or future Salesforce API without needing to update the library. It aims to provide 95% of the convenience for 5% of the size/complexity.