# Compile for Apex (/docs/core/apex/overview)



Apex builders produce SOQL for code that runs **inside Salesforce Apex**. They do
not execute Apex, deploy classes, or resolve Apex variables from TypeScript.

## Enter Apex after shaping the query [#enter-apex-after-shaping-the-query]

```ts
import { apexBind } from "@kysoql/core";
import { db } from "./db";

const compiled = db
  .selectFrom("Account")
  .select(["Id", "Name"])
  .apex()
  .where("Id", "=", apexBind<string>("accountId"))
  .withUserMode()
  .compile();

console.log(compiled.soql);
// SELECT Id, Name FROM Account WHERE Id = :accountId WITH USER_MODE
```

Define `accountId` in the Apex caller. The TypeScript generic only checks that
the bind is compatible with the field. It does not create or validate the Apex
variable.

Finish ordinary selections, sorting, grouping, and `HAVING` before calling
`.apex()` or `.dynamicApex()`. The Apex builder deliberately exposes a narrower
set of methods after the switch.

## Choose the access mode intentionally [#choose-the-access-mode-intentionally]

`withUserMode()` emits `WITH USER_MODE` and `withSystemMode()` emits
`WITH SYSTEM_MODE`. Calling one replaces the other.

Kysoql does not decide which mode is appropriate for your security model. The
mode also shares Salesforce's filtering `WITH` slot with data categories,
`RecordVisibilityContext`, and `UserId`.

## Lock records [#lock-records]

```ts
import { apexBind } from "@kysoql/core";
import { db } from "./db";

const locked = db
  .selectFrom("Account")
  .select(["Id", "Name"])
  .apex()
  .where("Id", "=", apexBind<string>("accountId"))
  .forUpdate()
  .compile();
```

`forUpdate()` is available on Apex record queries. It cannot be combined with
`ORDER BY` or `ALL ROWS`. Salesforce controls the actual transaction and lock
behaviour.

## Include deleted records [#include-deleted-records]

```ts
import { db } from "./db";

const compiled = db
  .selectFrom("Account")
  .select(["Id", "Name"])
  .apex()
  .allRows()
  .compile();
```

`allRows()` is available on Apex record, aggregate, and count builders. For REST
queries, use [`executeAll()`](/docs/rest/execution#queryall-is-a-separate-query-mode)
instead.

## Aggregate and count queries [#aggregate-and-count-queries]

Build the aggregate structure before entering Apex:

```ts
import { apexBind } from "@kysoql/core";
import { db } from "./db";

const report = db
  .selectFrom("Opportunity")
  .select(({ fn }) => fn.sum("Amount").as("totalAmount"))
  .groupBy("StageName")
  .select("StageName")
  .apex()
  .where("IsClosed", "=", apexBind<boolean>("closed"))
  .limit(apexBind<number>("rowLimit"))
  .compile();
```

The specialised Apex aggregate builders accept supported filters and binds but
do not reopen the full aggregate-building surface after the context switch.

## Static and dynamic Apex [#static-and-dynamic-apex]

Use `.apex()` for the static compile context. Use `.dynamicApex()` when you need
the dynamic-only features Kysoql models, such as bound `Database.QueryOptions`.

Neither method is a JavaScript execution adapter. Your Apex code still owns bind
values, access level, and the API used to execute the resulting SOQL.

<Accordions type="single">
  <Accordion title="Apex-specific boundaries">
    * Apex permits only the supported bounded `FIELDS(STANDARD)` form.
    * Knowledge article queries reject Kysoql bind expressions.
    * Relationship depth and syntax still depend on the target Apex API version.
  </Accordion>
</Accordions>

Continue with [Apex bindings](/docs/core/apex/bindings) or the
[Core API reference](/docs/core/reference/api).
