• Admin

Salesforce + GraphQL - The Sequel

Updated: Oct 8, 2018

Part 2: Building APIs with GraphQL and Salesforce


How do we build GraphQL for Salesforce?

Once you have got your head around the basics of GraphQL's syntax and schema, it is fairly easy to build CRUD (Create, Read, Update, Delete) patterns in GraphQL. Integrating Salesforce to GraphQL is similar to connecting a SQL or NoSQL database to GraphQL.

First, we will build the authentication in which we establish our application as a Connected App to Salesforce. Next, we will perform the authentication from GraphQL to Salesforce and provide best practice examples in keeping our tokens and credentials secure.

Once we have established a connection to Salesforce, performing queries to Salesforce will be as simple as running SOQL and DML queries.

Getting Started

To get started, you will need the following:

  • Salesforce Developer Org or a Salesforce DX Scratch Org Set up a Salesforce organisation which will allow you enable a Connected App. By default, the Org provides the Account Object which we will use in our examples.

  • Node.js Since GraphQL is agnostic to the language you choose, you can choose whichever framework you are comfortable with. However, in this example, we will keep things simple and use Node.js to build our application.

  • NPM Libraries Although NPM is sufficient, we highly recommend using Yarn as a package manager as it keeps our libraries and scripts more organised. Once you have installed Yarn, run the following commands in your terminal.

// Initialise the project
yarn init

// Add Express to our project
yarn add express

// Add GraphQl to our project
yarn add graphql

// Add the GraphQl interface for Express to our project
yarn add express-graphql

// Add nForce library to our project
yarn add nforce

// Add dotenv to our project as a dev dependency
yarn add dotenv --dev

// Add nodemon to our project as a dev dependency
yarn add nodemon --dev

Why nForce?

Since we will be keeping things simple in this guide, we will use nForce as our middleware to connect to Salesforce. This library provides the basic essentials in building a CRUD model and it supports Promises and Asynchronous calls which is a modern way to build Node.js applications.

We are not limited to using nForce as we can opt to use Salesforce built-in REST API and Bulk API to replace nForce as we need them. The beauty of GraphQL is that we can change the underlying interfaces while keeping the schema and syntax intact. For example, an iOS application which already utilises a certain schema to retrieve the Accounts model will not require rebuilding the way it consumes the API. It will only ever need the GraphQL schema to retrieve the data.

This article aims to provide a basic example of how the CRUDs can be implemented and thus, we chose to start with the following.

Setting up nForce as a Salesforce Connected App

  • Login to your Salesforce Org

  • Go to Setup > Quick Find > App Manager > New Connected App

  • Enter the Connected App Name, API Name and Contact Email

  • Enable OAuth Settings

  • Enter the Callback a URL

  • Under Selected OAuth Scopes, add Access and Manage your data (api)

  • Click Save

  • Take note of the Consumer Key and Consumer Secret


Setting up GraphQL

We will first create the index.js file in our project folder using the following boiler plate code. This will initialise our express server along with the GraphQl middleware express-graphql.

// Express
const express = require('express')
const assert = require('assert')

// Express-graphql
const graphqlHTTP = require('express-graphql')

// TODO: Initialise dotenv
// TODO: Import root schema

// Initialise express
const server = express()
const PORT = process.env.PORT || 4000

// TODO: Initialise nForce and connection parameters for Salesforce Connected App
// TODO: Authenticate to salesforce using single user mode

server.use('/graphql', (req, res) => {
  // TODO: Use facebook dataloader to cache queries
  graphqlHTTP({
    schema: rootSchema,
    graphiql: true,
    context: { salesforce }
  })(req, res)
})
    
// Error handler
server.use((error, req, res, next) => {
  res.json({
    error: {
      message: error.message
    }
  })
})

// Start the server
server.listen(PORT, error => {
  assert.equal(error, null)
  console.log(`GraphQL server started at ${PORT}`)
})

As you may have noticed, we left several TODO comments in the code and we will go through it one by one. Our GraphQL application will need its GraphQL Schema, the credentials for our Salesforce Org and an authenticated connection between Salesforce and GraphQL.

Setup the Salesforce Credentials

As a best practice, we should you a .env file in storing our credentials. This includes our consumer key, consumer secret, username, password and security token. Create a .env file in your project's root folder and enter the credentials as follows.

