This repository was archived by the owner on Apr 20, 2026. It is now read-only.

Repository files navigation

npm

Node client for the Salesforce Pub/Sub API

See the official Pub/Sub API repo for more information on the Salesforce gRPC-based Pub/Sub API.

Installation and Configuration

Install the client library with npm install salesforce-pubsub-api-client.

Create a .env file at the root of the project for configuration.

Pick one of these authentication flows and fill the relevant configuration:

  • User supplied authentication
  • Username/password authentication (recommended for tests)
  • OAuth 2.0 client credentials
  • OAuth 2.0 JWT Bearer (recommended for production)

Note
The default client logger is fine for a test environement but you'll want to switch to a custom logger with asynchronous logging for increased performance.

User supplied authentication

If you already have a Salesforce client in your app, you can reuse its authentication information. You only need this minimal configuration:

SALESFORCE_AUTH_TYPE=user-supplied
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

When connecting to the Pub/Sub API, use the following method instead of the standard connect() method to specify authentication information:

awaitclient.connectWithAuth(accessToken,instanceUrl,organizationId,username);

Username/password flow

Warning
Relying on a username/password authentication flow for production is not recommended. Consider switching to JWT auth for extra security.

SALESFORCE_AUTH_TYPE=username-password
SALESFORCE_LOGIN_URL=https://login.salesforce.com
SALESFORCE_USERNAME=YOUR_SALESFORCE_USERNAME
SALESFORCE_PASSWORD=YOUR_SALESFORCE_PASSWORD
SALESFORCE_TOKEN=YOUR_SALESFORCE_USER_SECURITY_TOKEN
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

OAuth 2.0 client credentials flow (client_credentials)

SALESFORCE_AUTH_TYPE=oauth-client-credentials
SALESFORCE_LOGIN_URL=YOUR_DOMAIN_URL
SALESFORCE_CLIENT_ID=YOUR_CONNECTED_APP_CLIENT_ID
SALESFORCE_CLIENT_SECRET=YOUR_CONNECTED_APP_CLIENT_SECRET
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

OAuth 2.0 JWT bearer flow

This is the most secure authentication option. Recommended for production use.

SALESFORCE_AUTH_TYPE=oauth-jwt-bearer
SALESFORCE_LOGIN_URL=https://login.salesforce.com
SALESFORCE_CLIENT_ID=YOUR_CONNECTED_APP_CLIENT_ID
SALESFORCE_USERNAME=YOUR_SALESFORCE_USERNAME
SALESFORCE_PRIVATE_KEY_FILE=PATH_TO_YOUR_KEY_FILE
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

Basic Example

