+ Start a Discussion
Arnaud CombesArnaud Combes 

SOQL from external website (forcejs, api, ...)

Hello,

I'm trying to get salesforce data from my website with  SOQL query but I don't manage to do it.
For that I have tried all the JS libraries I have found like forcejs, but I always have some errors. Is there a library to do this ? And how can I do it ?

Thank you very much.

Regards,
ProlayProlay
To use forcejs checkout the link https://github.com/ccoenraets/forcejs
Arnaud CombesArnaud Combes
Thank you for your answer. I have tried again and still have some issues, I certainly do something wrong. I have replaced in force.js file my AppID, then I have the error :
error=redirect_uri_mismatch&error_description=redirect_uri%20must%20match%20configuration

So after that, I have changed the callback in force.js file to put the one I have defined in my remote app (in Salesforce) and now, I have each time a popup which open :
User-added image

So my first question is : is it mandatory to display this popup each time ? And if I click on "Allow", I have then this error message : 


<OAuth>
<error>unsupported_grant_type</error>
<error_description>grant type not supported</error_description>
</OAuth>

I don't know what is wrong as I have followed the explanation on GIT, but there certainly something missing.
Any idea ?

Thanks again !

Regards,

 
ProlayProlay
Have you created the Connected App? Build|App|Connected Apps
Arnaud CombesArnaud Combes
Yes, but the issue is maybe there.
It is the first time I create one and I tried to define as I have seen on Google. 
So I have just defined a name for the connected app, then check the chekbox "Callback URL" but actually I don't know what to write inside this callback url. I have seen that it must be something like xxx.com/services/oauth2/token, but xxx must be the name of my website, or something else ?
 
ProlayProlay
Put the Callback URL as mysampleapp://auth/success
ProlayProlay
Where  "mysampleapp" is the name of your Connected App
Arnaud CombesArnaud Combes
Hello,

Thank you for all these information. I have only tested now, but unfortunately I still have the issue. I will write everything changes I have made, maybe you will can help me if you have already use forcejs.

So first I have created a remote app in Salesforce with the Remote app name "mysampleapp", api name "mysampleapp" and callbackURL "
mysampleapp://auth/success".  
Is the callbackURL used in the code (html/javascript) file ?

Then I have put on my server the three files 
index.html
oauthcallback.html
force.js


