ccoenraets / forcejs

Micro library to use the Salesforce REST APIs in JavaScript Apps
175 stars 61 forks source link

ForceJS - JavaScript Toolkit for Salesforce APIs

ForceJS is a micro-library that makes it easy to use the Salesforce REST APIs in JavaScript applications. ForceJS allows you to easily authenticate with Salesforce using OAuth, and to manipulate Salesforce data using a simple API.

The main target of ForceJS are:

Applications deployed inside a Salesforce instance (Visualforce Page or Lightning Components) can use one of the data access utilities built into the Salesforce Platform instead: JavaScript Remoting, Remote Objects, Lightning Data Service, etc.

Built on ECMAScript 6

Modern JavaScript applications now use ECMAScript 6 (aka ECMAScript 2015) and beyond. The current version of modern frameworks (such as React, Angular 2, and Ionic 2) are also built on top of ECMAScript 6 and beyond. To support modern application development, and to integrate nicely with these frameworks, ForceJS is now built on top of ECMAScript 6 as well.

Compatible with ECMAScript 5

The ECMAScript 6 source files are compiled into an ECMAScript 5 compatible version. The ECMAScript 5 compatible files are available in the dist directory. The ECMAScript 5 files support the Universal Module Definition (UMD) format. In other words, they can be used with AMD or CommonJS module loaders as well as globally using the force.OAuth and force.DataService variables.

The original ECMAScript 5-only version of forcejs is still available in the es5 branch of this repository. The es5 branch is no longer actively developed.

Key Characteristics

Modular

ForceJS is built on a modular architecture. It currently includes two modules:

forcejs/oauth and forcejs/data-service are typically used together in an application, but you can use them separately. For example, you could use forcejs/oauth by itself if all you need is a Salesforce access token (Lightning Out use cases). Similarly, you could use forcejs/data-service by itself if you already have an access token, and all you need is a simple library to access the Salesforce APIs.

Browser and Cordova Abstraction

ForceJS can be used to develop browser-based apps or hybrid mobile apps using the Salesforce Mobile SDK and Apache Cordova. If you develop a hybrid application using the Salesforce Mobile SDK, you often switch back and forth between running the app in the browser and on device. Developing in the browser is generally faster and easier to debug, but you still need to test device-specific features and check that everything runs as expected on the target platforms. The problem is that the configuration of OAuth and REST is different when running in the browser and on device. Here is a summary of the key differences:

BrowserMobile SDK
Requires ProxyYes(*)No
OAuthWindow PopupOAuth Plugin

(*) Starting in the Spring 15 release, some Salesforce REST APIs (like Chatter and sobjects) support CORS. To allow an app to make direct REST calls against your org, register the app domain in Setup: Administer > Security Controls > CORS.

ForceJS abstracts these differences and allows you to run your app in the browser and on device without code or configuration changes.

ECMAScript 6 Usage

import {OAuth, DataService} from 'forcejs';

let oauth = OAuth.createInstance();
oauth.login().then(oauthResult => DataService.createInstance(oauthResult));

let loadContacts = () => {
    let service = DataService.getInstance();
    service.query('select id, Name from contact LIMIT 50')
        .then(response => {
            let contacts = response.records;
            // do something with contacts
    });
}

If you are only using one of the forcejs submodules (either oauth or data), the following import syntax is recommended to make sure the compiled version does not include the module you don't use if your build tool doesn't support tree shaking:

import OAuth from 'forcejs/oauth';
//or
import DataService from 'forcejs/data-service';

Because current browsers don't yet support all the ECMAScript 6 features, you need to use a build tool to compile (transpile) your ECMAScript 6 code to ECMAScript 5 compatible code, and provide the module loading infrastructure. Webpack, Browserify, and Rollup are popular options. Webpack instructions are provided in the Quick Start sections below. Frameworks like React, Angular 2, and Ionic 2 already come with a build process. If you are using these frameworks, no additional step is necessary.

ECMAScript 5 Usage

Use the ECMAScript 5 compatible files available in the dist directory.

<script src="https://github.com/ccoenraets/forcejs/raw/master/force.all.js"></script>
<script>
    var oauth = force.OAuth.createInstance();
    oauth.login().then(function(oauthResult) {
        force.DataService.createInstance(oauthResult);    
    });

    function loadContacts() {
        var service = force.DataService.getInstance();
        service.query('select id, Name from contact LIMIT 50')
            .then(function(response) {
                var contacts = response.records;
                // do something with contacts
            });
    }
</script>

If you are only using one of the forcejs modules (either oauth or data), the following syntax is recommended to avoid including modules you don't use:

<script src="https://github.com/ccoenraets/forcejs/raw/master/force.oauth.js"></script>
// or
<script src="https://github.com/ccoenraets/forcejs/raw/master/force.data-service.js"></script>

var oauth = force.OAuth.createInstance();
// or
var service = force.DataService.createInstance(oauthResult);

Quick Start 1: Simple Browser App

  1. Create a new directory for your project, navigate (cd) to that directory, and type the following command to initialize a project that uses the npm package manager (accept all the default values):

    npm init
  2. Type the following command to install forcejs:

    npm install forcejs --save-dev
  3. Type the following command to install the force-server development server:

    npm install force-server --save-dev
  4. Type the following command to install Webpack and Babel:

    npm install babel-core babel-loader babel-preset-es2015 webpack --save-dev
  5. Using your favorite editor, open package.json and modify the scripts section as follows:

    "scripts": {
        "webpack": "webpack",
        "start": "force-server"
    },
  6. In your project's root directory, create a file named webpack.config.js:

    var path = require('path');
    var webpack = require('webpack');
    
    module.exports = {
        entry: './app.js',
        output: {
            filename: 'app.bundle.js'
        },
        module: {
            loaders: [
                {
                    test: /\.js$/,
                    loader: 'babel-loader',
                    query: {
                        presets: ['es2015']
                    }
                }
            ]
        },
        stats: {
            colors: true
        },
        devtool: 'source-map'
    };
  7. In your project's root directory, create a file named index.html:

    <!DOCTYPE html>
    <html>
    <body>
        <h1>Forcejs Quick Start</h1>
        <ul id="contacts"></ul>
        <script src="https://github.com/ccoenraets/forcejs/raw/master/app.bundle.js"></script>
    </body>
    </html>
  8. In your project's root directory, create a file named app.js:

    import {OAuth, DataService} from 'forcejs';
    
    let oauth = OAuth.createInstance();
    oauth.login()
        .then(oauthResult => {
            DataService.createInstance(oauthResult);
            loadContacts();
        });
    
    let loadContacts = () => {
        let service = DataService.getInstance();
        service.query('select id, Name from contact LIMIT 50')
            .then(response => {
                let contacts = response.records;
                let html = '';
                contacts.forEach(contact => html = html + `<li>${contact.Name}</li>`);
                document.getElementById("contacts").innerHTML = html;
        });
    }
  9. On the command line, type the following command to build your project:

    npm run webpack
  10. Type the following command to start the app in a browser:

    npm start

Quick Start 2: Hybrid Mobile App with Cordova and the Mobile SDK

  1. Install Cordova and the Salesforce Mobile SDK for the platform of your choice. For example, for iOS:

    npm install -g cordova forceios

    On a Mac, you may have to use sudo:

    sudo npm install -g cordova forceios
  2. Create a new mobile application:

    forceios create
  3. Answer the prompts as follows (adjust the company id and organization name as needed):

    Enter your application type (native, hybrid_remote, or hybrid_local): hybrid_local
    Enter your application name: myforcejsapp
    Enter the output directory for your app (defaults to the current directory):
    Enter your company identifier (com.mycompany): com.mycompany.myforcejsapp
    Enter your organization name (Acme, Inc.): MyCompany, Inc.
    Enter your Connected App ID (defaults to the sample app’s ID):
    Enter your Connected App Callback URI (defaults to the sample app’s URI):
  4. Navigate (cd) to the project directory:

    cd myforcejsapp
  5. Type the following command to initialize a project that uses the npm package manager (accept all the default values):

    npm init
  6. Type the following command to install forcejs:

    npm install forcejs --save-dev
  7. Type the following command to install the force-server development server:

    npm install force-server --save-dev
  8. Type the following command to install Webpack and Babel:

    npm install babel-core babel-loader babel-preset-es2015 webpack --save-dev
  9. Using your favorite editor, open package.json and modify the scripts section as follows:

    "scripts": {
        "webpack": "webpack",
        "start": "force-server --root www"
    },
  10. In your project's root directory, create a file named webpack.config.js:

    var path = require('path');
    var webpack = require('webpack');
    
    module.exports = {
        entry: './app/app.js',
        output: {
            path: path.resolve(__dirname, 'www'),
            filename: 'app.bundle.js'
        },
        module: {
            loaders: [
                {
                    test: /\.js$/,
                    loader: 'babel-loader',
                    query: {
                        presets: ['es2015']
                    }
                }
            ]
        },
        stats: {
            colors: true
        },
        devtool: 'source-map'
    };
  11. In your project's root directory, create a directory called app

  12. In the app directory, create a file named app.js:

    import {OAuth, DataService} from 'forcejs';
    
    let oauth = OAuth.createInstance();
    oauth.login()
        .then(oauthResult => {
            DataService.createInstance(oauthResult);
            loadContacts();
        });
    
    let loadContacts = () => {
        let service = DataService.getInstance();
        service.query('select id, Name from contact LIMIT 50')
            .then(response => {
                let contacts = response.records;
                let html = '';
                contacts.forEach(contact => html = html + `<li>${contact.Name}</li>`);
                document.getElementById("contacts").innerHTML = html;
        });
    }
  13. In the www directory, delete all the files and directories except bootconfig.json and index.html

  14. Open index.html. Replace the content with:

    <!DOCTYPE html>
    <html>
    <body>
        <h1>Forcejs App</h1>
        <ul id="contacts"></ul>
        <script src="https://github.com/ccoenraets/forcejs/raw/master/cordova.js"></script>
        <script src="https://github.com/ccoenraets/forcejs/raw/master/app.bundle.js"></script>
    </body>
    </html>
  15. On the command line, type the following command to build your project:

    npm run webpack
  16. Type the following command to run the app in the browser:

    npm start
  17. On a Mac, type the following command to build the app for iOS:

    cordova build ios
  18. Run the app on your iOS device:

    • Open platforms/ios/myforcejsapp.xcodeproj in Xcode
    • Click myforcejsapp in the left sidebar
    • In the Signing section, select a team corresponding to a valid certificate
    • Click the Run button in the toolbar to run the application on your device.

API Reference

forcejs/oauth

Basic Usage:

import OAuth from "forcejs/oauth";
let oauth = OAuth.createInstance();
oauth.login().then(result => {
    console.log(result); // Prints access token, instance URL, and refresh token (if any)
});

createInstance(appId, loginURL, oauthCallbackURL)

login()

Starts the User Agent OAuth workflow using a popup window when running in the browser or the oauth plugin when running in Cordova.

forcejs/data-service

Basic Usage:

import Oauth from "forcejs/oauth";
import Service from "forcejs/data-service";
let oauth = OAuth.createInstance();
oauth.login().then(oauthResult => {
    Service.createInstance(oauthResult);
});

createInstance(oauth, options, name)

getInstance(name)

getUserId()

query(soql)

Used to execute a SOQL statement

Example:

service.query("SELECT id, name FROM contact")
    .then(result => {
        console.log(result.records);
    })
    .catch(error => {
        console.log(error);
    });

create(objectName, valueObject)

Used to create a record for a Salesforce object

Example:

service.create('contact', {FirstName: "Lisa", LastName: "Jones"})
    .then(response => {
        console.log(response);
    })
    .catch(error => {
        console.log(error);
    });