CONSUMER_KEY = XXXXXXXXXXXXXXXXXX
CONSUMER_SECRET = XXXXXXXXXXXXXXXXXX
SALESFORCE_USERNAME = someone@mav3rik.com
SALESFORCE_PASSWORD = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
SALESFORCE_SECURITY_TOKEN = XXXXXXXXXXXXXXXXXX

The salesforce security token is sent to your email during your username registration. If you have not saved this token, you may reset this token by following Salesforce's Reset Your Security Token guide: https://help.salesforce.com/apex/HTViewHelpDoc?id=user_security_token.htm

Let us now replace the // TODO: Initialise dotenv part of our code to the following:

// Initialise dotenv
require('dotenv').config()

This tells our application to read our salesforce credentials from the .env file. This file should be ignored when we push our source code to a public repository such as Github. Also, when we deploy our app to cloud platform such as Heroku, this is when our app identifies that it should retrieve the credentials from the environment variables that we configure in Heroku.

Setup the GraphQL Schema

The GraphQL schema is the heart and soul of a GraphQL application. It tells the GraphQL application what type of information the API is capable of providing and the operations that it is allowed to do.

To set up the GraphQL Schema, we will create a folder called schema and create the root schema file root.js in this folder. The root schema file will contain the following boilerplate code:

// Import the basic dependencies from the GraphQL Library
const {
  GraphQLSchema,
  GraphQLObjectType,
  GraphQLString,
} = require('graphql')

// The Root Query Type
const RootQueryType = new GraphQLObjectType({
  name: 'RootQuery',
  description: 'The root query type',
  fields: () => {
    // TODO: Declare Types needed for our Schema
    return {
      hello: {
        type: GraphQLString,
        resolve: (obj, args, { salesforce }) => {
          return "Hello World from Mav3rik!"
        }
      }
      // TODO: Create Queries for the Accounts Object
    }
  }
})

// The Root Schema
const rootSchema = new GraphQLSchema({
  // The root query
  query: RootQueryType
  // TODO: Create a Mutation Root Query Type
})

module.exports = rootSchema

In our index.js file, replace // TODO: Import root schema line with the following code:

// Import root schema
const rootSchema = require('./schema/root')

This tells our application that it will be using the root schema we created in ./schema/root file.

Every GraphQL application starts with an import of its dependencies. For this example, we imported the following:

  • GraphQLSchema - This is the main object which tells our Schema to be the root schema.

  • GraphQLObjectType - This is the GraphQL type for either a Query or a Mutation.

  • GraphQLString - This is a basic scalar GraphQL type which is a String and very similar to any programming language's data type.

Next, we established the Root Query Type which is GraphQL type object. A GraphQL type object will have the following attributes:

  • name - The name label of the query type.

  • description - The description of the query type. The texts that you put in here will appear in the Documentation Explorer of GraphiQL interface.

  • fields - A function that returns a key-value pair in which the key is the name of the field and the value is also an object which is a key value pair of keys type, resolve, and later on args.

  • type - Tells the function that the data being retrieved is a String.

  • resolve - Tells the function how it is going to retrieve or calculate the data.

  • obj - A variable that refers to the object being returned. As an example if we have fields called firstName and lastName, we can use the obj variable to to return a fullName by saying return obj.firstName + ' ' + obj.lastName.

  • args - A variable containing the inputs from the user.

  • { salesforce } - The name of our context variables. For this example, we will call it salesforce. A context is a reference object that will point us to our salesforce CRUD functions.

  • args - Tells the function that our resolve will be taking in inputs which will be used as parameters to our queries.

Lastly, we are creating and exporting the GraphQL schema which can have one root query and one root mutation, but for the simplicity of this introduction, we will leave out the root mutation for later.

That is a lot of information to take in, but let's just say we are simply establishing the basic building blocks of structure that are very reusable all throughout the GraphQL schema.

Run the GraphQL application

To run the application, we need to perform a slight modification to our package.json file which was generated for us. Add the following key-value pair in the file:

"scripts": {
    "start": "nodemon index.js"
}

This tells us the application that when we are running the command yarn dev, we will be using nodemon to run our index.js file. Run the application using the following command:

yarn start

If all steps were followed correctly, you should see the following in your terminal:

$ yarn start
yarn run v1.7.0
$ nodemon index.js
[nodemon] 1.17.4
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: *.*
[nodemon] starting `node index.js`
GraphQL server started at 4000

