Backend Calls
Wrap Apex calls in timeBackendCall(...) to capture durations, start/complete events, and exceptions — with four flexible error-handling scenarios.
Most LWC performance problems live in the round trip to the server. timeBackendCall(methodName, apexCall) is the workhorse for measuring that round trip. It logs the start of the call, waits for the promise, logs completion (with duration), and — if something goes wrong — logs the failure with the same duration baseline plus the exception.
Under the hood it returns a PerformanceCallBuilder, which lets you tune how errors are handled before the call is actually executed.
Basic Usage
import Triton from 'c/triton';
import searchProducts from '@salesforce/apex/ProductController.searchProducts';
export default class ProductSearch extends LightningElement {
triton;
products = [];
connectedCallback() {
this.triton = new Triton().bindToComponent('ProductSearch');
this.triton.trackComponentLifecycle('connected');
}
async handleSearch() {
const searchTerm = this.searchInput.value;
try {
const results = await this.triton.timeBackendCall(
'searchProducts',
() => searchProducts({ searchTerm })
).execute();
this.products = results;
} catch (error) {
this.showErrorMessage('Search failed. Please try again.');
}
}
}Three things to notice:
The Apex call is wrapped in an arrow function so Triton controls when it runs.
methodName("searchProducts") is the label that ends up on the resulting log records. Use the real Apex method name so logs are easy to find..execute()is the trigger — until you call it, no logging or Apex invocation happens.
The Four Error-Handling Scenarios
PerformanceCallBuilder exposes two modifiers that combine into four scenarios. The defaults (built-in logging + rethrow) match what 90% of code wants.
.execute()
Built-in (logs failure)
Yes
Default. The standard try/catch pattern.
.withoutRethrow().execute()
Built-in (logs failure)
No
You want failures logged but don't need an exception in the calling code.
.withCustomErrorHandler(fn).execute()
Custom only
Yes
You want full control over the side effects but the calling code still needs to react.
.withCustomErrorHandler(fn).withoutRethrow().execute()
Custom only
No
Fire-and-forget calls with custom telemetry/UX.
1. Default: log + rethrow
The failure is automatically logged with the method name, duration, and exception, then re-thrown so your catch block runs as usual.
2. Built-in logging, no rethrow
Useful when you want the call to fail silently from a control-flow perspective but still capture the failure log. A null return is your signal that something went wrong.
3. Custom handler + rethrow
Custom handlers receive (error, context) where context is { methodName, duration } for backend calls. Use this when you want to enrich the failure with extra side effects (external monitoring, fancy toasts) but still need an exception to bubble.
A custom handler replaces the built-in failure log — it doesn't run alongside it. If you still want a Triton log on failure, call this.triton.log(...) from inside your handler.
4. Custom handler, no rethrow
The most "tolerant" mode — perfect for non-critical operations like notifications, where a failure shouldn't poison the rest of the flow.
Parallel Calls
timeBackendCall returns a builder, and .execute() returns a regular Promise, so you can fan out calls and wait on them with Promise.all. Each call gets its own timing, start log, and completion log.
This pattern is especially powerful for record pages — you instantly get a per-call breakdown of which Apex method is the slow one, instead of staring at a single aggregated load time.
What Gets Logged
For a successful backend call, you get two log entries:
Start
BACKEND_CALL
Backend call started: <methodName>
action = <methodName>
Completion
BACKEND_CALL
Backend call completed: <methodName>
duration, action = <methodName>
A failure produces the start log plus a Backend call failed: <methodName> entry with duration, the exception details, and the stack trace (unless you've supplied a custom handler).
Best Practices
Use the real Apex method name as
methodName— searching and grouping logs by method is dramatically easier.Keep the arrow function passed to
timeBackendCallthin. Anything you do inside it that isn't the Apex call gets folded into the duration measurement.Lean on
withoutRethrow()for fire-and-forget side effects (analytics pings, optional enrichment) so a flaky non-critical call can't break your component.Combine with User Interactions when a single user action triggers multiple backend calls — you'll get a parent interaction record plus children for each call.
Last updated