Here's an example that will get you started quickly. It listens to a single account change event.

  1. Activate Account change events in Salesforce Setup > Change Data Capture.

  2. Create a sample.js file with this content:

    importPubSubApiClientfrom'salesforce-pubsub-api-client';asyncfunctionrun(){try{constclient=newPubSubApiClient();awaitclient.connect();// Subscribe to a single incoming account change eventsconsteventEmitter=awaitclient.subscribe('/data/AccountChangeEvent',1);// Handle incoming eventseventEmitter.on('data',(event)=>{console.log(`Handling ${event.payload.ChangeEventHeader.entityName} change event ${event.replayId}`);console.log(JSON.stringify(event,null,2));});}catch(error){console.error(error);}}run();
  3. Run the project with node sample.js

    If everything goes well, you'll see output like this:

    Connected to Salesforce org https://pozil-dev-ed.my.salesforce.com as grpc@pozil.com
    Connected to Pub/Sub API endpoint api.pubsub.salesforce.com:7443
    Topic schema loaded: /data/AccountChangeEvent
    Subscribe request sent for 1 events from /data/AccountChangeEvent...
    

    At this point the script will be on hold and will wait for events.

  4. Modify an account record in Salesforce. This fires an account change event.

    Once the client receives an event, it will display it like this:

    Received 1 events, latest replay ID: 17093000
    Handling Account change event 17093000
    {
    "replayId": 17093000,
    "payload": {
    "ChangeEventHeader": {
    "entityName": "Account",
    "recordIds": [
    "0014H00002LbR7QQAV"
    ],
    "changeType": "UPDATE",
    "changeOrigin": "com/salesforce/api/soap/56.0;client=SfdcInternalAPI/",
    "transactionKey": "0005349f-124e-0df1-3a25-f551ab84d237",
    "sequenceNumber": 1,
    "commitTimestamp": 1672428268000,
    "commitNumber": 11449587527037,
    "commitUser": "00558000000yFyDAAU",
    "nulledFields": [],
    "diffFields": [],
    "changedFields": [
    "Rating",
    "LastModifiedDate"
    ]
    },
    "Name": null,
    "Type": null,
    "ParentId": null,
    "BillingAddress": null,
    "ShippingAddress": null,
    "Phone": null,
    "Fax": null,
    "AccountNumber": null,
    "Website": null,
    "Sic": null,
    "Industry": null,
    "AnnualRevenue": null,
    "NumberOfEmployees": null,
    "Ownership": null,
    "TickerSymbol": null,
    "Description": null,
    "Rating": {
    "string": "Hot"
    },
    "Site": null,
    "OwnerId": null,
    "CreatedDate": null,
    "CreatedById": null,
    "LastModifiedDate": {
    "long": 1672428268000
    },
    ...
    }
    }
    

    Note that the change event payloads include all object fields but fields that haven't changed are null. Use the values from ChangeEventHeader.nulledFields, ChangeEventHeader.diffFields and ChangeEventHeader.changedFields to identify actual value changes.

    After receiving the number of requested events, the script will terminate with these messages:

    gRPC stream status: {
    code: 0,
    details: '',
    metadata: Metadata { _internal_repr: {}, flags: 0 }
    }
    gRPC stream ended
    

Other Examples

Publish a platform event

Publish a Sample__e Platform Event with a Message__c field:

constpayload={CreatedDate: newDate().getTime(),// Non-null value required but there's no validity check performed on this fieldCreatedById: 'someone',// Non-null value required but there's no validity check performed on this fieldMessage__c: {string: 'Hello world'}// Field is nullable so we need to specify the 'string' type};constpublishResult=awaitclient.publish('/event/Sample__e',payload);console.log('Published event: ',JSON.stringify(publishResult));

Subscribe with a replay ID

Subscribe to account change events starting from a replay ID:

consteventEmitter=awaitclient.subscribeFromReplayId('/data/AccountChangeEvent',5,17092989);

Subscribe to past events in retention window

Subscribe to past account change events in retention window:

consteventEmitter=awaitclient.subscribeFromEarliestEvent('/data/AccountChangeEvent',3);

Handle gRPC stream lifecycle events

Use the EventEmmitter returned by subscribe methods to handle gRPC stream lifecycle events:

// Stream endeventEmitter.on('end',()=>{console.log('gRPC stream ended');});// Stream erroreventEmitter.on('error',(error)=>{console.error('gRPC stream error: ',JSON.stringify(error));});// Stream status updateeventEmitter.on('status',(status)=>{console.log('gRPC stream status: ',status);});

Use a custom logger

The client logs output to the console by default but you can provide your favorite logger in the client constructor.

When in production, asynchronous logging is preferable for performance reasons.

For example:

importpinofrom'pino';constlogger=pino();constclient=newPubSubApiClient(logger);

About

A node client for the Salesforce Pub/Sub API

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
This repository was archived by the owner on Apr 20, 2026. It is now read-only.

Repository files navigation

npm

Node client for the Salesforce Pub/Sub API

See the official Pub/Sub API repo for more information on the Salesforce gRPC-based Pub/Sub API.

Installation and Configuration

Install the client library with npm install salesforce-pubsub-api-client.

Create a .env file at the root of the project for configuration.

Pick one of these authentication flows and fill the relevant configuration:

  • User supplied authentication
  • Username/password authentication (recommended for tests)
  • OAuth 2.0 client credentials
  • OAuth 2.0 JWT Bearer (recommended for production)

Note
The default client logger is fine for a test environement but you'll want to switch to a custom logger with asynchronous logging for increased performance.

User supplied authentication

If you already have a Salesforce client in your app, you can reuse its authentication information. You only need this minimal configuration:

SALESFORCE_AUTH_TYPE=user-supplied
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

When connecting to the Pub/Sub API, use the following method instead of the standard connect() method to specify authentication information:

awaitclient.connectWithAuth(accessToken,instanceUrl,organizationId,username);

Username/password flow

Warning
Relying on a username/password authentication flow for production is not recommended. Consider switching to JWT auth for extra security.

SALESFORCE_AUTH_TYPE=username-password
SALESFORCE_LOGIN_URL=https://login.salesforce.com
SALESFORCE_USERNAME=YOUR_SALESFORCE_USERNAME
SALESFORCE_PASSWORD=YOUR_SALESFORCE_PASSWORD
SALESFORCE_TOKEN=YOUR_SALESFORCE_USER_SECURITY_TOKEN
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

OAuth 2.0 client credentials flow (client_credentials)

SALESFORCE_AUTH_TYPE=oauth-client-credentials
SALESFORCE_LOGIN_URL=YOUR_DOMAIN_URL
SALESFORCE_CLIENT_ID=YOUR_CONNECTED_APP_CLIENT_ID
SALESFORCE_CLIENT_SECRET=YOUR_CONNECTED_APP_CLIENT_SECRET
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

OAuth 2.0 JWT bearer flow

This is the most secure authentication option. Recommended for production use.

SALESFORCE_AUTH_TYPE=oauth-jwt-bearer
SALESFORCE_LOGIN_URL=https://login.salesforce.com
SALESFORCE_CLIENT_ID=YOUR_CONNECTED_APP_CLIENT_ID
SALESFORCE_USERNAME=YOUR_SALESFORCE_USERNAME
SALESFORCE_PRIVATE_KEY_FILE=PATH_TO_YOUR_KEY_FILE
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

Basic Example

Here's an example that will get you started quickly. It listens to a single account change event.

  1. Activate Account change events in Salesforce Setup > Change Data Capture.

  2. Create a sample.js file with this content:

    importPubSubApiClientfrom'salesforce-pubsub-api-client';asyncfunctionrun(){try{constclient=newPubSubApiClient();awaitclient.connect();// Subscribe to a single incoming account change eventsconsteventEmitter=awaitclient.subscribe('/data/AccountChangeEvent',1);// Handle incoming eventseventEmitter.on('data',(event)=>{console.log(`Handling ${event.payload.ChangeEventHeader.entityName} change event ${event.replayId}`);console.log(JSON.stringify(event,null,2));});}catch(error){console.error(error);}}run();
  3. Run the project with node sample.js

    If everything goes well, you'll see output like this:

    Connected to Salesforce org https://pozil-dev-ed.my.salesforce.com as grpc@pozil.com
    Connected to Pub/Sub API endpoint api.pubsub.salesforce.com:7443
    Topic schema loaded: /data/AccountChangeEvent
    Subscribe request sent for 1 events from /data/AccountChangeEvent...
    

    At this point the script will be on hold and will wait for events.

  4. Modify an account record in Salesforce. This fires an account change event.

    Once the client receives an event, it will display it like this:

    Received 1 events, latest replay ID: 17093000
    Handling Account change event 17093000
    {
    "replayId": 17093000,
    "payload": {
    "ChangeEventHeader": {
    "entityName": "Account",
    "recordIds": [
    "0014H00002LbR7QQAV"
    ],
    "changeType": "UPDATE",
    "changeOrigin": "com/salesforce/api/soap/56.0;client=SfdcInternalAPI/",
    "transactionKey": "0005349f-124e-0df1-3a25-f551ab84d237",
    "sequenceNumber": 1,
    "commitTimestamp": 1672428268000,
    "commitNumber": 11449587527037,
    "commitUser": "00558000000yFyDAAU",
    "nulledFields": [],
    "diffFields": [],
    "changedFields": [
    "Rating",
    "LastModifiedDate"
    ]
    },
    "Name": null,
    "Type": null,
    "ParentId": null,
    "BillingAddress": null,
    "ShippingAddress": null,
    "Phone": null,
    "Fax": null,
    "AccountNumber": null,
    "Website": null,
    "Sic": null,
    "Industry": null,
    "AnnualRevenue": null,
    "NumberOfEmployees": null,
    "Ownership": null,
    "TickerSymbol": null,
    "Description": null,
    "Rating": {
    "string": "Hot"
    },
    "Site": null,
    "OwnerId": null,
    "CreatedDate": null,
    "CreatedById": null,
    "LastModifiedDate": {
    "long": 1672428268000
    },
    ...
    }
    }
    

    Note that the change event payloads include all object fields but fields that haven't changed are null. Use the values from ChangeEventHeader.nulledFields, ChangeEventHeader.diffFields and ChangeEventHeader.changedFields to identify actual value changes.

    After receiving the number of requested events, the script will terminate with these messages:

    gRPC stream status: {
    code: 0,
    details: '',
    metadata: Metadata { _internal_repr: {}, flags: 0 }
    }
    gRPC stream ended
    

Other Examples

Publish a platform event

Publish a Sample__e Platform Event with a Message__c field:

constpayload={CreatedDate: newDate().getTime(),// Non-null value required but there's no validity check performed on this fieldCreatedById: 'someone',// Non-null value required but there's no validity check performed on this fieldMessage__c: {string: 'Hello world'}// Field is nullable so we need to specify the 'string' type};constpublishResult=awaitclient.publish('/event/Sample__e',payload);console.log('Published event: ',JSON.stringify(publishResult));

Subscribe with a replay ID

Subscribe to account change events starting from a replay ID:

consteventEmitter=awaitclient.subscribeFromReplayId('/data/AccountChangeEvent',5,17092989);

Subscribe to past events in retention window

Subscribe to past account change events in retention window:

consteventEmitter=awaitclient.subscribeFromEarliestEvent('/data/AccountChangeEvent',3);

Handle gRPC stream lifecycle events

Use the EventEmmitter returned by subscribe methods to handle gRPC stream lifecycle events:

// Stream endeventEmitter.on('end',()=>{console.log('gRPC stream ended');});// Stream erroreventEmitter.on('error',(error)=>{console.error('gRPC stream error: ',JSON.stringify(error));});// Stream status updateeventEmitter.on('status',(status)=>{console.log('gRPC stream status: ',status);});

Use a custom logger

The client logs output to the console by default but you can provide your favorite logger in the client constructor.

When in production, asynchronous logging is preferable for performance reasons.

For example:

importpinofrom'pino';constlogger=pino();constclient=newPubSubApiClient(logger);

About

A node client for the Salesforce Pub/Sub API

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Apr 20, 2026. It is now read-only.

Repository files navigation

npm

Node client for the Salesforce Pub/Sub API

See the official Pub/Sub API repo for more information on the Salesforce gRPC-based Pub/Sub API.

Installation and Configuration

Install the client library with npm install salesforce-pubsub-api-client.

Create a .env file at the root of the project for configuration.

Pick one of these authentication flows and fill the relevant configuration:

  • User supplied authentication
  • Username/password authentication (recommended for tests)
  • OAuth 2.0 client credentials
  • OAuth 2.0 JWT Bearer (recommended for production)

Note
The default client logger is fine for a test environement but you'll want to switch to a custom logger with asynchronous logging for increased performance.

User supplied authentication

If you already have a Salesforce client in your app, you can reuse its authentication information. You only need this minimal configuration:

SALESFORCE_AUTH_TYPE=user-supplied
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

When connecting to the Pub/Sub API, use the following method instead of the standard connect() method to specify authentication information:

awaitclient.connectWithAuth(accessToken,instanceUrl,organizationId,username);

Username/password flow

Warning
Relying on a username/password authentication flow for production is not recommended. Consider switching to JWT auth for extra security.

SALESFORCE_AUTH_TYPE=username-password
SALESFORCE_LOGIN_URL=https://login.salesforce.com
SALESFORCE_USERNAME=YOUR_SALESFORCE_USERNAME
SALESFORCE_PASSWORD=YOUR_SALESFORCE_PASSWORD
SALESFORCE_TOKEN=YOUR_SALESFORCE_USER_SECURITY_TOKEN
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

OAuth 2.0 client credentials flow (client_credentials)

SALESFORCE_AUTH_TYPE=oauth-client-credentials
SALESFORCE_LOGIN_URL=YOUR_DOMAIN_URL
SALESFORCE_CLIENT_ID=YOUR_CONNECTED_APP_CLIENT_ID
SALESFORCE_CLIENT_SECRET=YOUR_CONNECTED_APP_CLIENT_SECRET
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

OAuth 2.0 JWT bearer flow

This is the most secure authentication option. Recommended for production use.

SALESFORCE_AUTH_TYPE=oauth-jwt-bearer
SALESFORCE_LOGIN_URL=https://login.salesforce.com
SALESFORCE_CLIENT_ID=YOUR_CONNECTED_APP_CLIENT_ID
SALESFORCE_USERNAME=YOUR_SALESFORCE_USERNAME
SALESFORCE_PRIVATE_KEY_FILE=PATH_TO_YOUR_KEY_FILE
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

Basic Example

Here's an example that will get you started quickly. It listens to a single account change event.

  1. Activate Account change events in Salesforce Setup > Change Data Capture.

  2. Create a sample.js file with this content:

    importPubSubApiClientfrom'salesforce-pubsub-api-client';asyncfunctionrun(){try{constclient=newPubSubApiClient();awaitclient.connect();// Subscribe to a single incoming account change eventsconsteventEmitter=awaitclient.subscribe('/data/AccountChangeEvent',1);// Handle incoming eventseventEmitter.on('data',(event)=>{console.log(`Handling ${event.payload.ChangeEventHeader.entityName} change event ${event.replayId}`);console.log(JSON.stringify(event,null,2));});}catch(error){console.error(error);}}run();
  3. Run the project with node sample.js

    If everything goes well, you'll see output like this:

    Connected to Salesforce org https://pozil-dev-ed.my.salesforce.com as grpc@pozil.com
    Connected to Pub/Sub API endpoint api.pubsub.salesforce.com:7443
    Topic schema loaded: /data/AccountChangeEvent
    Subscribe request sent for 1 events from /data/AccountChangeEvent...
    

    At this point the script will be on hold and will wait for events.

  4. Modify an account record in Salesforce. This fires an account change event.

    Once the client receives an event, it will display it like this:

    Received 1 events, latest replay ID: 17093000
    Handling Account change event 17093000
    {
    "replayId": 17093000,
    "payload": {
    "ChangeEventHeader": {
    "entityName": "Account",
    "recordIds": [
    "0014H00002LbR7QQAV"
    ],
    "changeType": "UPDATE",
    "changeOrigin": "com/salesforce/api/soap/56.0;client=SfdcInternalAPI/",
    "transactionKey": "0005349f-124e-0df1-3a25-f551ab84d237",
    "sequenceNumber": 1,
    "commitTimestamp": 1672428268000,
    "commitNumber": 11449587527037,
    "commitUser": "00558000000yFyDAAU",
    "nulledFields": [],
    "diffFields": [],
    "changedFields": [
    "Rating",
    "LastModifiedDate"
    ]
    },
    "Name": null,
    "Type": null,
    "ParentId": null,
    "BillingAddress": null,
    "ShippingAddress": null,
    "Phone": null,
    "Fax": null,
    "AccountNumber": null,
    "Website": null,
    "Sic": null,
    "Industry": null,
    "AnnualRevenue": null,
    "NumberOfEmployees": null,
    "Ownership": null,
    "TickerSymbol": null,
    "Description": null,
    "Rating": {
    "string": "Hot"
    },
    "Site": null,
    "OwnerId": null,
    "CreatedDate": null,
    "CreatedById": null,
    "LastModifiedDate": {
    "long": 1672428268000
    },
    ...
    }
    }
    

    Note that the change event payloads include all object fields but fields that haven't changed are null. Use the values from ChangeEventHeader.nulledFields, ChangeEventHeader.diffFields and ChangeEventHeader.changedFields to identify actual value changes.

    After receiving the number of requested events, the script will terminate with these messages:

    gRPC stream status: {
    code: 0,
    details: '',
    metadata: Metadata { _internal_repr: {}, flags: 0 }
    }
    gRPC stream ended
    

Other Examples

Publish a platform event

Publish a Sample__e Platform Event with a Message__c field:

constpayload={CreatedDate: newDate().getTime(),// Non-null value required but there's no validity check performed on this fieldCreatedById: 'someone',// Non-null value required but there's no validity check performed on this fieldMessage__c: {string: 'Hello world'}// Field is nullable so we need to specify the 'string' type};constpublishResult=awaitclient.publish('/event/Sample__e',payload);console.log('Published event: ',JSON.stringify(publishResult));

Subscribe with a replay ID

Subscribe to account change events starting from a replay ID:

consteventEmitter=awaitclient.subscribeFromReplayId('/data/AccountChangeEvent',5,17092989);

Subscribe to past events in retention window

Subscribe to past account change events in retention window:

consteventEmitter=awaitclient.subscribeFromEarliestEvent('/data/AccountChangeEvent',3);

Handle gRPC stream lifecycle events

Use the EventEmmitter returned by subscribe methods to handle gRPC stream lifecycle events:

// Stream endeventEmitter.on('end',()=>{console.log('gRPC stream ended');});// Stream erroreventEmitter.on('error',(error)=>{console.error('gRPC stream error: ',JSON.stringify(error));});// Stream status updateeventEmitter.on('status',(status)=>{console.log('gRPC stream status: ',status);});

Use a custom logger

The client logs output to the console by default but you can provide your favorite logger in the client constructor.

When in production, asynchronous logging is preferable for performance reasons.

For example:

importpinofrom'pino';constlogger=pino();constclient=newPubSubApiClient(logger);

About

A node client for the Salesforce Pub/Sub API

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Apr 20, 2026. It is now read-only.

Repository files navigation

npm

Node client for the Salesforce Pub/Sub API

See the official Pub/Sub API repo for more information on the Salesforce gRPC-based Pub/Sub API.

Installation and Configuration

Install the client library with npm install salesforce-pubsub-api-client.

Create a .env file at the root of the project for configuration.

Pick one of these authentication flows and fill the relevant configuration:

  • User supplied authentication
  • Username/password authentication (recommended for tests)
  • OAuth 2.0 client credentials
  • OAuth 2.0 JWT Bearer (recommended for production)

Note
The default client logger is fine for a test environement but you'll want to switch to a custom logger with asynchronous logging for increased performance.

User supplied authentication

If you already have a Salesforce client in your app, you can reuse its authentication information. You only need this minimal configuration:

SALESFORCE_AUTH_TYPE=user-supplied
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

When connecting to the Pub/Sub API, use the following method instead of the standard connect() method to specify authentication information:

awaitclient.connectWithAuth(accessToken,instanceUrl,organizationId,username);

Username/password flow

Warning
Relying on a username/password authentication flow for production is not recommended. Consider switching to JWT auth for extra security.

SALESFORCE_AUTH_TYPE=username-password
SALESFORCE_LOGIN_URL=https://login.salesforce.com
SALESFORCE_USERNAME=YOUR_SALESFORCE_USERNAME
SALESFORCE_PASSWORD=YOUR_SALESFORCE_PASSWORD
SALESFORCE_TOKEN=YOUR_SALESFORCE_USER_SECURITY_TOKEN
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

OAuth 2.0 client credentials flow (client_credentials)

SALESFORCE_AUTH_TYPE=oauth-client-credentials
SALESFORCE_LOGIN_URL=YOUR_DOMAIN_URL
SALESFORCE_CLIENT_ID=YOUR_CONNECTED_APP_CLIENT_ID
SALESFORCE_CLIENT_SECRET=YOUR_CONNECTED_APP_CLIENT_SECRET
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

OAuth 2.0 JWT bearer flow

This is the most secure authentication option. Recommended for production use.

SALESFORCE_AUTH_TYPE=oauth-jwt-bearer
SALESFORCE_LOGIN_URL=https://login.salesforce.com
SALESFORCE_CLIENT_ID=YOUR_CONNECTED_APP_CLIENT_ID
SALESFORCE_USERNAME=YOUR_SALESFORCE_USERNAME
SALESFORCE_PRIVATE_KEY_FILE=PATH_TO_YOUR_KEY_FILE
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

Basic Example

Here's an example that will get you started quickly. It listens to a single account change event.

  1. Activate Account change events in Salesforce Setup > Change Data Capture.

  2. Create a sample.js file with this content:

    importPubSubApiClientfrom'salesforce-pubsub-api-client';asyncfunctionrun(){try{constclient=newPubSubApiClient();awaitclient.connect();// Subscribe to a single incoming account change eventsconsteventEmitter=awaitclient.subscribe('/data/AccountChangeEvent',1);// Handle incoming eventseventEmitter.on('data',(event)=>{console.log(`Handling ${event.payload.ChangeEventHeader.entityName} change event ${event.replayId}`);console.log(JSON.stringify(event,null,2));});}catch(error){console.error(error);}}run();
  3. Run the project with node sample.js

    If everything goes well, you'll see output like this:

    Connected to Salesforce org https://pozil-dev-ed.my.salesforce.com as grpc@pozil.com
    Connected to Pub/Sub API endpoint api.pubsub.salesforce.com:7443
    Topic schema loaded: /data/AccountChangeEvent
    Subscribe request sent for 1 events from /data/AccountChangeEvent...
    

    At this point the script will be on hold and will wait for events.

  4. Modify an account record in Salesforce. This fires an account change event.

    Once the client receives an event, it will display it like this:

    Received 1 events, latest replay ID: 17093000
    Handling Account change event 17093000
    {
    "replayId": 17093000,
    "payload": {
    "ChangeEventHeader": {
    "entityName": "Account",
    "recordIds": [
    "0014H00002LbR7QQAV"
    ],
    "changeType": "UPDATE",
    "changeOrigin": "com/salesforce/api/soap/56.0;client=SfdcInternalAPI/",
    "transactionKey": "0005349f-124e-0df1-3a25-f551ab84d237",
    "sequenceNumber": 1,
    "commitTimestamp": 1672428268000,
    "commitNumber": 11449587527037,
    "commitUser": "00558000000yFyDAAU",
    "nulledFields": [],
    "diffFields": [],
    "changedFields": [
    "Rating",
    "LastModifiedDate"
    ]
    },
    "Name": null,
    "Type": null,
    "ParentId": null,
    "BillingAddress": null,
    "ShippingAddress": null,
    "Phone": null,
    "Fax": null,
    "AccountNumber": null,
    "Website": null,
    "Sic": null,
    "Industry": null,
    "AnnualRevenue": null,
    "NumberOfEmployees": null,
    "Ownership": null,
    "TickerSymbol": null,
    "Description": null,
    "Rating": {
    "string": "Hot"
    },
    "Site": null,
    "OwnerId": null,
    "CreatedDate": null,
    "CreatedById": null,
    "LastModifiedDate": {
    "long": 1672428268000
    },
    ...
    }
    }
    

    Note that the change event payloads include all object fields but fields that haven't changed are null. Use the values from ChangeEventHeader.nulledFields, ChangeEventHeader.diffFields and ChangeEventHeader.changedFields to identify actual value changes.

    After receiving the number of requested events, the script will terminate with these messages:

    gRPC stream status: {
    code: 0,
    details: '',
    metadata: Metadata { _internal_repr: {}, flags: 0 }
    }
    gRPC stream ended
    

Other Examples

Publish a platform event

Publish a Sample__e Platform Event with a Message__c field:

constpayload={CreatedDate: newDate().getTime(),// Non-null value required but there's no validity check performed on this fieldCreatedById: 'someone',// Non-null value required but there's no validity check performed on this fieldMessage__c: {string: 'Hello world'}// Field is nullable so we need to specify the 'string' type};constpublishResult=awaitclient.publish('/event/Sample__e',payload);console.log('Published event: ',JSON.stringify(publishResult));

Subscribe with a replay ID

Subscribe to account change events starting from a replay ID:

consteventEmitter=awaitclient.subscribeFromReplayId('/data/AccountChangeEvent',5,17092989);

Subscribe to past events in retention window

Subscribe to past account change events in retention window:

consteventEmitter=awaitclient.subscribeFromEarliestEvent('/data/AccountChangeEvent',3);

Handle gRPC stream lifecycle events

Use the EventEmmitter returned by subscribe methods to handle gRPC stream lifecycle events:

// Stream endeventEmitter.on('end',()=>{console.log('gRPC stream ended');});// Stream erroreventEmitter.on('error',(error)=>{console.error('gRPC stream error: ',JSON.stringify(error));});// Stream status updateeventEmitter.on('status',(status)=>{console.log('gRPC stream status: ',status);});

Use a custom logger

The client logs output to the console by default but you can provide your favorite logger in the client constructor.

When in production, asynchronous logging is preferable for performance reasons.

For example:

importpinofrom'pino';constlogger=pino();constclient=newPubSubApiClient(logger);

About

A node client for the Salesforce Pub/Sub API

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
This repository was archived by the owner on Apr 20, 2026. It is now read-only.

Repository files navigation

npm

Node client for the Salesforce Pub/Sub API

See the official Pub/Sub API repo for more information on the Salesforce gRPC-based Pub/Sub API.

Installation and Configuration

Install the client library with npm install salesforce-pubsub-api-client.

Create a .env file at the root of the project for configuration.

Pick one of these authentication flows and fill the relevant configuration:

  • User supplied authentication
  • Username/password authentication (recommended for tests)
  • OAuth 2.0 client credentials
  • OAuth 2.0 JWT Bearer (recommended for production)

Note
The default client logger is fine for a test environement but you'll want to switch to a custom logger with asynchronous logging for increased performance.

User supplied authentication

If you already have a Salesforce client in your app, you can reuse its authentication information. You only need this minimal configuration:

SALESFORCE_AUTH_TYPE=user-supplied
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

When connecting to the Pub/Sub API, use the following method instead of the standard connect() method to specify authentication information:

awaitclient.connectWithAuth(accessToken,instanceUrl,organizationId,username);

Username/password flow

Warning
Relying on a username/password authentication flow for production is not recommended. Consider switching to JWT auth for extra security.

SALESFORCE_AUTH_TYPE=username-password
SALESFORCE_LOGIN_URL=https://login.salesforce.com
SALESFORCE_USERNAME=YOUR_SALESFORCE_USERNAME
SALESFORCE_PASSWORD=YOUR_SALESFORCE_PASSWORD
SALESFORCE_TOKEN=YOUR_SALESFORCE_USER_SECURITY_TOKEN
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

OAuth 2.0 client credentials flow (client_credentials)

SALESFORCE_AUTH_TYPE=oauth-client-credentials
SALESFORCE_LOGIN_URL=YOUR_DOMAIN_URL
SALESFORCE_CLIENT_ID=YOUR_CONNECTED_APP_CLIENT_ID
SALESFORCE_CLIENT_SECRET=YOUR_CONNECTED_APP_CLIENT_SECRET
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

OAuth 2.0 JWT bearer flow

This is the most secure authentication option. Recommended for production use.

SALESFORCE_AUTH_TYPE=oauth-jwt-bearer
SALESFORCE_LOGIN_URL=https://login.salesforce.com
SALESFORCE_CLIENT_ID=YOUR_CONNECTED_APP_CLIENT_ID
SALESFORCE_USERNAME=YOUR_SALESFORCE_USERNAME
SALESFORCE_PRIVATE_KEY_FILE=PATH_TO_YOUR_KEY_FILE
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

Basic Example

Here's an example that will get you started quickly. It listens to a single account change event.

  1. Activate Account change events in Salesforce Setup > Change Data Capture.

  2. Create a sample.js file with this content:

    importPubSubApiClientfrom'salesforce-pubsub-api-client';asyncfunctionrun(){try{constclient=newPubSubApiClient();awaitclient.connect();// Subscribe to a single incoming account change eventsconsteventEmitter=awaitclient.subscribe('/data/AccountChangeEvent',1);// Handle incoming eventseventEmitter.on('data',(event)=>{console.log(`Handling ${event.payload.ChangeEventHeader.entityName} change event ${event.replayId}`);console.log(JSON.stringify(event,null,2));});}catch(error){console.error(error);}}run();
  3. Run the project with node sample.js

    If everything goes well, you'll see output like this:

    Connected to Salesforce org https://pozil-dev-ed.my.salesforce.com as grpc@pozil.com
    Connected to Pub/Sub API endpoint api.pubsub.salesforce.com:7443
    Topic schema loaded: /data/AccountChangeEvent
    Subscribe request sent for 1 events from /data/AccountChangeEvent...
    

    At this point the script will be on hold and will wait for events.

  4. Modify an account record in Salesforce. This fires an account change event.

    Once the client receives an event, it will display it like this:

    Received 1 events, latest replay ID: 17093000
    Handling Account change event 17093000
    {
    "replayId": 17093000,
    "payload": {
    "ChangeEventHeader": {
    "entityName": "Account",
    "recordIds": [
    "0014H00002LbR7QQAV"
    ],
    "changeType": "UPDATE",
    "changeOrigin": "com/salesforce/api/soap/56.0;client=SfdcInternalAPI/",
    "transactionKey": "0005349f-124e-0df1-3a25-f551ab84d237",
    "sequenceNumber": 1,
    "commitTimestamp": 1672428268000,
    "commitNumber": 11449587527037,
    "commitUser": "00558000000yFyDAAU",
    "nulledFields": [],
    "diffFields": [],
    "changedFields": [
    "Rating",
    "LastModifiedDate"
    ]
    },
    "Name": null,
    "Type": null,
    "ParentId": null,
    "BillingAddress": null,
    "ShippingAddress": null,
    "Phone": null,
    "Fax": null,
    "AccountNumber": null,
    "Website": null,
    "Sic": null,
    "Industry": null,
    "AnnualRevenue": null,
    "NumberOfEmployees": null,
    "Ownership": null,
    "TickerSymbol": null,
    "Description": null,
    "Rating": {
    "string": "Hot"
    },
    "Site": null,
    "OwnerId": null,
    "CreatedDate": null,
    "CreatedById": null,
    "LastModifiedDate": {
    "long": 1672428268000
    },
    ...
    }
    }
    

    Note that the change event payloads include all object fields but fields that haven't changed are null. Use the values from ChangeEventHeader.nulledFields, ChangeEventHeader.diffFields and ChangeEventHeader.changedFields to identify actual value changes.

    After receiving the number of requested events, the script will terminate with these messages:

    gRPC stream status: {
    code: 0,
    details: '',
    metadata: Metadata { _internal_repr: {}, flags: 0 }
    }
    gRPC stream ended
    

Other Examples

Publish a platform event

Publish a Sample__e Platform Event with a Message__c field:

constpayload={CreatedDate: newDate().getTime(),// Non-null value required but there's no validity check performed on this fieldCreatedById: 'someone',// Non-null value required but there's no validity check performed on this fieldMessage__c: {string: 'Hello world'}// Field is nullable so we need to specify the 'string' type};constpublishResult=awaitclient.publish('/event/Sample__e',payload);console.log('Published event: ',JSON.stringify(publishResult));

Subscribe with a replay ID

Subscribe to account change events starting from a replay ID:

consteventEmitter=awaitclient.subscribeFromReplayId('/data/AccountChangeEvent',5,17092989);

Subscribe to past events in retention window

Subscribe to past account change events in retention window:

consteventEmitter=awaitclient.subscribeFromEarliestEvent('/data/AccountChangeEvent',3);

Handle gRPC stream lifecycle events

Use the EventEmmitter returned by subscribe methods to handle gRPC stream lifecycle events:

// Stream endeventEmitter.on('end',()=>{console.log('gRPC stream ended');});// Stream erroreventEmitter.on('error',(error)=>{console.error('gRPC stream error: ',JSON.stringify(error));});// Stream status updateeventEmitter.on('status',(status)=>{console.log('gRPC stream status: ',status);});

Use a custom logger

The client logs output to the console by default but you can provide your favorite logger in the client constructor.

When in production, asynchronous logging is preferable for performance reasons.

For example:

importpinofrom'pino';constlogger=pino();constclient=newPubSubApiClient(logger);

About

A node client for the Salesforce Pub/Sub API

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Apr 20, 2026. It is now read-only.

Repository files navigation

npm

Node client for the Salesforce Pub/Sub API

See the official Pub/Sub API repo for more information on the Salesforce gRPC-based Pub/Sub API.

Installation and Configuration

Install the client library with npm install salesforce-pubsub-api-client.

Create a .env file at the root of the project for configuration.

Pick one of these authentication flows and fill the relevant configuration:

  • User supplied authentication
  • Username/password authentication (recommended for tests)
  • OAuth 2.0 client credentials
  • OAuth 2.0 JWT Bearer (recommended for production)

Note
The default client logger is fine for a test environement but you'll want to switch to a custom logger with asynchronous logging for increased performance.

User supplied authentication

If you already have a Salesforce client in your app, you can reuse its authentication information. You only need this minimal configuration:

SALESFORCE_AUTH_TYPE=user-supplied
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

When connecting to the Pub/Sub API, use the following method instead of the standard connect() method to specify authentication information:

awaitclient.connectWithAuth(accessToken,instanceUrl,organizationId,username);

Username/password flow

Warning
Relying on a username/password authentication flow for production is not recommended. Consider switching to JWT auth for extra security.

SALESFORCE_AUTH_TYPE=username-password
SALESFORCE_LOGIN_URL=https://login.salesforce.com
SALESFORCE_USERNAME=YOUR_SALESFORCE_USERNAME
SALESFORCE_PASSWORD=YOUR_SALESFORCE_PASSWORD
SALESFORCE_TOKEN=YOUR_SALESFORCE_USER_SECURITY_TOKEN
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

OAuth 2.0 client credentials flow (client_credentials)

SALESFORCE_AUTH_TYPE=oauth-client-credentials
SALESFORCE_LOGIN_URL=YOUR_DOMAIN_URL
SALESFORCE_CLIENT_ID=YOUR_CONNECTED_APP_CLIENT_ID
SALESFORCE_CLIENT_SECRET=YOUR_CONNECTED_APP_CLIENT_SECRET
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

OAuth 2.0 JWT bearer flow

This is the most secure authentication option. Recommended for production use.

SALESFORCE_AUTH_TYPE=oauth-jwt-bearer
SALESFORCE_LOGIN_URL=https://login.salesforce.com
SALESFORCE_CLIENT_ID=YOUR_CONNECTED_APP_CLIENT_ID
SALESFORCE_USERNAME=YOUR_SALESFORCE_USERNAME
SALESFORCE_PRIVATE_KEY_FILE=PATH_TO_YOUR_KEY_FILE
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

Basic Example

Here's an example that will get you started quickly. It listens to a single account change event.

  1. Activate Account change events in Salesforce Setup > Change Data Capture.

  2. Create a sample.js file with this content:

    importPubSubApiClientfrom'salesforce-pubsub-api-client';asyncfunctionrun(){try{constclient=newPubSubApiClient();awaitclient.connect();// Subscribe to a single incoming account change eventsconsteventEmitter=awaitclient.subscribe('/data/AccountChangeEvent',1);// Handle incoming eventseventEmitter.on('data',(event)=>{console.log(`Handling ${event.payload.ChangeEventHeader.entityName} change event ${event.replayId}`);console.log(JSON.stringify(event,null,2));});}catch(error){console.error(error);}}run();
  3. Run the project with node sample.js

    If everything goes well, you'll see output like this:

    Connected to Salesforce org https://pozil-dev-ed.my.salesforce.com as grpc@pozil.com
    Connected to Pub/Sub API endpoint api.pubsub.salesforce.com:7443
    Topic schema loaded: /data/AccountChangeEvent
    Subscribe request sent for 1 events from /data/AccountChangeEvent...
    

    At this point the script will be on hold and will wait for events.

  4. Modify an account record in Salesforce. This fires an account change event.

    Once the client receives an event, it will display it like this:

    Received 1 events, latest replay ID: 17093000
    Handling Account change event 17093000
    {
    "replayId": 17093000,
    "payload": {
    "ChangeEventHeader": {
    "entityName": "Account",
    "recordIds": [
    "0014H00002LbR7QQAV"
    ],
    "changeType": "UPDATE",
    "changeOrigin": "com/salesforce/api/soap/56.0;client=SfdcInternalAPI/",
    "transactionKey": "0005349f-124e-0df1-3a25-f551ab84d237",
    "sequenceNumber": 1,
    "commitTimestamp": 1672428268000,
    "commitNumber": 11449587527037,
    "commitUser": "00558000000yFyDAAU",
    "nulledFields": [],
    "diffFields": [],
    "changedFields": [
    "Rating",
    "LastModifiedDate"
    ]
    },
    "Name": null,
    "Type": null,
    "ParentId": null,
    "BillingAddress": null,
    "ShippingAddress": null,
    "Phone": null,
    "Fax": null,
    "AccountNumber": null,
    "Website": null,
    "Sic": null,
    "Industry": null,
    "AnnualRevenue": null,
    "NumberOfEmployees": null,
    "Ownership": null,
    "TickerSymbol": null,
    "Description": null,
    "Rating": {
    "string": "Hot"
    },
    "Site": null,
    "OwnerId": null,
    "CreatedDate": null,
    "CreatedById": null,
    "LastModifiedDate": {
    "long": 1672428268000
    },
    ...
    }
    }
    

    Note that the change event payloads include all object fields but fields that haven't changed are null. Use the values from ChangeEventHeader.nulledFields, ChangeEventHeader.diffFields and ChangeEventHeader.changedFields to identify actual value changes.

    After receiving the number of requested events, the script will terminate with these messages:

    gRPC stream status: {
    code: 0,
    details: '',
    metadata: Metadata { _internal_repr: {}, flags: 0 }
    }
    gRPC stream ended
    

Other Examples

Publish a platform event

Publish a Sample__e Platform Event with a Message__c field:

constpayload={CreatedDate: newDate().getTime(),// Non-null value required but there's no validity check performed on this fieldCreatedById: 'someone',// Non-null value required but there's no validity check performed on this fieldMessage__c: {string: 'Hello world'}// Field is nullable so we need to specify the 'string' type};constpublishResult=awaitclient.publish('/event/Sample__e',payload);console.log('Published event: ',JSON.stringify(publishResult));

Subscribe with a replay ID

Subscribe to account change events starting from a replay ID:

consteventEmitter=awaitclient.subscribeFromReplayId('/data/AccountChangeEvent',5,17092989);

Subscribe to past events in retention window

Subscribe to past account change events in retention window:

consteventEmitter=awaitclient.subscribeFromEarliestEvent('/data/AccountChangeEvent',3);

Handle gRPC stream lifecycle events

Use the EventEmmitter returned by subscribe methods to handle gRPC stream lifecycle events:

// Stream endeventEmitter.on('end',()=>{console.log('gRPC stream ended');});// Stream erroreventEmitter.on('error',(error)=>{console.error('gRPC stream error: ',JSON.stringify(error));});// Stream status updateeventEmitter.on('status',(status)=>{console.log('gRPC stream status: ',status);});

Use a custom logger

The client logs output to the console by default but you can provide your favorite logger in the client constructor.

When in production, asynchronous logging is preferable for performance reasons.

For example:

importpinofrom'pino';constlogger=pino();constclient=newPubSubApiClient(logger);

About

A node client for the Salesforce Pub/Sub API

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Apr 20, 2026. It is now read-only.

Repository files navigation

npm

Node client for the Salesforce Pub/Sub API

See the official Pub/Sub API repo for more information on the Salesforce gRPC-based Pub/Sub API.

Installation and Configuration

Install the client library with npm install salesforce-pubsub-api-client.

Create a .env file at the root of the project for configuration.

Pick one of these authentication flows and fill the relevant configuration:

  • User supplied authentication
  • Username/password authentication (recommended for tests)
  • OAuth 2.0 client credentials
  • OAuth 2.0 JWT Bearer (recommended for production)

Note
The default client logger is fine for a test environement but you'll want to switch to a custom logger with asynchronous logging for increased performance.

User supplied authentication

If you already have a Salesforce client in your app, you can reuse its authentication information. You only need this minimal configuration:

SALESFORCE_AUTH_TYPE=user-supplied
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

When connecting to the Pub/Sub API, use the following method instead of the standard connect() method to specify authentication information:

awaitclient.connectWithAuth(accessToken,instanceUrl,organizationId,username);

Username/password flow

Warning
Relying on a username/password authentication flow for production is not recommended. Consider switching to JWT auth for extra security.

SALESFORCE_AUTH_TYPE=username-password
SALESFORCE_LOGIN_URL=https://login.salesforce.com
SALESFORCE_USERNAME=YOUR_SALESFORCE_USERNAME
SALESFORCE_PASSWORD=YOUR_SALESFORCE_PASSWORD
SALESFORCE_TOKEN=YOUR_SALESFORCE_USER_SECURITY_TOKEN
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

OAuth 2.0 client credentials flow (client_credentials)

SALESFORCE_AUTH_TYPE=oauth-client-credentials
SALESFORCE_LOGIN_URL=YOUR_DOMAIN_URL
SALESFORCE_CLIENT_ID=YOUR_CONNECTED_APP_CLIENT_ID
SALESFORCE_CLIENT_SECRET=YOUR_CONNECTED_APP_CLIENT_SECRET
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

OAuth 2.0 JWT bearer flow

This is the most secure authentication option. Recommended for production use.

SALESFORCE_AUTH_TYPE=oauth-jwt-bearer
SALESFORCE_LOGIN_URL=https://login.salesforce.com
SALESFORCE_CLIENT_ID=YOUR_CONNECTED_APP_CLIENT_ID
SALESFORCE_USERNAME=YOUR_SALESFORCE_USERNAME
SALESFORCE_PRIVATE_KEY_FILE=PATH_TO_YOUR_KEY_FILE
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

Basic Example

Here's an example that will get you started quickly. It listens to a single account change event.

  1. Activate Account change events in Salesforce Setup > Change Data Capture.

  2. Create a sample.js file with this content:

    importPubSubApiClientfrom'salesforce-pubsub-api-client';asyncfunctionrun(){try{constclient=newPubSubApiClient();awaitclient.connect();// Subscribe to a single incoming account change eventsconsteventEmitter=awaitclient.subscribe('/data/AccountChangeEvent',1);// Handle incoming eventseventEmitter.on('data',(event)=>{console.log(`Handling ${event.payload.ChangeEventHeader.entityName} change event ${event.replayId}`);console.log(JSON.stringify(event,null,2));});}catch(error){console.error(error);}}run();
  3. Run the project with node sample.js

    If everything goes well, you'll see output like this:

    Connected to Salesforce org https://pozil-dev-ed.my.salesforce.com as grpc@pozil.com
    Connected to Pub/Sub API endpoint api.pubsub.salesforce.com:7443
    Topic schema loaded: /data/AccountChangeEvent
    Subscribe request sent for 1 events from /data/AccountChangeEvent...
    

    At this point the script will be on hold and will wait for events.

  4. Modify an account record in Salesforce. This fires an account change event.

    Once the client receives an event, it will display it like this:

    Received 1 events, latest replay ID: 17093000
    Handling Account change event 17093000
    {
    "replayId": 17093000,
    "payload": {
    "ChangeEventHeader": {
    "entityName": "Account",
    "recordIds": [
    "0014H00002LbR7QQAV"
    ],
    "changeType": "UPDATE",
    "changeOrigin": "com/salesforce/api/soap/56.0;client=SfdcInternalAPI/",
    "transactionKey": "0005349f-124e-0df1-3a25-f551ab84d237",
    "sequenceNumber": 1,
    "commitTimestamp": 1672428268000,
    "commitNumber": 11449587527037,
    "commitUser": "00558000000yFyDAAU",
    "nulledFields": [],
    "diffFields": [],
    "changedFields": [
    "Rating",
    "LastModifiedDate"
    ]
    },
    "Name": null,
    "Type": null,
    "ParentId": null,
    "BillingAddress": null,
    "ShippingAddress": null,
    "Phone": null,
    "Fax": null,
    "AccountNumber": null,
    "Website": null,
    "Sic": null,
    "Industry": null,
    "AnnualRevenue": null,
    "NumberOfEmployees": null,
    "Ownership": null,
    "TickerSymbol": null,
    "Description": null,
    "Rating": {
    "string": "Hot"
    },
    "Site": null,
    "OwnerId": null,
    "CreatedDate": null,
    "CreatedById": null,
    "LastModifiedDate": {
    "long": 1672428268000
    },
    ...
    }
    }
    

    Note that the change event payloads include all object fields but fields that haven't changed are null. Use the values from ChangeEventHeader.nulledFields, ChangeEventHeader.diffFields and ChangeEventHeader.changedFields to identify actual value changes.

    After receiving the number of requested events, the script will terminate with these messages:

    gRPC stream status: {
    code: 0,
    details: '',
    metadata: Metadata { _internal_repr: {}, flags: 0 }
    }
    gRPC stream ended
    

Other Examples

Publish a platform event

Publish a Sample__e Platform Event with a Message__c field:

constpayload={CreatedDate: newDate().getTime(),// Non-null value required but there's no validity check performed on this fieldCreatedById: 'someone',// Non-null value required but there's no validity check performed on this fieldMessage__c: {string: 'Hello world'}// Field is nullable so we need to specify the 'string' type};constpublishResult=awaitclient.publish('/event/Sample__e',payload);console.log('Published event: ',JSON.stringify(publishResult));

Subscribe with a replay ID

Subscribe to account change events starting from a replay ID:

consteventEmitter=awaitclient.subscribeFromReplayId('/data/AccountChangeEvent',5,17092989);

Subscribe to past events in retention window

Subscribe to past account change events in retention window:

consteventEmitter=awaitclient.subscribeFromEarliestEvent('/data/AccountChangeEvent',3);

Handle gRPC stream lifecycle events

Use the EventEmmitter returned by subscribe methods to handle gRPC stream lifecycle events:

// Stream endeventEmitter.on('end',()=>{console.log('gRPC stream ended');});// Stream erroreventEmitter.on('error',(error)=>{console.error('gRPC stream error: ',JSON.stringify(error));});// Stream status updateeventEmitter.on('status',(status)=>{console.log('gRPC stream status: ',status);});

Use a custom logger

The client logs output to the console by default but you can provide your favorite logger in the client constructor.

When in production, asynchronous logging is preferable for performance reasons.

For example:

importpinofrom'pino';constlogger=pino();constclient=newPubSubApiClient(logger);

About

A node client for the Salesforce Pub/Sub API

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
This repository was archived by the owner on Apr 20, 2026. It is now read-only.

Repository files navigation

npm

Node client for the Salesforce Pub/Sub API

See the official Pub/Sub API repo for more information on the Salesforce gRPC-based Pub/Sub API.

Installation and Configuration

Install the client library with npm install salesforce-pubsub-api-client.

Create a .env file at the root of the project for configuration.

Pick one of these authentication flows and fill the relevant configuration:

  • User supplied authentication
  • Username/password authentication (recommended for tests)
  • OAuth 2.0 client credentials
  • OAuth 2.0 JWT Bearer (recommended for production)

Note
The default client logger is fine for a test environement but you'll want to switch to a custom logger with asynchronous logging for increased performance.

User supplied authentication

If you already have a Salesforce client in your app, you can reuse its authentication information. You only need this minimal configuration:

SALESFORCE_AUTH_TYPE=user-supplied
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

When connecting to the Pub/Sub API, use the following method instead of the standard connect() method to specify authentication information:

awaitclient.connectWithAuth(accessToken,instanceUrl,organizationId,username);

Username/password flow

Warning
Relying on a username/password authentication flow for production is not recommended. Consider switching to JWT auth for extra security.

SALESFORCE_AUTH_TYPE=username-password
SALESFORCE_LOGIN_URL=https://login.salesforce.com
SALESFORCE_USERNAME=YOUR_SALESFORCE_USERNAME
SALESFORCE_PASSWORD=YOUR_SALESFORCE_PASSWORD
SALESFORCE_TOKEN=YOUR_SALESFORCE_USER_SECURITY_TOKEN
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

OAuth 2.0 client credentials flow (client_credentials)

SALESFORCE_AUTH_TYPE=oauth-client-credentials
SALESFORCE_LOGIN_URL=YOUR_DOMAIN_URL
SALESFORCE_CLIENT_ID=YOUR_CONNECTED_APP_CLIENT_ID
SALESFORCE_CLIENT_SECRET=YOUR_CONNECTED_APP_CLIENT_SECRET
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

OAuth 2.0 JWT bearer flow

This is the most secure authentication option. Recommended for production use.

SALESFORCE_AUTH_TYPE=oauth-jwt-bearer
SALESFORCE_LOGIN_URL=https://login.salesforce.com
SALESFORCE_CLIENT_ID=YOUR_CONNECTED_APP_CLIENT_ID
SALESFORCE_USERNAME=YOUR_SALESFORCE_USERNAME
SALESFORCE_PRIVATE_KEY_FILE=PATH_TO_YOUR_KEY_FILE
PUB_SUB_ENDPOINT=api.pubsub.salesforce.com:7443

Basic Example

Here's an example that will get you started quickly. It listens to a single account change event.

  1. Activate Account change events in Salesforce Setup > Change Data Capture.

  2. Create a sample.js file with this content:

    importPubSubApiClientfrom'salesforce-pubsub-api-client';asyncfunctionrun(){try{constclient=newPubSubApiClient();awaitclient.connect();// Subscribe to a single incoming account change eventsconsteventEmitter=awaitclient.subscribe('/data/AccountChangeEvent',1);// Handle incoming eventseventEmitter.on('data',(event)=>{console.log(`Handling ${event.payload.ChangeEventHeader.entityName} change event ${event.replayId}`);console.log(JSON.stringify(event,null,2));});}catch(error){console.error(error);}}run();
  3. Run the project with node sample.js

    If everything goes well, you'll see output like this:

    Connected to Salesforce org https://pozil-dev-ed.my.salesforce.com as grpc@pozil.com
    Connected to Pub/Sub API endpoint api.pubsub.salesforce.com:7443
    Topic schema loaded: /data/AccountChangeEvent
    Subscribe request sent for 1 events from /data/AccountChangeEvent...
    

    At this point the script will be on hold and will wait for events.

  4. Modify an account record in Salesforce. This fires an account change event.

    Once the client receives an event, it will display it like this:

    Received 1 events, latest replay ID: 17093000
    Handling Account change event 17093000
    {
    "replayId": 17093000,
    "payload": {
    "ChangeEventHeader": {
    "entityName": "Account",
    "recordIds": [
    "0014H00002LbR7QQAV"
    ],
    "changeType": "UPDATE",
    "changeOrigin": "com/salesforce/api/soap/56.0;client=SfdcInternalAPI/",
    "transactionKey": "0005349f-124e-0df1-3a25-f551ab84d237",
    "sequenceNumber": 1,
    "commitTimestamp": 1672428268000,
    "commitNumber": 11449587527037,
    "commitUser": "00558000000yFyDAAU",
    "nulledFields": [],
    "diffFields": [],
    "changedFields": [
    "Rating",
    "LastModifiedDate"
    ]
    },
    "Name": null,
    "Type": null,
    "ParentId": null,
    "BillingAddress": null,
    "ShippingAddress": null,
    "Phone": null,
    "Fax": null,
    "AccountNumber": null,
    "Website": null,
    "Sic": null,
    "Industry": null,
    "AnnualRevenue": null,
    "NumberOfEmployees": null,
    "Ownership": null,
    "TickerSymbol": null,
    "Description": null,
    "Rating": {
    "string": "Hot"
    },
    "Site": null,
    "OwnerId": null,
    "CreatedDate": null,
    "CreatedById": null,
    "LastModifiedDate": {
    "long": 1672428268000
    },
    ...
    }
    }
    

    Note that the change event payloads include all object fields but fields that haven't changed are null. Use the values from ChangeEventHeader.nulledFields, ChangeEventHeader.diffFields and ChangeEventHeader.changedFields to identify actual value changes.

    After receiving the number of requested events, the script will terminate with these messages:

    gRPC stream status: {
    code: 0,
    details: '',
    metadata: Metadata { _internal_repr: {}, flags: 0 }
    }
    gRPC stream ended
    

Other Examples

Publish a platform event

Publish a Sample__e Platform Event with a Message__c field:

constpayload={CreatedDate: newDate().getTime(),// Non-null value required but there's no validity check performed on this fieldCreatedById: 'someone',// Non-null value required but there's no validity check performed on this fieldMessage__c: {string: 'Hello world'}// Field is nullable so we need to specify the 'string' type};constpublishResult=awaitclient.publish('/event/Sample__e',payload);console.log('Published event: ',JSON.stringify(publishResult));

Subscribe with a replay ID

Subscribe to account change events starting from a replay ID:

consteventEmitter=awaitclient.subscribeFromReplayId('/data/AccountChangeEvent',5,17092989);

Subscribe to past events in retention window

Subscribe to past account change events in retention window:

consteventEmitter=awaitclient.subscribeFromEarliestEvent('/data/AccountChangeEvent',3);

Handle gRPC stream lifecycle events

Use the EventEmmitter returned by subscribe methods to handle gRPC stream lifecycle events:

// Stream endeventEmitter.on('end',()=>{console.log('gRPC stream ended');});// Stream erroreventEmitter.on('error',(error)=>{console.error('gRPC stream error: ',JSON.stringify(error));});// Stream status updateeventEmitter.on('status',(status)=>{console.log('gRPC stream status: ',status);});

Use a custom logger

The client logs output to the console by default but you can provide your favorite logger in the client constructor.

When in production, asynchronous logging is preferable for performance reasons.

For example:

importpinofrom'pino';constlogger=pino();constclient=newPubSubApiClient(logger);

About

A node client for the Salesforce Pub/Sub API

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages