Kysoql
Build and run queries

Labels, currency, and formatting

Select translated labels and display values without confusing them with stored values.

SELECT functions are added through the selection callback and need an explicit alias. They can sit beside normal record fields, but they cannot be mixed with a TYPEOF selection.

Translated labels

import { db } from "./db";

const opportunities = db
  .selectFrom("Opportunity")
  .select(["Id", "StageName"])
  .select(({ fn }) => fn.toLabel("StageName").as("stageLabel"));

toLabel() works with supported generated picklist and multipicklist fields, including supported parent paths.

Keep the stored value for application logic and use the translated label for display. A translated label can change with the Salesforce user's language.

You can also filter using toLabel():

import { db } from "./db";

const query = db
  .selectFrom("Opportunity")
  .select(["Id", "StageName"])
  .where((eb) => eb(eb.fn.toLabel("StageName"), "like", "Pros%"));

Translated picklists support =, !=, and like. Translated multipicklists support = and !=.

Currency conversion

import { db } from "./db";

const opportunities = db
  .selectFrom("Opportunity")
  .select(["Id", "Amount"])
  .select(({ fn }) => fn.convertCurrency("Amount").as("convertedAmount"))
  .orderBy("Amount", "desc");

convertCurrency() converts a selected currency field using the querying Salesforce user's currency. The result is numeric and keeps the source nullability.

This is different from an ISO-coded filter literal. Use soqlCurrency() when a WHERE value needs a specific currency code.

Localized display strings

import { db } from "./db";

const opportunities = db
  .selectFrom("Opportunity")
  .select("Id")
  .select(({ fn }) => [
    fn.format("CloseDate").as("localizedCloseDate"),
    fn.format(fn.convertCurrency("Amount")).as("localizedAmount"),
  ]);

format() returns display strings for supported numeric and temporal fields. It can also wrap an unaliased convertCurrency() expression.

Formatted values are for presentation. Keep the raw numeric or temporal field when downstream code still needs calculations, stable sorting, or locale-independent serialization.

Aggregate expressions can be formatted too:

import { db } from "./db";

const totals = db
  .selectFrom("Opportunity")
  .select(({ fn }) => [
    fn.format(fn.count("Id")).as("displayCount"),
    fn.format(fn.sum("Amount")).as("displayAmount"),
  ]);

Apply .as(...) to the outer format() call. Bare COUNT() uses the scalar count builder and is not a field aggregate expression to format.

On this page