update(objectName, valueObject)

Used to update a record

Example:

service.update('contact', {Id: "0031a000001x7DOAAY", FirstName: "Emma", LastName: "Wong"})
    .then() => {
        console.log("Update successful");
    })
    .catch(error => {
        console.log(error);
    });

del(objectName, recordId)

Used to delete a record

Example:

service.del('contact', "0031a000001x7DOAAY",
    .then() => {
        console.log("Delete successful");
    })
    .catch(error => {
        console.log(error);
    });

upsert(objectName, externalIdField, externalId, data)

Used to upsert a record

Example:

service.upsert('contact', 'My_Contact_Id__c', '101', {FirstName: "Emma", LastName: "Wong"})
    .then() => {
        console.log("Upsert successful");
    })
    .catch(error => {
        console.log(error);
    });

retrieve(objectName, recordId, fields)

Used to retrieve a single record

Example:

service.retrieve('contact', id)
    .then(contact => {
        console.log(contact);
    })
    .catch(error => {
        console.log(error);
    });

reports(recordId)

Used to return reports

Example:

service.reports()
    .then(contact => {
        console.log(reports);
    })
    .catch(error => {
        console.log(error);
    });

dasboard(recordId)

Used to return dashboards

Example:

service.dashboard()
    .then(contact => {
        console.log(reports);
    })
    .catch(error => {
        console.log(error);
    });

apexrest(urlMapping)

Used to invoke a custom REST service endpoint implemented by your own Apex class.

Example:

force.apexrest("contacts")
    .then(result => {
        console.log(result)
    })
    .catch(error => {
        console.log(error);
    });

request(obj)

The core method to invoke a REST services. Other functions (query, create, update, del, upsert, apexrest) are just convenience functions invoking request() behind the scenes. You can use request() directly to invoke other REST services that are not directly exposed through a convenience function.

Example:

force.request({path: "/services/data-service"})
    .then(result => {
        console.log(result)
    })
    .catch(error => {
        console.log(error);
    });

Parameters:

chatter(obj)

A convenience function to use the Chatter API

Example:

force.chatter({path: "/users/me"})
    .then(result => {
        console.log(result)
    })
    .catch(error => {
        console.log(error);
    });

Parameters:

versions()

Lists summary information about each Salesforce.com version currently available, including the version, label, and a link to each version's root.

resources()

Lists available resources for the client's API version, including resource name and URI.

describeGlobal()

Lists the available objects and their metadata for your organization's data.

metadata(objectName)

Describes the individual metadata for the specified object.

describe(objectName)

Completely describes the individual metadata at all levels for the specified object.

describeLayout(objectName, recordTypeId)

Fetches the layout configuration for a particular sobject name and record type id.

queryMore(url)

Queries the next set of records based on pagination. This should be used if performing a query that retrieves more than can be returned in accordance with http://www.salesforce.com/us/developer/docs/api_rest/Content/dome_query.htm

search(sosl)

Executes the specified SOSL search.

batch(requests)

Executes batch commands the batch parameter in the other calls like query, create will save the request for the batch. So you have to call before you execute this function. Important note: In API version 34.0 and later, subrequests can be calls to the Limits, SObject, Query/QueryAll, Search, Connect, and Chatter resources. API version 35.0 adds the ability to use Actions resources.

// don't do it in production with nested promises :) Chain it or use observals
    let query: string = 'SELECT id FROM Contact LIMIT 10';
    let query1: string = 'SELECT id FROM Contact LIMIT 20';
    let query2: string = 'SELECT id FROM Contact LIMIT 30';

    DataService.getInstance().query(query, true).then(q1 => {
      DataService.getInstance().query(query1, true).then(q2 => {
        DataService.getInstance().query(query2, true).then(q3 => {
          DataService.getInstance().batch([q1, q2, q3]).then((response) => {
            console.log(q1, q2, q3);
            console.log(response);
          });
        });
      });
    });

composite(request)