When evaluating AI solutions for Salesforce, teams often default to Einstein and its associated tools like Prompt Builder and Agent Builder. However, factors such as cost, usage patterns, vendor lock-in, or specific strategic requirements may lead developers to explore alternative AI stacks. This exploration aims to determine if it's feasible to utilize an external AI stack without resorting to managing bare-metal infrastructure, focusing on DigitalOcean's managed AI capabilities and its FaaS (Functions as a Service) offering.
Historically, Heroku served as a viable platform for extending Salesforce's capabilities beyond Apex, offering language support, scalability, and managed AI add-ons. The recent AppLink feature further facilitated offloading computational tasks. With Heroku transitioning to maintenance mode, the search for robust alternatives becomes critical.
This article examines DigitalOcean's FaaS and managed inference features and their integration potential with Salesforce. The platform's commitment to simplicity, similar to Heroku's developer experience, allows developers to focus on core logic. Support for build packs, including the Cloud Native Buildpack standard, is also noted. For hands-on exploration, sample code and deployment steps are available in the digitalocean-salesforce-demos repository.
Functions as a Service (FaaS)
DigitalOcean Functions offer a serverless compute model, scaling down to zero when inactive. This approach minimizes boilerplate code and infrastructure management, requiring only the function logic itself for deployment. To streamline Salesforce integration, similar to Heroku AppLink, these Functions support OpenAPI schemas.
Adopting a contract-driven API approach, OpenAPI annotations are embedded directly within the function code using Swagger tooling. These annotations are then processed into an OpenAPI specification suitable for Salesforce External Services.
/**
* @openapi
* /hello:
* get:
* operationId: hello
* summary: Hello from DigitalOcean
* parameters:
* - name: name
* in: query
* schema: { type: string }
* responses:
* '200':
* description: Greeting
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/HelloResponse'
* post:
* operationId: helloPost
* # ...same greeting via JSON body { "name": "…" }
*/
/**
* @openapi
* components:
* schemas:
* HelloResponse:
* type: object
* properties:
* message: { type: string }
* name: { type: string }
* source: { type: string }
*/
// Build the greeting payload returned to the caller
function greet(name) {
const who = (name && String(name).trim()) || 'world';
return {
message: `Hello from DigitalOcean, ${who}!`,
name: who,
source: 'DigitalOcean Functions',
};
}
// DigitalOcean Functions entry point
function main(event = {}) {
return {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: greet(event.name),
};
}
module.exports.main = main;
The deployment requires minimal files: a single .js file containing the function logic and a project.yml file to define the function's configuration.
# packages/sfdemo/
# └── hello/
# └── hello.js
# Function + @openapi JSDoc
project.yml
# package/function config for doctl
The project.yml specifies the function as a public web function requiring secure web authentication, with the secret managed via an environment variable.
packages:
- name: sfdemo
functions:
- name: hello
runtime: nodejs:22
web: true
# Secure Web Function — callers send X-Require-Whisk-Auth
webSecure: ${DO_FUNCTIONS_WEB_SECRET}
Deployment using doctl commands involves authentication, installation of the serverless plugin, namespace creation, and deploying the functions package. Secure web functions are invoked by including the secret in the X-Require-Whisk-Auth header.
# Authenticate doctl and install the serverless plugin
doctl auth init
doctl serverless install
doctl serverless namespaces create --label sfdemo --region nyc1
# Project root + Secure Web Function secret
cd blog/playgrounds/functions
echo "DO_FUNCTIONS_WEB_SECRET=$(openssl rand -hex 32)" >> .env
# Build & deploy the Functions package
doctl serverless deploy . --remote-build
# Call with the Secure Web Function secret
SECRET=$(grep '^DO_FUNCTIONS_WEB_SECRET=' .env | cut -d= -f2-)
URL=$(doctl serverless functions get sfdemo/hello --url)
curl -sS -H "X-Require-Whisk-Auth: ${SECRET}" \
-G "${URL}" --data-urlencode "name=Salesforce" | jq .
# Function response
# {
# "message": "Hello from DigitalOcean, Salesforce!",
# "name": "Salesforce",
# "source": "DigitalOcean Functions"
# }
Out-of-the-box authentication for public functions includes secret-based tokens or custom authentication. Functions can also be made private and invoked solely through DigitalOcean Functions APIs. The provided samples are public and utilize secret-based authentication for invoking functions, while callbacks to Salesforce use admin-approved users.
DigitalOcean's dashboard offers an overview of deployed functions, with capabilities for code review and testing directly within the interface.
To invoke these functions from Salesforce using External Services, a Named Credential is required to manage the URL and authentication secret. OpenAPI schemas generated from Swagger tooling facilitate this setup. An automated script (./bin/functions-apex.sh) can handle the synchronization of Named Credential URLs, generation of External Service metadata, and deployment.
This setup results in an External Service that can be invoked from Apex. The following example demonstrates calling the hello operation:
// Generated External Service client for DigitalOceanFunctions
ExternalService.DigitalOceanFunctions svc =
new ExternalService.DigitalOceanFunctions();
// Build the hello operation request
ExternalService.DigitalOceanFunctions.hello_Request req =
new ExternalService.DigitalOceanFunctions.hello_Request();
req.name = 'Salesforce';
// Invoke the Function via Named Credential + External Service
ExternalService.DigitalOceanFunctions.hello_Response res = svc.hello(req);
System.debug(res.Code200.message); // Hello from DigitalOcean, Salesforce!
External Services not only expose functions to Apex but also to Flow and Agentforce, enabling declarative builders to utilize them as actions.
Callbacks to Salesforce
By leveraging External Client Apps (the successors to Connected Apps) and OAuth JWT authentication, functions can gain callback access into Salesforce, allowing them to query and update data, subject to the calling user's permissions.
The following example demonstrates a function that counts Account records in Salesforce using JWT Bearer authentication and REST helpers:
// JWT Bearer + REST helpers (username comes from Named Credential headers)
const sf = require('./salesforce');
// Authenticate as the calling user, then COUNT Accounts
async function countAccounts(event = {}) {
const username = sf.usernameFromEvent(event);
const token = await sf.getAccessToken(event, username);
const result = await sf.query(
token.access_token,
token.instance_url,
'SELECT COUNT() FROM Account',
event
);
return {
objectName: 'Account',
count: result.totalSize,
source: 'DigitalOcean Functions → Salesforce SOQL (JWT Bearer + fetch)',
};
}
// DigitalOcean Functions entry point
async function main(event = {}) {
try {
return {
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: await countAccounts(event),
};
} catch (err) {
return {
statusCode: 500,
headers: { 'Content-Type': 'application/json' },
body: { error: err.message || String(err) },
};
}
}
module.exports.main = main;
Dynamic header values, a feature of Named Credentials, can be used to pass the calling user's identity. The productCount function in the sample repository showcases the use of the Heroku AppLink Node.js library, facilitating easier porting of existing AppLink code and providing Unit of Work capabilities for transactional data access.
Knowledge Bases – Retrieval Augmented Generation (RAG)
Managed databases like PostgreSQL with vector similarity search (e.g., pgvector) are foundational for pipelines that ingest documents, generate embeddings, and perform searches. DigitalOcean's Knowledge Bases offer an API to provision a knowledge base, upload files as data sources, and have the platform index them for retrieval and semantic search. This managed approach reduces the need for extensive plumbing, allowing focus on scenario implementation.
DigitalOcean's Serverless Inference and its Inference Router feature are also valuable, enabling routing requests across chosen models to manage costs. The API-first design of DigitalOcean's platform, with APIs described using OpenAPI, makes them readily consumable by agents and Salesforce.
A native Salesforce experience can be built on top of DigitalOcean's Knowledge Bases, allowing users to upload files and ask questions against the indexed content. This integration uses External Services to directly call DigitalOcean's platform APIs.
The UI displays preparation status while DigitalOcean indexes uploaded files. The DigitalOcean dashboard provides a view of uploaded data sources, indexing status, and a RAG Playground for interactive testing.
Key Takeaways
- DigitalOcean provides a managed AI and FaaS platform that can serve as a viable alternative to Salesforce's native AI offerings.
- Functions as a Service (FaaS) allows developers to deploy custom logic externally and integrate it with Salesforce via OpenAPI specifications and External Services.
- External Services bridge the gap, exposing DigitalOcean Functions to Apex, Flow, and Agentforce.
- Functions can securely call back into Salesforce using JWT Bearer authentication and External Client Apps.
- DigitalOcean Knowledge Bases offer a managed solution for RAG pipelines, simplifying document ingestion, embedding, and semantic search.
- The API-first design of DigitalOcean's platform, with OpenAPI documentation, facilitates integration with Salesforce and other enterprise systems.
Leave a Comment