Open your browser and open http://localhost:4000/graphql To test our query, enter the following command in the left text area:

query anyOperationNameThatILike {
   hello
}

The output will be similar to the following:


That is the longest Hello World tutorial that was ever written but keep in mind that this is the bread and butter of a high-performance GraphQL application.

Building Queries

Now that we have set up the GraphQL basic schema, we can now extend it to use the nForce library and perform queries to salesforce. A query in GraphQL should be capable of retrieving information in the form that the user requested. For this example, we will use the Account object in Salesforce to retrieve the Id, Name, Description and Employees fields.

Create a lib folder in our root directory and inside, create a file called nforce.js. Copy the following boilerplate code:

const nforce = require('nforce')

/**
 * Perform a SOQL Query
 * @param {object} org         - Salesforce org
 * @param {string} queryString - SOQL query
 * @param {object} oauth       - OAuth string received from successful authentication
 * @return {Promise}           - Returns a response containing the queried records
 */
const query = (org, queryString, oauth) =>
  new Promise((resolve, reject) => {
    org.query(
      {
        query: queryString,
        oauth: oauth
      },
      (error, response) => {
        if (!error) {
          resolve(response.records)
        } else {
          reject(error)
        }
      }
    )
  })
  
module.exports = {
  query
}

Let us also create a folder called database in our root directory and inside this folder, create a file called utils.js and salesforce.js and use the boilerplate code below:

utils.js - This contains a function mapRecordsToFields which reformats the response object of nForce and make it more suitable as a response to GraphQL. This function was made specifically for nForce and other implementations other than nForce may differ in its default response.

/**- 
 * Map salesforce response records to its fields
 * @param {list} records - list of records from salesforce response
 * @return {list} list of records which correspond to their fields
 */
const mapRecordsToFields = records => (
  records.reduce((result, record) => ([...result, record._fields]), [])
)

module.exports = {
  mapRecordsToFields
}

salesforce.js - This file is where we define our SOQL queries and later on, our mutations.

const { query } = require('../lib/nforce')
const { mapRecordsToFields } = require('../lib/utils')

module.exports = org => {
  return {
    async getAccounts() {
      const response = await query(
        org,
        `select Id, Name, Description from Account`,
        null
      )
      return mapRecordsToFields(response)
    }
  }
}

In our index.js file, replace the // TODO: Initialise nForce and connection parameters for Salesforce Connected App line with the following:

// Initialise nForce and connection parameters for Salesforce Connected App
const nforce = require('nforce')
const org = nforce.createConnection({
  clientId: process.env.CONSUMER_KEY,
  clientSecret: process.env.CONSUMER_SECRET,
  redirectUri: 'https://localhost:3000/oauth/_callback',
  environment: 'production',
  mode: 'single'
})
const salesforce = require('./database/salesforce')(org)

This tells our application to use the nForce library to create a connection to our org using our consumer key, consumer secret, the callback URL that we defined in our connected app and using “single user mode” which means that only one user will be logging in to our Salesforce org. We are limiting this connection to one user since GraphQL will be the only user that will authenticate to our Org. The last line means that we are initialising our salesforce context using our salesforce database file and pass our org connection as a parameter.

Next, replace the following lines in our index.js file

// TODO: Authenticate to salesforce using single user mode

server.use('/graphql', (req, res) => {
  graphqlHTTP({
    schema: rootSchema,
    graphiql: true,
    context: { salesforce }
  })(req, res)
})

with the following code:

// Authenticate to salesforce using single user mode
org.authenticate(
  {
    username: process.env.SALESFORCE_USERNAME,
    password: process.env.SALESFORCE_PASSWORD,
    securityToken: process.env.SALESFORCE_SECURITY_TOKEN
  },
  (error, response) => {
    assert.equal(error, null)
    // GraphQL middleware
    server.use('/graphql', (req, res) => {
      graphqlHTTP({
        schema: rootSchema,
        graphiql: true,
        context: { salesforce }
      })(req, res)
    })
  }
)

This tells our application that in order to serve the /graphql route to our user, we will need to authenticate first with salesforce. The function graphqlHTTP takes an object key-value pair as a parameter which establishes that our default schema will be the root schema that we defined earlier, the GraphiQL interface will be enabled and our context variable which will be used across all queries and mutations will be called salesforce.

