Awesome Go

go-salesforce

CategoryThird-party APIs
SubcategoryThird-party APIs
Stars56

Go client library for interacting with the [Salesforce REST API](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_list.htm)

About go-salesforce

This large README is shown as a plain-text preview. Read the full README at the source

# go-salesforce

A REST API wrapper for interacting with Salesforce using the Go programming language.

[![GoDoc](https://godoc.org/github.com/k-capehart/go-salesforce/v2?status.png)](https://pkg.go.dev/github.com/k-capehart/go-salesforce/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/k-capehart/go-salesforce)](https://goreportcard.com/report/github.com/k-capehart/go-salesforce)
[![codecov](https://codecov.io/gh/k-capehart/go-salesforce/graph/badge.svg?token=20V6A05GMH)](https://codecov.io/gh/k-capehart/go-salesforce)
[![MIT License](https://img.shields.io/badge/license-MIT-blue)](https://github.com/k-capehart/go-salesforce/blob/main/LICENSE)
[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go)

- Read my [blog post](https://www.kylecapehart.com/posts/go-salesforce/) for an in-depth example
- Check out [go-salesforce-examples](https://github.com/k-capehart/go-salesforce-examples) for more examples
- Read the [Salesforce REST API documentation](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_list.htm)
- Read the [Golang documentation](https://go.dev/doc/)

## Table of Contents

- [Installation](#installation)
- [Types](#types)
- [Authentication](#authentication)
- [Configuration](#configuration)
- [SOQL](#soql)
- [SObject Single Record Operations](#sobject-single-record-operations)
- [SObject Collections](#sobject-collections)
- [Composite Requests](#composite-requests)
- [Bulk v2](#bulk-v2)
- [Other](#other)

## Installation

```
go get github.com/k-capehart/go-salesforce/v3
```

## Types

```go
type Salesforce struct {
    auth   *authentication
    config configuration
}

type configuration struct {
    compressionHeaders           bool
    apiVersion                   string
    batchSizeMax                 int
    bulkBatchSizeMax             int
    httpClient                   *http.Client
    roundTripper                 http.RoundTripper
    shouldValidateAuthentication bool
    httpTimeout                  time.Duration
    tagName                      string
}

type Creds struct {
    Domain         string
    Username       string
    Password       string
    SecurityToken  string
    ConsumerKey    string
    ConsumerSecret string
    ConsumerRSAPem string
    AccessToken    string
}

type SalesforceResults struct {
    Results             []SalesforceResult
    HasSalesforceErrors bool
}

type SalesforceResult struct {
    Id      string
    Errors  []SalesforceErrorMessage
    Success bool
}

type SalesforceErrorMessage struct {
    Message    string
    StatusCode string
    Fields     []string
}

type BulkJobResults struct {
    Id                  string
    State               string
    NumberRecordsFailed int
    ErrorMessage        string
    SuccessfulRecords   []map[string]any
    FailedRecords       []map[string]any
}
```

### Salesforce tag

- tag struct fields with the configured tag (default `salesforce`) to decode them into their Salesforce API field names

```go
type Account struct {
    ExternalID string `salesforce:"ExternalId__c"`
    Name       string
}
```

## Authentication

- To begin using, create an instance of the `Salesforce` type by calling `salesforce.Init()` and passing your credentials as arguments
- Once authenticated, all other functions can be called as methods using the resulting `Salesforce` instance
- [Creating an External Client App in Salesforce](https://help.salesforce.com/s/articleView?id=xcloud.external_client_apps.htm&type=5)
- [Review Salesforce OAuth flows](https://help.salesforce.com/s/articleView?id=sf.remoteaccess_oauth_flows.htm&type=5)

### Init

`func Init(creds Creds, options ...Option) *Salesforce`

Returns a new Salesforce instance given a user's credentials.

- `creds`: a struct containing the necessary credentials to authenticate into a Salesforce org
- `options`: optional configuration - see [Configuration](#configuration)
- If an operation fails with the Error Code `INVALID_SESSION_ID`, go-salesforce will attempt to refresh the session by resubmitting the same credentials used during initialization
- Configuration values are set to the defaults if not specified

[Client Credentials Flow](https://help.salesforce.com/s/articleView?id=sf.remoteaccess_oauth_client_credentials_flow.htm&type=5)

```go
sf, err := salesforce.Init(salesforce.Creds{
    Domain:         DOMAIN, // ex: https://myslug.my.salesforce.com
    ConsumerKey:    CONSUMER_KEY,
    ConsumerSecret: CONSUMER_SECRET,
})
```

[Username-Password Flow](https://help.salesforce.com/s/articleView?id=sf.remoteaccess_oauth_username_password_flow.htm&type=5)

```go
sf, err := salesforce.Init(salesforce.Creds{
    Domain:         DOMAIN,
    Username:       USERNAME,
    Password:       PASSWORD,
    SecurityToken:  SECURITY_TOKEN,
    ConsumerKey:    CONSUMER_KEY,
    ConsumerSecret: CONSUMER_SECRET,
})
```

[JWT Bearer Flow](https://help.salesforce.com/s/articleView?id=sf.remoteaccess_oauth_jwt_flow.htm&type=5)

```go
sf, err := salesforce.Init(salesforce.Creds{
    Domain:         DOMAIN,
    Username:       USERNAME,
    ConsumerKey:    CONSUMER_KEY,
    ConsumerRSAPem: CONSUMER_RSA_PEM,
})
```

Authenticate with an Access Token

- Implement your own OAuth flow and use the resulting `access_token` from the response to initialize go-salesforce

```go
sf, err := salesforce.Init(salesforce.Creds{
    Domain:      DOMAIN,
    AccessToken: ACCESS_TOKEN,
})
```

### GetAccessToken

`func (sf *Salesforce) GetAccessToken() string`

Returns the current session's Access Token as a string.

```go
token := sf.GetAccessToken()
```

### GetInstanceUrl

`func (sf *Salesforce) GetInstanceUrl() string`

Returns the current session's Instance URL as a string.

```go
url := sf.GetInstanceUrl()
```

### GetAuthFlow

`func (sf *Salesforce) GetAuthFlow() AuthFlowType`

Returns the auth flow used for authentication.

```go
authType := sf.GetAuthFlow()
```

## Configuration

Configure optional parameters for your Salesforce instance

Optional configuration: 
- `func WithCompressionHeaders(compression bool) Option` - see [docs](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/intro_rest_compression.htm)
- `func WithAPIVersion(version string) Option` - set API version manually instead of using default
- `func WithBatchSizeMax(size int)` - for collections API
- `func WithBulkBatchSizeMax(size int) Option` - for Bulk API
- `func WithBulkPollTimeout(timeout time.Duration) Option` - set max wait when polling bulk results with `waitForResults=true`
- `func WithRoundTripper(rt http.RoundTripper) Option` - for http requests
- `func WithHTTPTimeout(timeout time.Duration) Option` - set custom timeout, e.g. for proxy
- `func WithValidateAuthentication(validate bool) Option` - optionally skip validation during certain auth flows
- `func WithTagName(tagName string) Option` - set custom struct tag for mapstructure and csvutil decoders
- `func WithBulkQueryMaxRecords(maxRecords int) Option` - for max number of records per set of results in a bulk query

Get configuration:
- `func (sf *Salesforce) GetAPIVersion() string`
- `func (sf *Salesforce) GetBatchSizeMax() int`
- `func (sf *Salesforce) GetBulkBatchSizeMax() int`
- `func (sf *Salesforce) GetCompressionHeaders() bool`
- `func (sf *Salesforce) GetHTTPClient() *http.Client`
- `func (sf *Salesforce) GetBulkQueryMaxRecords() int`

See [HTTP_CLIENT_CONFIG](https://github.com/k-capehart/go-salesforce/blob/main/HTTP_CLIENT_CONFIG.md) for additional documentation on round trippers

See [examples/functional-config](https://github.com/k-capehart/go-salesforce/blob/main/examples/functional-config/main.go) and [examples/http-config](https://github.com/k-capehart/go-salesforce/blob/main/examples/http-config/main.go) for usage

See [WithHeader](#withheader) for custom header options

## SOQL

Query Salesforce records

- [Review Salesforce REST API resources for queries](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_query.htm)

### Query

`func (sf *Salesforce) Query(query string, sObject any) error`

Performs a SOQL query given a query string and decodes the response into the given struct

- `query`: a SOQL query
- `sObject`: a slice of a custom struct type representing a Salesforce Object

```go
type Contact struct {
    Id       string
    LastName string
}
```

```go
contacts := []Contact{}
err := sf.Query("SELECT Id, LastName FROM Contact WHERE LastName = 'Lee'", &contacts)
```

### QueryStruct

`func (sf *Salesforce) QueryStruct(soqlStruct any, sObject any) error`

Performs a SOQL query given a go-soql struct and decodes the response into the given struct

- `soqlStruct`: a custom struct using `soql` tags
- `sObject`: a slice of a custom struct type representing a Salesforce Object
- Review [forcedotcom/go-soql](https://github.com/forcedotcom/go-soql)
  - Eliminates need to separately maintain query string and struct
  - Helps prevent SOQL injection

```go
type Contact struct {
    Id       string `soql:"selectColumn,fieldName=Id" json:"Id"`
    LastName string `soql:"selectColumn,fieldName=LastName" json:"LastName"`
}

type ContactQueryCriteria struct {
    LastName string `soql:"equalsOperator,fieldName=LastName"`
}

type ContactSoqlQuery struct {
    SelectClause Contact              `soql:"selectClause,tableName=Contact"`
    WhereClause  ContactQueryCriteria `soql:"whereClause"`
}
```

```go
soqlStruct := ContactSoqlQuery{
    SelectClause: Contact{},
    WhereClause: ContactQueryCriteria{
        LastName: "Lee",
    },
}
contacts := []Contact{}
err := sf.QueryStruct(soqlStruct, &contacts)
```

### Handling Relationship Queries

When querying Salesforce objects, it's common to access fields that are related through parent-child or lookup relationships. For instance, querying `Account.Name` with related `Contact` might look like this:

```go
type Account struct {
    Name string
}

type Contact struct {
    Id       string
    Account Account
}

contacts := []Contact{}
sf.Query("SELECT Id, Account.Name FROM Contact", &contacts)
```

## DML

Note that any DML operation that includes an uninitialized struct field, or 0 or null value, will effectively be treated as passing a null value to Salesforce.

```go
type Contact struct {
    Id        string
    LastName  string
    FirstName string
}

contact := Contact{
    Id:       "003Dn00000pEYQSIA4",
    LastName: "Banner",
}
err = sf.UpdateOne("Contact", contact) // will update the FirstName of the contact to an empty string ""
```

## SObject Single Record Operations

Insert, Update, Upsert, or Delete one record at a time

- [Review Salesforce REST API resources for working with records](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/using_resources_working_with_records.htm?q=update)
- Only Insert and Upsert will return an instance of `SalesforceResult`, which contains the record ID
- DML errors result in a status code of 400

### InsertOne

`func (sf *Salesforce) InsertOne(sObjectName string, record any) (SalesforceResult, error)`

InsertOne inserts one salesforce record of the given type

- `sObjectName`: API name of Salesforce object
- `record`: a Salesforce object record

```go
type Contact struct {
    LastName string
}
```

```go
contact := Contact{
    LastName: "Stark",
}
result, err := sf.InsertOne("Contact", contact)
```

### UpdateOne

`func (sf *Salesforce) UpdateOne(sObjectName string, record any) error`

Updates one salesforce record of the given type

- `sObjectName`: API name of Salesforce object
- `record`: a Salesforce object record
  - An Id is required

```go
type Contact struct {
    Id       string
    LastName string
}
```

```go
contact := Contact{
    Id:       "003Dn00000pEYQSIA4",
    LastName: "Banner",
}
err := sf.UpdateOne("Contact", contact)
```

### UpsertOne

`func (sf *Salesforce) UpsertOne(sObjectName string, externalIdFieldName string, record any) (SalesforceResult, error)`

Updates (or inserts) one salesforce record using the given external Id

- `sObjectName`: API name of Salesforce object
- `externalIdFieldName`: field API name for an external Id that exists on the given object
- `record`: a Salesforce object record
  - A value for the External Id is required

```go
type Contact struct {
    ContactExternalId__c string
    LastName             string
}
```

```go
contact := Contact{
    ContactExternalId__c: "Avng0",
    LastName:             "Rogers",
}
result, err := sf.UpsertOne("Contact", "ContactExternalId__c", contact)
```

### DeleteOne

`func (sf *Salesforce) DeleteOne(sObjectName string, record any) error`

Deletes a Salesforce record

- `sObjectName`: API name of Salesforce object
- `record`: a Salesforce object record
  - Should only contain an Id

```go
type Contact struct {
    Id       string
}
```

```go
contact := Contact{
    Id: "003Dn00000pEYQSIA4",
}
err := sf.DeleteOne("Contact", contact)
```

## SObject Collections

Insert, Update, Upsert, or Delete collections of records

- [Review Salesforce REST API resources for working with collections](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_composite_sobjects_collections.htm)
- Perform operations in batches of up to 200 records at a time
- Consider making a Bulk request for very large operations
- Partial successes are enabled
  - If a record fails then successes are still committed to the database
- Will return an instance of `SalesforceResults` which contains information on each affected record and whether DML errors were encountered

### InsertCollection

`func (sf *Salesforce) InsertCollection(sObjectName string, records any, batchSize int) (SalesforceResults, error)`

Inserts a list of salesforce records of the given type

- `sObjectName`: API name of Salesforce object
- `records`: a slice of salesforce records
- `batchSize`: `1 <= batchSize <= 200`

```go
type Contact struct {
    LastName string
}
```

```go
contacts := []Contact{
    {
        LastName: "Barton",
    },
    {
        LastName: "Romanoff",
    },
}
results, err := sf.InsertCollection("Contact", contacts, 200)
```

### UpdateCollection

`func (sf *Salesforce) UpdateCollection(sObjectName string, records any, batchSize int) (SalesforceResults, error)`

Updates a list of salesforce records of the given type

- `sObjectName`: API name of Salesforce object
- `records`: a slice of salesforce records
  - An Id is required
- `batchSize`: `1 <= batchSize <= 200`

```go
type Contact struct {
    Id       string
    LastName string
}
```

```go
contacts := []Contact{
    {
        Id:       "003Dn00000pEfyAIAS",
        LastName: "Fury",
    },
    {
        Id:       "003Dn00000pEfy9IAC",
        LastName: "Odinson",
    },
}
results, err := sf.UpdateCollection("Contact", contacts, 200)
```

### UpsertCollection

`func (sf *Salesforce) UpsertCollection(sObjectName string, externalIdFieldName string, records any, batchSize int) (SalesforceResults, error)`

Updates (or inserts) a list of salesforce records using the given ExternalId

- `sObjectName`: API name of Salesforce object
- `externalIdFieldName`: field API name for an external Id that exists on the given object
- `records`: a slice of salesforce records
  - A value for the External Id is required
- `batchSize`: `1 <= batchSize <= 200`

```go
type Contact struct {
    ContactExternalId__c string
    LastName             string
}
```

```go
contacts := []Contact{
    {
        ContactExternalId__c: "Avng1",
        LastName:             "Danvers",
    },
    {
        ContactExternalId__c: "Avng2",
        LastName:             "Pym",
    },
}
results, err := sf.UpsertCollection("Contact", "ContactExternalId__c", contacts, 200)
```

### DeleteCollection

`func (sf *Salesforce) DeleteCollection(sObjectName string, records any, batchSize int) (SalesforceResults, error)`

Deletes a list of salesforce records

- `sObjectName`: API name of Salesforce object
- `records`: a slice of salesforce records
  - Should only contain Ids
- `batchSize`: `1 <= batchSize <= 200`

```go
type Contact struct {
    Id       string
}
```

```go
contacts := []Contact{
    {
        Id: "003Dn00000pEfyAIAS",
    },
    {
        Id: "003Dn00000pEfy9IAC",
    },
}
results, err := sf.DeleteCollection("Contact", contacts, 200)
```

## Composite Requests

Make numerous 'subrequests' contained within a single 'composite request', reducing the overall number of calls to Salesforce

- [Review Salesforce REST API resources for making composite requests](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_composite_composite_post.htm)
- Up to 25 subrequests may be included in a single composite request
  - For DML operations, max number of records to be processed is determined by batch size (`25 * (batch size)`)
  - So if batch size is 1, then max number of records to be included in request is 25
  - If batch size is 200, then max is 5000
- If allOrNone is true, then records are only committed to database if everything succeeds
- Will return an instance of SalesforceResults which contains information on each affected record and whether DML errors were encountered

### InsertComposite

`func (sf *Salesforce) InsertComposite(sObjectName string, records any, batchSize int, allOrNone bool) (SalesforceResults, error)`

Inserts a list of salesforce records in a single request

- `sObjectName`: API name of Salesforce object
- `records`: a slice of salesforce records
- `batchSize`: `1 <= batchSize <= 200`
- `allOrNone`: denotes whether to roll back entire operation if a record fails

```go
type Contact struct {
    LastName string
}
```

```go
contacts := []Contact{
    {
        LastName: "Parker",
    },
    {
        LastName: "Murdock",
    },
}
results, err := sf.InsertComposite("Contact", contacts, 200, true)
```

### UpdateComposite

`func (sf *Salesforce) UpdateComposite(sObjectName string, records any, batchSize int, allOrNone bool) (SalesforceResults, error)`

Updates a list of salesforce records in a single request

- `sObjectName`: API name of Salesforce object
- `records`: a slice of salesforce records
  - An Id is required
- `batchSize`: `1 <= batchSize <= 200`
- `allOrNone`: denotes whether to roll back entire operation if a record fails

```go
type Contact struct {
    Id       string
    LastName string
}
```

```go
contacts := []Contact{
    {
        Id:       "003Dn00000pEi32IAC",
        LastName: "Richards",
    },
    {
        Id:       "003Dn00000pEi31IAC",
        LastName: "Storm",
    },
}
results, err := sf.UpdateComposite("Contact", contacts, 200, true)
```

### UpsertComposite

`func (sf *Salesforce) UpsertComposite(sObjectName string, externalIdFieldName string, records any, batchSize int, allOrNone bool) (SalesforceResults, error)`

Updates (or inserts) a list of salesforce records using the given ExternalId in a single request

- `sObjectName`: API name of Salesforce object
- `externalIdFieldName`: field API name for an external Id that exists on the given object
- `records`: a slice of salesforce records
  - A value for the External Id is required
- `batchSize`: `1 <= batchSize <= 200`
- `allOrNone`: denotes whether to roll back entire operation if a record fails

```go
type Contact struct {
    ContactExternalId__c string
    LastName             string
}
```

```go
contacts := []Contact{
    {
        ContactExternalId__c: "Avng3",
        LastName:             "Maximoff",
    },
    {
        ContactExternalId__c: "Avng4",
        LastName:             "Wilson",
    },
}
results, err := sf.UpsertComposite("Contact", "ContactExternalId__c", contacts, 200, true)
```

### DeleteComposite

`func (sf *Salesforce) DeleteComposite(sObjectName string, records any, batchSize int, allOrNone bool) (SalesforceResults, error)`

Deletes a list of salesforce records in a single request

- `sObjectName`: API name of Salesforce object

Frequently Asked Questions

What is go-salesforce?

go-salesforce is a Third-party APIs library for the Go programming language. Go client library for interacting with the [Salesforce REST API](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_list.htm)

How do I install go-salesforce?

Install go-salesforce with the Go module system using `go get k-capehart/go-salesforce`. Check the repository for the current installation instructions.

What category does go-salesforce belong to?

go-salesforce is listed under Third-party APIs, specifically Third-party APIs.

← Back to Third-party APIs