node-salesforce
Salesforce API Connection Library for Node.js Applications
Abstract
Node-salesforce, which is designed to be a wrapper of Salesforce REST API in Node.js, enables Salesforce application development in event-driven style. It capsulates the access to REST API end point in asynchronous JavaScript function call. You can use both OAuth2 authorization scheme and SOAP API login for API authentication.
Install
If you are using node-salesforce as an API library in your Node.js project :
$ npm install node-salesforce
If you want to utilize node-salesforce REPL (interactive API console) in tty:
$ npm install node-salesforce -g
If you want to get the latest from GitHub :
$ git clone git://github.com/stomita/node-salesforce.git $ cd node-salesforce $ npm link
API Usage
Connection
Username and Password Login
When you have Salesforce username and password (and maybe security token if required), you can use Connection#login(username, password)
to establish connection to Salesforce.
By default, it uses SOAP login API (so no OAuth2 client information is required).
var sf = ;var conn = // you can change loginUrl to connect to sandbox or prerelease env. // loginUrl : 'https://test.salesforce.com';conn;
Username and Password Login (OAuth2 Resource Owner Password Credential)
When OAuth2 client information is given, Connection#login(username, password)
uses OAuth2 Resource Owner Password Credential flow to login to Salesforce.
var sf = ;var conn = oauth2 : // you can change loginUrl to connect to sandbox or prerelease env. // loginUrl : 'https://test.salesforce.com', clientId : '<your Salesforce OAuth2 client ID is here>' clientSecret : '<your Salesforce OAuth2 client secret is here>' redirectUri : '<callback URI is here>' ;conn;
Session ID
If Salesforce session ID and its server URL information is passed from Salesforce (from 'Custom Link' or something), you can pass it to constructor.
var sf = ;var conn = serverUrl : '<your Salesforce server URL (e.g. https://na1.salesforce.com) is here>' sessionId : '<your Salesforce session ID is here>';
Access Token
After the login API call or OAuth2 authorization, you can get Salesforce access token and its instance URL. Next time you can use them to establish connection.
var sf = ;var conn = instanceUrl : '<your Salesforce server URL (e.g. https://na1.salesforce.com) is here>' accessToken : '<your Salesforrce OAuth2 access token is here>';
Access Token with Refresh Token
If refresh token is given in constructor, the connection will automatically refresh access token when it has expired
NOTE: Refresh token is only available for OAuth2 authorization code flow.
var sf = ;var conn = oauth2 : clientId : '<your Salesforce OAuth2 client ID is here>' clientSecret : '<your Salesforce OAuth2 client secret is here>' redirectUri : '<your Salesforce OAuth2 redirect URI is here>' instanceUrl : '<your Salesforce server URL (e.g. https://na1.salesforce.com) is here>' accessToken : '<your Salesforrce OAuth2 access token is here>' refreshToken : '<your Salesforce OAuth2 refresh token is here>';conn;
Logout
Connection#logout()
to logout from server and invalidate current session. Currently this method only works for SOAP API session.
var sf = ;var conn = sessionId : '<session id to logout>' serverUrl : '<your Salesforce Server url to logout is here>';conn;
OAuth2 Authorization
(Following examples are assuming running on express.js framework.)
Authorization Request
First, you should redirect user to Salesforce page to get authorized. You can get Salesforce authorization page URL by OAuth2#getAuthorizationUrl(options)
.
var sf = ;//// OAuth2 client information can be shared with multiple connections.//var oauth2 = // you can change loginUrl to connect to sandbox or prerelease env. // loginUrl : 'https://test.salesforce.com', clientId : '<your Salesforce OAuth2 client ID is here>' clientSecret : '<your Salesforce OAuth2 client secret is here>' redirectUri : '<callback URI is here>';//// Get authz url and redirect to it.//app;
Access Token Request
After the acceptance of authorization request, your app is callbacked from Salesforce with authorization code in URL parameter. Pass the code to Connection#authorize(code)
and get access token.
//// Pass received authz code and get access token//app;
Query
Using SOQL
By using Connection#query(soql)
, you can achieve very basic SOQL query to fetch Salesforce records.
var records = ;conn;
Callback Style
There are two ways to retrieve the result records.
As we have seen above, our package provides widely-used callback style API call for query execution. It returns one API call result in its callback.
var records = ;conn;
Event-Driven Style
When a query is executed, it emits "record" event for each fetched record. By listening the event you can collect fetched records.
If you want to fetch records exceeding the limit number of returning records per one query, you can use autoFetch
option in Query#execute(options)
(or its synonym Query#exec(options)
, Query#run(options)
) method. It is recommended to use maxFetch
option also, if you have no idea how large the query result will become.
When query is completed, end
event will be fired. The error
event occurs something wrong when doing query.
var records = ;conn ; // synonym of Query#execute();
Using Query Method-Chain
Basic Method Chaining
By using SObject#find(conditions, fields)
, you can do query in JSON-based condition expression (like MongoDB). By chaining other query construction methods, you can create a query programatically.
//// Following query is equivalent to this SOQL//// "SELECT Id, Name, CreatedDate FROM Contact// WHERE LastName LIKE 'A%' AND CreatedDate >= YESTERDAY AND Account.Name = 'Sony, Inc.'// ORDER BY CreatedDate DESC, Name ASC// LIMIT 5 OFFSET 10"//conn ;
Another representation of the query above.
conn // if "-" is prefixed to field name, considered as descending. ;
Wildcard Fields
When fields
argument is omitted in SObject#find(conditions, fields)
call, it will implicitly describe current SObject fields before the query (lookup cached result first, if available) and then fetch all fields defined in the SObject.
NOTE: In the version less than 0.6, it fetches only Id
field if fields
argument is omitted.
conn // "fields" argument is omitted ;
The above query is equivalent to:
conn // fields in asterisk, means wildcard. ;
Query can also be represented in more SQL-like verbs - SObject#select(fields)
, Query#where(conditions)
, Query#orderby(sort, dir)
, and Query#offset(num)
.
conn // asterisk means all fields in specified level are target. // conditions in raw SOQL where clause. // synonym of "skip" ;
You can also include child relationship records into query result by calling Query#include(childRelName)
. After Query#include(childRelName)
call, it enters into the context of child query. In child query context, query construction call is applied to the child query. Use SubQuery#end()
to recover from the child context.
//// Following query is equivalent to this SOQL//// "SELECT Id, FirstName, LastName, ..., // Account.Id, Acount.Name, ...,// (SELECT Id, Subject, … FROM Cases// WHERE Status = 'New' AND OwnerId = :conn.userInfo.id// ORDER BY CreatedDate DESC)// FROM Contact// WHERE CreatedDate = TODAY// LIMIT 10 OFFSET 20"//conn // include child relationship records in query result. // after include() call, entering into the context of child query. // be sure to call end() to exit child query context ;
CRUD Operation
We support basic "CRUD" operation for records. It also supports multiple record manipulation, but it consumes one API request per record. Be careful for the API quota.
Retrieve
SObject#retrieve(id)
fetches a record or records specified by id(s) in first argument.
////conn;////conn;
Create
SObject#create(record)
(or its synonym SObject#insert(record)
) creates a record or records given in first argument.
////conn;////conn;
Update
SObject#update(record)
updates a record or records given in first argument.
////conn;////conn;
Delete
SObject#destroy(id)
(or its synonym SObject#del(id)
, SObject#delete(id)
) deletes a record or records given in first argument.
////conn;////conn;
Upsert
SObject#upsert(record, extIdField)
will upsert a record or records given in first argument. External ID field name must be specified in second argument.
////conn;////conn;
Metadata Description
Metadata description API for Salesforce object.
Describe SObject
You can use SObject#describe()
to fetch SObject metadata,
conn;
or can use Connection#describe(sobjectType)
alternatively.
conn;
Describe Global
SObject#describeGlobal()
returns all SObject information registered in Salesforce (without detail information like fields, childRelationships).
conn;
Cache
Each description API has "cached" version with suffix of $
(coming from similar pronounce "cash"), which keeps the API call result for later use.
//// First lookup local cache, and then call remote API if cache doesn't exist.//conn;//// If you can assume it should have already cached the result,// you can use synchronous call to access the result;//var meta = conn;console;console;// ...
Cache clearance should be done explicitly by developers.
//// Delete cache of 'Account' SObject description result//conndescribe$clear;//// Delete cache of global sobject description//conndescribeGlobal$clear;//// Delete all API caches in connection.//conncacheclear;
Streaming API
You can subscribe topic and receive message from Salesforce Streaming API, by using Topic#subscribe(topicName, listener)
.
Before the subscription, you should insert appropriate PushTopic record (in this example, "InvoiceStatementUpdates") as written in Streaming API guide.
connstreaming;
NOTE: Before version 0.6, there are Connection#topic(topicName)
to access streaming topic object, and Connection#subscribe(topicName, listener)
is used to subscribe altenatively. These methods are now obsolete and use Streaming#topic(topicName)
and Streaming#subscribe(topicName, listener)
through streaming
API object in connection object instead.
Tooling API
You can use Tooling API to execute anonymous Apex Code, by passing apex code string text to Tooling#executeAnonymous
.
// execute anonymous Apex Codevar apexBody = "System.debug('Hello, World');";conntooling;
Analytics API
By using Analytics API, you can get the output result from a report registered in Salesforce.
Analytics#reports()
lists recently accessed reports.
// get recent reportsconnanalytics;
Analytics#report(reportId)
gives a reference to the report object specified in reportId
.
By calling Report#execute(options)
, the report is exected in Salesforce, and returns executed result synchronously.
Please refer to Analytics API document about the format of retruning result.
// get report referencevar reportId = '00O10000000pUw2EAE';var report = connanalytics; // execute report synchronouslyreport;
Setting details
to true in options
, it returns execution result with detail rows.
// execute report synchronously with details option,// to get detail rows in execution result.report;
You can override report behavior by putting metadata
object in options
.
For example, following code shows how to update filtering conditions of a report on demand.
// overriding report metadatavar metadata = reportMetadata : reportFilters : column: 'COMPANY' operator: 'contains' value: ',Inc.' ;// execute report synchronously with overridden filters.report;
Report#executeAsync(options)
executes the report asynchronously in Salesforce, registering an instance to the report to lookup the executed result in future.
var instanceId; // execute report asynchronouslyreport; // retrieve asynchronously executed result afterward.report;
Apex REST
If you have a static Apex class in Salesforce and are exposing it using "Apex REST" feature, you can call it by using Apex#get(path)
, Apex#post(path, body)
, Apex#put(path, body)
, Apex#patch(path, body)
, and Apex#del(path, body)
(or its synonym Apex#delete(path, body)
) through apex
API object in connection object.
// body payload structure is depending to the Apex REST method interface.var body = title: 'hello' num : 1 ;connapex;
Bulk API
Node-salesforce package also supports Bulk API. It is not only mapping each Bulk API endpoint in low level, but also introducing utility interface in bulk load operations.
Loading From Records
First, assume that you have record set in array object to insert into Salesforce.
//// Records to insert in bulk.//var accounts = Name : 'Account #1' ... Name : 'Account #2' ... Name : 'Account #3' ... ...;
You can use SObject#create(record)
, but it consumes API quota per record, so not practical for large set of records. We can use bulk API interface to load them.
Similar to Salesforce Bulk API, first create bulk job by Bulk#createJob(sobjectType, operation)
through bulk
API object in connection object.
Next, create a new batch in the job, by calling Job#createBatch()
through the job object created previously.
var job = connbulk;var batch = job;
Then bulk load the records by calling Batch#execute(input)
of created batch object, passing the records in input
argument.
When the batch is queued in Salesforce, it is notified by queue
event, and you can get job ID and batch ID.
batch;batch;
After the batch is queued and job / batch ID is created, wait the batch completion by polling.
When the batch process in Salesforce has been completed, it is notified by response
event with batch result information.
var job = connbulk;var batch = job;batch; // start pollingbatch;
Alternatively, you can use Bulk#load(sobjectType, operation, input)
interface to achieve the above process in one method call.
connbulk;
Following are same calls but in different interfaces :
conn;
conn;
Loading From CSV File
It also supports bulk loading from CSV file. Just use CSV file input stream as input
argument in Bulk#load(sobjectType, operation, input)
, instead of passing records in array.
//// Create readable stream for CSV file to upload//var csvFileIn = ;//// Call Bulk#load(sobjectType, operation, input) - use CSV file stream as "input" argument//connbulk;
Batch#stream()
returns a Node.js standard writable stream which accepts batch input. You can pipe input stream to it afterward.
var batch = connbulk;batch
Updating/Deleting Queried Records
If you want to update / delete records in Salesforce which match specified condition in bulk, now you don't have to write a code which download & upload records information. Query#update(mapping)
/ Query#destroy()
will directly manipulate records.
//// DELETE FROM Account WHERE CreatedDate < LAST_YEAR//conn ;
//// UPDATE Opportunity // SET CloseDate = '2013-08-31'// WHERE Account.Name = 'Salesforce.com'//conn ;
In Query#update(mapping)
, you can include simple templating notation in mapping record.
//// UPDATE Task // SET Description = CONCATENATE(Subject || ' ' || Status)// WHERE ActivityDate = TODAY//conn ;
To achieve further complex mapping, Query#update(mapping)
accepts mapping function in mapping
argument.
conn ;
If you are creating query object from SOQL by using Connection#query(soql)
, the bulk delete/update operation cannot be achieved because no sobject type information available initially. You can avoid it by passing optional argument sobjectType
in Query#destroy(sobjectType)
or Query#update(mapping, sobjectType)
.
conn ;
conn ;
NOTE: Be careful when using this feature not to break/lose existing data in Salesforce. Careful testing is recommended before applying the code to your production environment.
Record Stream Pipeline
Record stream is a stream system which regards records in its stream, similar to Node.js's standard readable/writable streams.
Query object - usually returned by Connection#query(soql)
/ SObject#find(conditions, fields)
methods - is considered as InputRecordStream
which emits event record
when received record from server.
Batch object - usually returned by Job#createBatch()
/ Bulk#load(sobjectType, operation, input)
/ SObject#bulkload(operation, input)
methods - is considered as OutputRecordStream
and have send()
and end()
method to accept incoming record.
You can use InputRecordStream#pipe(outputRecordStream)
to pipe record stream.
RecordStream can be converted to usual Node.js's stream object by calling RecordStream#stream()
method.
By default (and only currently) records are serialized to CSV string.
Piping Query Record Stream to Batch Record Stream
The idea of record stream pipeline is the base of bulk operation for queried record. For example, the same process of Query#destroy()
can be expressed as following:
//// This is much more complex version of Query#destroy().//var Account = conn;Account ;
And Query#update(mapping)
can be expressed as following:
//// This is much more complex version of Query#update().//var Opp = conn;Opp ;
Following is an example using Query#stream()
(inherited RecordStream#stream()
) to convert record stream to Node.js stream, in order to export all queried records to CSV file.
var csvFileOut = ;conn // Convert to Node.js's usual readable stream. ;
Record Stream Filtering / Mapping
You can also filter / map queried records to output record stream. Static functions like InputRecordStream#map(mappingFn)
and InputRecordStream#filter(filterFn)
create a record stream which accepts records from upstream and pass to downstream, applying given filtering / mapping function.
//// Write down Contact records to CSV, with header name converted.//conn ;//// Write down Lead records to CSV file, eliminating duplicated entry with same email address.//var emails = {};conn ;
Here is much lower level code to achieve the same result using InputRecordStream#pipe()
.
//// Write down Contact records to CSV, with header name converted.//conn ;//// Write down Lead records to CSV file, eliminating duplicated entry with same email address.//var emails = {};conn ;
Example: Data Migration
By using record stream pipeline, you can achieve data migration in a simple code.
//// Connection for org which migrating data from//var conn1 = // ...;//// Connection for org which migrating data to//var conn2 = // ...;//// Get query record stream from Connetin #1// and pipe it to batch record stream from connection #2//var query = conn1;var job = conn2bulk;var batch = job;query;batch
API Reference
See API Reference document in https://stomita.github.io/node-salesforce/doc/ .
REPL (Interactive API Console) Usage
Node-salesforce is not merely an API library, but gives sfjs
and sfcoffee
REPL interface to test and inspect node-salesforce APIs in interactive JavaScript/CoffeeScript shell.
It includes buit-in support of node-salesforce package, default connection instance. In the REPL context, package root objects and API methods of default connection are exposed.
Because the REPL automatically waits the promised object during its evaluation, no callback required for all async API calls. The _
variable keeps evaluated result in previous statement (as usual Node.JS REPL).
$ sfjs
> login("username@example.org", "mypassword123");
{ id: '005xxxxxxxxxxxxxxx',
organizationId: '00Dyyyyyyyyyyyyyyy' }
> sobject('Account').find({}, "Id, Name").sort({ CreatedDate: 1}).limit(5);
[ { attributes:
{ type: 'Account',
url: '/services/data/v28.0/sobjects/Account/001i0000009PyDrAAK' },
Id: '001i0000009PyDrAAK',
Name: 'GenePoint' },
{ attributes:
{ type: 'Account',
url: '/services/data/v28.0/sobjects/Account/001i0000009PyDsAAK' },
Id: '001i0000009PyDsAAK',
Name: 'United Oil & Gas, UK' },
{ attributes:
{ type: 'Account',
url: '/services/data/v28.0/sobjects/Account/001i0000009PyDtAAK' },
Id: '001i0000009PyDtAAK',
Name: 'United Oil & Gas, Singapore' },
{ attributes:
{ type: 'Account',
url: '/services/data/v28.0/sobjects/Account/001i0000009PyDuAAK' },
Id: '001i0000009PyDuAAK',
Name: 'Edge Communications' },
{ attributes:
{ type: 'Account',
url: '/services/data/v28.0/sobjects/Account/001i0000009PyDvAAK' },
Id: '001i0000009PyDvAAK',
Name: 'Burlington Textiles Corp of America' } ]
> _[0].Name
'GenePoint'
>
Change History
v0.8.0 (Jan 22, 2014):
-
Support Chatter API.
-
Support Metadata API.
v0.7.2 (Jan 16, 2014):
- Removed unneeded files in npm-published package.
v0.7.1 (Dec 19, 2013):
- Support SObject get updated/deleted.
v0.7.0 (Dec 11, 2013):
-
Support Analytics API and Tooling API.
-
Add Connection#queryAll to include deleted/archived records in query.
-
Add Connection#recent to fetch recently viewed record information.
-
Add RecordReference#blob(fieldName) to access blob in a record field.
-
Fix installation issue in Windows environment.
v0.6.4 (Dec 5, 2013):
- Add Topic#unsubscribe for unsubscribing from a topic in Streaming API.
v0.6.3 (Oct 31, 2013):
- Fix issue in building query using $exists operator in SObject#find()
v0.6.2 (Oct 15, 2013):
-
Change default Salesforce API ver. to 29.0 (Winter '14)
-
Fix issue in Connection#queryMore
-
Add identity URL information in the callback response of Connection#login/authorize.
v0.6.0 (Aug 23, 2013):
-
Change default Salesforce API ver. to 28.0 (Summer '13)
-
Add REPL interface for interactive API inspection.
-
Return Promises/A+ interface object for all async call. The interface is also added to Query / Batch.
-
Accept "*" in fields argument in
SObject#find()
to select all fields defined in SObject. -
Add
Connection#describe$()
,Connection#describeGlobal$()
, andSObject#describe$()
, as caching versions of correspondings. -
Changed
SObject#find(conditions, fields)
behavior in fields argument omission. -
Add
SObject#select()
andQuery#where()
methods to construct a query in SQL-like verb. -
Add
Query#update()
andQuery#destroy()
to apply bulk operation for queried records. -
Add child relationship query support in
Query#include()
-
Add Apex REST support.
-
Move streaming API methods from connection object to separated object.
v0.5.1 (Jan 11, 2013):
- Move Query#stream() method to RecordStream#stream() to support stream serialization even in filtered stream.
v0.5.0 (Jan 11, 2013):
-
Support Bulk API for insert/update/upsert/delete/hardDelete operation (except for 'query').
-
Refine Query#pipe to pipe to other output record stream (like bulk upload batch).
-
Add Query#stream() method to convert record stream to general node.js readable stream (generates CSV data).
v0.4.0 (Nov 05, 2012):
-
Support JSON-style query object to query records other than SOQL, inspired by MongoDB query interface.
-
Change default API version to 26.0 (Winter '13).
-
Return logged-in user info in the callback response of Connection#login() and Connection#authorize().
-
Add Connection#logout() method to terminate session explicitly (Note: only useful for SOAP API login session).
v0.3.4 (Oct 19, 2012):
-
Fix issue to refresh access token multiple time in concurrent requests.
-
Change to use "Bearer", not "OAuth" in HTTP Authorization header to attach access token.
-
Separate oauth2 configuration into different hash object in connection constructor option (old style is still supported for backward compatiblity).
v0.3.2 (Oct 18, 2012):
- Fix error handling in access token refresh flow.
v0.3.1 (Jun 26, 2012):
- Add support of Node.js 0.8.x.
v0.3.0 (May 10, 2012):
- Support Salesforce Streaming API.