We will now create a folder call types in our schema directory and inside it, create a file called account.js. This will create the Account Type object which is using the GraphQL Object Type. It will also contain the fields Id, Name and Description which are standard fields in Salesforce. Notice how the types used for these fields are similar to the data types in Apex.

const {
  GraphQLObjectType,
  GraphQLID,
  GraphQLString
} = require('graphql')

const salesforce = require('../../database/salesforce')

module.exports = new GraphQLObjectType({
  name: 'Account',
  fields: () => {
    return {
      id: { type: GraphQLID },
      name: { type: GraphQLString },
      description: { type: GraphQLString }
    }
  }
})

We also update our root schema in the root.js file and tell it to use the Account Type we just created and create a new query called accounts which calls our salesforce database function getAccounts.

const {
  GraphQLSchema,
  GraphQLObjectType,
  GraphQLString
} = require('graphql')

const RootQueryType = new GraphQLObjectType({
  name: 'RootQuery',
  description: 'The root query type',
  fields: () => {
    // Tells our application to use the Account Type
    const AccountType = require('./types/account')
    return {
      hello: {
        type: GraphQLString,
        resolve: (obj, args, { salesforce }) => {
          return "Hello World from Mav3rik!"
        }
      },
      accounts: {
        type: new GraphQLList( AccountType ),
        resolve: (obj, args, { salesforce }) => {
          return salesforce.getAccounts()
        }
      }
    }
  }
})

const rootSchema = new GraphQLSchema({
  query: RootQueryType,
  // TODO: Create a Mutation Root Query Type
})

module.exports = rootSchema

Refresh the GraphiQL interface page and type the following query:

query ViewAllAccounts {
  accounts {
    id
    name
    description
  }
}

If everything went well, you should see results similar to the following:


Congratulations! You successfully used GraphQL to query the Accounts object in Salesforce. The number of fields you retrieve and the complexity of the query will be up to you. In a real-world scenario, customer data requirements can get very complicated really quickly. Thankfully, GraphQL's flexibility sorts this out for us.

There are more query examples on our GitHub page but one important query that you should explore is using parameters to filter these queries. To get you started, consider implementing the following in your code as an exercise:

// Implement this in database/salesforce.js
async getAccountById(id) {
  const response = await query(
    org,
    `select Id, Name, SLA__c from Account WHERE Id = '${id}'`,
    null
  )
  return mapRecordsToFields(response)
}
// Implement this in root.js
accountsByName: {
  type: new GraphQLList( AccountType ),
  args: {
    name: { type: new GraphQLNonNull(GraphQLString) }
  },
    resolve: (obj, args, { salesforce }) => {
    return salesforce.getAccountByName(args.name) 
  }
}