In the index.html file, I have :
 - Uncommented the function  force.init
 - Change AppId by my Consumer Key (of the connected app)
 - Change loginURL from https://login.salesforce.com  to   https://test.salesforce.com'
 - delete proxyURL (as I don't use proxy)
 - 

In force.js :
 - Change loginURL from https://login.salesforce.com  to   https://test.salesforce.com'
 - Change AppId by my Consumer Key (of the connected app)


I hope it is clear enough, do you see sometihng wrong in this ?

Thanks a lot again for your help !

Regards,
ProlayProlay
Did you run it? What's the issue are you facing? 
Arnaud CombesArnaud Combes
Actually, I just have the error :  
The page at lolcalhos says : An error has occured. See console for details.

And in Chrome, when I look at the console, I just see "useProxy: true" but nothing else...
 
ProlayProlay
Can you copy paste the error and all your code here? I can investigate further.
Arnaud CombesArnaud Combes
Oh thanks. So the error I have when I click on Get Contacts is :


error




Then my connected app is :
User-added image


And my code is :

index.html :
<html>
<head>
    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
</head>
<body>

<div class="navbar navbar-default navbar-fixed-top" role="navigation">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">ForceJS</a>
        </div>
        <div class="collapse navbar-collapse">
            <ul class="nav navbar-nav">
                <li><a href="javascript:login()">Login</a></li>
                <li><a href="javascript:discardToken()">Discard Token</a></li>
                <li><a href="javascript:isLoggedIn()">Is Logged In?</a></li>
            </ul>
        </div>
    </div>
</div>

<div class="container" style="padding-top: 60px;">

    <div class="row">
        <div class="col-xs-12 col-sm-4">
            <p><button type="button" class="btn btn-default" onclick="query()"><i class="glyphicon glyphicon-refresh"></i> Get Contacts</button></p>
            <ul id="list" class="list-group"></ul>
        </div>
        <div class="col-xs-12 col-sm-8">
            <p><button type="button" class="btn btn-default" onclick="newContact()"><i class="glyphicon glyphicon-plus"></i> New</button></p>
            <form role="form">
                <div class="form-group">
                    <label for="contactId">Id</label>
                    <input type="text" class="form-control" id="contactId" disabled>
                </div>
                <div class="form-group">
                    <label for="firstName">First Name</label>
                    <input type="text" class="form-control" id="firstName">
                </div>
                <div class="form-group">
                    <label for="lastName">Last Name</label>
                    <input type="text" class="form-control" id="lastName">
                </div>
                <button type="button" class="btn btn-default" id="createBtn" onclick="create()" style="display:none">Create</button>
                <button type="button" class="btn btn-default" id="updateBtn" onclick="update()" style="display:none">Update</button>
                <button type="button" class="btn btn-default" id="deleteBtn" onclick="del()" style="display:none">Delete</button>
            </form>
        </div>
    </div>

</div>

<script src="force.js"></script>
<script>

    var contactList = document.getElementById('contact-list'),
        idField = document.getElementById('contactId'),
        firstNameField = document.getElementById('firstName'),
        lastNameField = document.getElementById('lastName'),
        createBtn = document.getElementById('createBtn'),
        updateBtn = document.getElementById('updateBtn'),
        deleteBtn = document.getElementById('deleteBtn');


// ForceJS is built to work out of the box with sensible defaults.
// Uncomment the force.init() function call below to provide specific runtime parameters
    force.init({
        appId: '3MVG9OI03ecbG2VrZvChgcHVDtTGOrpzUkoLvJn7t7NkAMKUfOIa7_2jmhBx__wY8OofT4pXxrdGCbpB8CIAT',
        apiVersion: 'v32.0',
        loginUrl: 'https://test.salesforce.com',
        oauthRedirectURL: 'http://mywebsite.com/oauthcallback.html',
        proxyURL: 'http://myproxy.com'
    });

    function login() {
        force.login(
            function() {
                console.log('Salesforce login succeeded');
            },
            function(error) {
                console.log(error);
                alert('Salesforce login failed');
            });
    }

    function query() {

        // Empty list
        list.innerHTML = '';

        // Retrieve contacts
        force.query('select id, firstName, lastName from contact LIMIT 50',
            function(response) {
                var str = '';
                var contacts = response.records;
                for (var i=0; i < contacts.length; i++) {
                    str += '<a href="#' + contacts[i].Id + '" class="list-group-item">' + contacts[i].FirstName + ' '
                            + contacts[i].LastName + '</a>';
                }
                list.innerHTML = str;
            },
            function(error) {
                alert("An error has occurred. See console for details.");
            });
    }

    function request() {

        // Empty list
        list.innerHTML = '';

        // Retrieve contacts
        force.request({path: '/services/apexrest/contact',
            success: function(response) {
                console.log('request');
                var str = '';
                var contacts = response;
                for (var i=0; i < contacts.length; i++) {
                    str += '<li><a href="#' + contacts[i].Id + '" class="list-group-item">' + contacts[i].Name + '</a></li>';
                }
                list.innerHTML = str;
            },
            error: function(error) {
                alert("An error has occurred. See console for details.");
            }});
    }

    function create() {
        force.create('contact', {FirstName: firstNameField.value, LastName: lastNameField.value},
            function(response) {
                console.log(response);
            },
            function(error) {
                alert("An error has occurred. See console for details.");
            });
    }

    function update() {
        force.update('contact', {Id: idField.value, FirstName: firstNameField.value, LastName: lastNameField.value},
            function(response) {
                console.log(response);
            },
            function(error) {
                alert("An error has occurred. See console for details.");
            });
    }

    function del() {
        force.del('contact', idField.value,
                function(response) {
                    console.log(response);
                },
                function(error) {
                    alert("An error has occurred. See console for details.");
                });
    }

    function retrieve(id) {
        force.retrieve('contact', id, null,
                function(contact) {
                    console.log(contact);
                    idField.value = contact.Id;
                    firstNameField.value = contact.FirstName;
                    lastNameField.value = contact.LastName;
                    createBtn.style.display = 'none';
                    updateBtn.style.display = 'inline';
                    deleteBtn.style.display = 'inline';

                },
                function(error) {
                    alert("An error has occurred. See console for details.");
                });
    }

    function discardToken() {
        force.discardToken();
        alert('Token discarded');
    }

    function isLoggedIn() {
        alert(force.isLoggedIn());
    }

    function newContact() {
        idField.value = "";
        firstNameField.value = "";
        lastNameField.value = "";
        createBtn.style.display = 'inline';
        updateBtn.style.display = 'none';
        deleteBtn.style.display = 'none';
    }

    window.onhashchange = function () {
        var id = window.location.hash.substr(1);
        retrieve(id);
    }

</script>
</body>
</html>
 
Arnaud CombesArnaud Combes
force.js (part1): 
/**
 * ForceJS - REST toolkit for Salesforce.com
 * Author: Christophe Coenraets @ccoenraets
 * Version: 0.6
 */
var force = (function () {

    "use strict";

    // The login URL for the OAuth process
    // To override default, pass loginURL in init(props)
    var loginURL = 'https://test.salesforce.com',

    // The Connected App client Id. Default app id provided - Not for production use.
    // This application supports http://localhost:8200/oauthcallback.html as a valid callback URL
    // To override default, pass appId in init(props)
        appId = 'myconsumerkey',

    // The force.com API version to use.
    // To override default, pass apiVersion in init(props)
        apiVersion = 'v33.0',

    // Keep track of OAuth data (access_token, refresh_token, and instance_url)
        oauth,

    // By default we store fbtoken in sessionStorage. This can be overridden in init()
        tokenStore = {},

    // if page URL is http://localhost:3000/myapp/index.html, context is /myapp
        context = window.location.pathname.substring(0, window.location.pathname.lastIndexOf("/")),

    // if page URL is http://localhost:3000/myapp/index.html, serverURL is http://localhost:3000
        serverURL = window.location.protocol + '//' + window.location.hostname + (window.location.port ? ':' + window.location.port : ''),

    // if page URL is http://localhost:3000/myapp/index.html, baseURL is http://localhost:3000/myapp
        baseURL = serverURL + context,

    // Only required when using REST APIs in an app hosted on your own server to avoid cross domain policy issues
    // To override default, pass proxyURL in init(props)
        proxyURL = baseURL,

    // if page URL is http://localhost:3000/myapp/index.html, oauthCallbackURL is http://localhost:3000/myapp/oauthcallback.html
    // To override default, pass oauthCallbackURL in init(props)
        oauthCallbackURL = baseURL + '/oauthcallback.html',

    // Because the OAuth login spans multiple processes, we need to keep the login success and error handlers as a variables
    // inside the module instead of keeping them local within the login function.
        loginSuccessHandler,
        loginErrorHandler,

    // Reference to the Salesforce OAuth plugin
        oauthPlugin,

    // Whether or not to use a CORS proxy. Defaults to false if app running in Cordova, in a VF page,
    // or using the Salesforce console. Can be overriden in init()
        useProxy = (window.cordova || window.SfdcApp || window.sforce) ? false : true;

    /*
     * Determines the request base URL.
     */
    function getRequestBaseURL() {

        var url;

        if (useProxy) {
            url = proxyURL;
        } else if (oauth.instance_url) {
            url = oauth.instance_url;
        } else {
            url = serverURL;
        }

        // dev friendly API: Remove trailing '/' if any so url + path concat always works
        if (url.slice(-1) === '/') {
            url = url.slice(0, -1);
        }

        return url;
    }

    function parseQueryString(queryString) {
        var qs = decodeURIComponent(queryString),
            obj = {},
            params = qs.split('&');
        params.forEach(function (param) {
            var splitter = param.split('=');
            obj[splitter[0]] = splitter[1];
        });
        return obj;
    }

    function toQueryString(obj) {
        var parts = [],
            i;
        for (i in obj) {
            if (obj.hasOwnProperty(i)) {
                parts.push(encodeURIComponent(i) + "=" + encodeURIComponent(obj[i]));
            }
        }
        return parts.join("&");
    }
ProlayProlay
Did you install the followings -

Install force-server

Because of the browser's cross-origin restrictions, your JavaScript application hosted on your own server (or localhost) will not be able to make API calls directly to the *.salesforce.com domain. The solution is to proxy your API calls through your own server. You can use your own proxy server, but ForceJS is tightly integrated with ForceServer, a simple development server for Force.com.

To install ForceServer, make sure Node.js is installed on your system, open a command prompt and execute the following commandnpm install -g force-server

For CORS set up please review the following linkhttps://developer.salesforce.com/blogs/developer-relations/2015/01/spring-15-preview-cors-force-com-rest-api.html

To sum up you have to install the following as per the sequence-

1. Node.js - https://nodejs.org/en/
2.  ForceServer -  npm install -g force-server
3. Set up COR as per the link I mentioned earlier
 
ProlayProlay
After installing the node.js go to the command prompt and type the command 
npm install -g force-server to install the ForceServer globally
ProlayProlay
To install Force Server please follow this link https://github.com/ccoenraets/force-server
Arnaud CombesArnaud Combes
Oh, I thought with forcejs there didn't need to use nodejs. I try to install it.

Thanks !
ProlayProlay
Please follow all the steps to install ForceServer. This will resolve the need of the  proxy server.
Arnaud CombesArnaud Combes
Oh, it is almost working ! But I still have 2 issues.
To sum up, if I follow the steps to install force server and forcejs, I manage to display data from my account. But I have 2 issues :
 1) Each time I run the index.html file to get data, I have these two popup :
One to log in
User-added image

Another one to "Allow" :
User-added image


So the issue is that the users can't see that, if each time I execute a SOQL query I have these 2 popup, it is an issue. So is there a way to disable them ?

2) The other issue is that, when I replace the appId there is in the force.js file by my appId (the consumerKey of my conneted app), it no longer works. I have this error message :
User-added image


And I don't see any data.

Do you know what can be the problem ?


Thanks again :)

Regards,
ProlayProlay
Have you created the Start URL for your Web App setting? Please check this link https://developer.salesforce.com/docs/atlas.en-us.identityImplGuide.meta/identityImplGuide/identity_google_connapp.htm.

The Popups are for authentication. Please click on Allow. Please check the Web App setting of your connected app and let me know.
ProlayProlay
Have you checked all the scopes in the Connected App? In your case the "Start URL" will be your local application server URL. Please check the Web App setting in this document. https://developer.salesforce.com/docs/atlas.en-us.packagingGuide.meta/packagingGuide/connected_app_create.htm
ProlayProlay
redirect_uri is the Callback URL from the remote access application definition. For example  sfdc://success. Please put your own defined call back url there.
Arnaud CombesArnaud Combes
I have added the web app, and here is what I have now : 
User-added image

I still have the issue. 
I will try the example of Gmail connected app, but for the moment I can't see what is wrong. But it is sure, it comes frmo my connected app as it works with the default connected app of forcejs.

 
Arnaud CombesArnaud Combes
Thanks a lot ! It finally works :)
I have just one last issue you have already replied, but each time I tried to run a SOQL query, I have the popup where I need to click on button "Allow". Is it possible to hide this popup ? Because I can't display it to our customer. It should connect automatically without this popup.

Thanks again !
Arnaud CombesArnaud Combes
Finally, I am not sure to be able to do what I wanted. Actually, I want to make my own login page on my website, and with SOQL I wanted to retreive the login and password of users? But it seems it is not possible to get password with SOQL for security reason. So do I need to user something else than API ?
 
ProlayProlay
You can use the custom domain to login into your Salesforce Org. Please go through this article http://salesforce.stackexchange.com/questions/64248/how-do-you-configure-a-cname-to-point-to-the-custom-my-domain-url
Mike milarMike milar
Very well explained. You can also check my blog on the latest entertainment news like getting slingTV trial free (https://www.neptunofilms.com/start-sling-tv-free-trial/) and many more.