If it gets too challenging, you can always refer to our Mav3rik GitHub page (https://github.com/wearemav3rik/salesforce-graphql) for the solution.

Building Mutations

GraphQL mutations provide the means to create records, update records, upsert records or delete records from a data source. These are very familiar database transactions to salesforce and an integral part of writing our CRUDs.

We create a new function in our lib/nforce.js file. This is a function which calls the insert function of nForce to create records in our Salesforce Org.

/**
 * Create a record
 * @param {object} org         - Salesforce org
 * @param {string} sobjectName - Name of the SObject (e.g. Account)
 * @param {object} recordData  - Key value pair of Field Names and Field Values
 * @param {object} oauth       - OAuth string received from successful authentication
 * @return {Promise}           - Returns a response containing the inserted record
 */
const createRecord = (org, sobjectName, recordData, oauth) => {
  const sobj = nforce.createSObject(sobjectName, recordData)
  return new Promise((resolve, reject) => {
    org.insert(
      {
        sobject: sobj,
        oauth: oauth
      },
      (error, response) => {
        if (!error) {
          recordData.id = response.id
          resolve(recordData)
        } else {
          reject(error)
        }
      }
    )
  })
}

Next, we create a new type for creating Account data. Create an input folder inside our schema directory. Inside the input folder, create a file called account-input.js.

const {
  GraphQLInputObjectType,
  GraphQLID,
  GraphQLString
} = require('graphql')

module.exports = new GraphQLInputObjectType({
  name: 'AccountInput',
  fields: {
    id: { type: GraphQLID },
    name: { type: GraphQLString },
    description: { type: GraphQLString }
  }
})

We now create a mutations folder in our schema directory and inside it, create a file called add-account.js. This file tells our application that we are outputting an Account Type but using the Account Type Input as our input object model. The GraphQLNonNull type simply just tells our application that if the Account Input type is used, its value is required and it cannot be empty. Lastly, our resolve calls the createAccount function which passes the input variable which contains our input data.

const {
  GraphQLNonNull
} = require('graphql')

const AccountType = require('../types/account')
const AccountInputType = require('../inputs/account-input')

module.exports = {
  type: AccountType,
  args: {
    input: { type: new GraphQLNonNull(AccountInputType) }
  },
  resolve: (obj, { input }, { salesforce }) => {
    return salesforce.createAccount(input)
  }
}

Finally, we enable our root mutation in our root.js file and replace the // TODO: Create a Mutation Root Query Type line with an actual root mutation type. Below our Root Query Type, enter the following code:

// The Account Mutation we created earlier
const AddAccountMutation = require('./mutations/add-account')

// The Root Mutation Type
const RootMutationType = new GraphQLObjectType({
  name: 'RootMutation',
  description: 'The root mutation type',
  fields: () => ({
    AddAccount: AddAccountMutation,
    UpdateAcconunt: UpdateAccountMutation,
    DeleteAccount: DeleteAccountMutation,
    UpsertAccount: UpsertAccountMutation
  })
})

// The Root Schema
const rootSchema = new GraphQLSchema({
  // The root query
  query: RootQueryType,
  mutation: RootMutationType
})

module.exports = rootSchema

Let us now run a mutation in our GraphiQL interface. Enter the following mutation our query area:

mutation MakeMeAnAccount($input: AccountInput!) {
  AddAccount(input: $input) {
    id
    name
    description
  }
}

This syntax creates a mutation using the Account Input type object and stores it in the $input variable. As a result of successful input, it should output the Id, Name and Description. Declare the input variable in the Query Variables area:

{
  "input": {
    "name": "Mav3rik",
    "description": "A cool company"
  }
}

Notice that the input query variables look very similar to a JSON object file. That is because it is a JSON object file. Run the mutation and if everything goes correctly, you should see a result similar to the following:

You now have the skills to build mutations with GraphQL. Try to build the Update, Upsert and Delete functionalities of this application as an exercise. Hint: You will need to explore the functions of the nForce library and create the types and mutations the same way as Add Account. If it gets too difficult, refer to our GitHub examples (https://github.com/wearemav3rik/salesforce-graphql).

Enhancing Scalability

As data requirements become more complex, recursive queries are unavoidable in GraphQL. As a solution to this complexity, a library called DataLoader (https://github.com/facebook/dataloader) was created by Facebook which is typically used in conjunction with GraphQL. DataLoader is a generic utility to be used as part of an application's data fetching layer to provide a consistent API over various backends and reduce requests to those backends via batching and caching.

For simplicity purposes, we used the nForce library in developing our GraphQL application. However, as of current, the nForce may have limitations specific to performing bulk mutations. If your customer requirements require a bulk mutation solution, it is recommended to replace the nForce library and use Salesforce's REST and Bulk API directly in GraphQL.

Conclusion

We learned the basic elements of a GraphQL application and discovered how we can authenticate and integrate it to Salesforce. We also learned how to create a GraphQL schema, initialise GraphQL Object Types then write Queries and Mutations for Salesforce.

GraphQL is an amazing tool that is fitting for modern day applications. Since it is an open-source library, it has large support from the community in which plenty of resources have been established such as best practice patterns for performance and scalability as well as an extensive number of utilities to support enterprise-level projects.

Salesforce can benefit a lot from GraphQL due to its flexibility and simplicity and Salesforce's REST API and Bulk API is a joy to be used with GraphQL regardless if it is invoked directly from Salesforce or libraries like nForce are used to abstract the transaction.

Today's technological trends and increasing demands for big data and mobility simply demand a platform like Salesforce. A tool such as GraphQL will further explore that capability and increase adoption to Salesforce.


That's all folks! Appreciate you going through a rather lengthy, but hopefully interesting and insightful post about applying GraphQL on Salesforce. #beamav3rik


-Glenn-

References

2,283 views

Recent Posts

See All

Async Component in LWC

Sameed is back at it with a blog post on the application of Lightning Web Components (LWC) to build out an asynchronous component that will be useful in addressing a fairly common real world scenario

  • LinkedIn - Black Circle
  • Twitter - Black Circle
  • Instagram - Black Circle

© 2020 | mav3rik.com