# Schema and result types (/docs/core/reference/schema-types)



For application code, prefer a schema generated by [`@kysoql/codegen`](/docs/codegen/schema-generation).
It reflects the objects, fields, relationships, and capabilities visible to the
Salesforce identity used during generation.

## Use the generated schema [#use-the-generated-schema]

```ts
import { Kysoql } from "@kysoql/core";
import type { SalesforceSchema } from "./kysoql/salesforce.generated";

export const db = new Kysoql<SalesforceSchema>();
```

The schema is a TypeScript snapshot, not a live permission check. Regenerate it
when the Salesforce shape you rely on changes.

## Handwritten schemas [#handwritten-schemas]

Handwritten schemas are useful for small examples and isolated tests:

```ts
import {
  Kysoql,
  type SalesforceField,
  type SalesforceObject,
} from "@kysoql/core";

interface DemoSchema {
  readonly Account: SalesforceObject<{
    readonly Id: SalesforceField<string, {
      readonly salesforceType: "id";
      readonly nullable: false;
      readonly filterable: true;
      readonly sortable: true;
      readonly groupable: true;
      readonly aggregatable: true;
    }>;
    readonly Name: SalesforceField<string, {
      readonly salesforceType: "string";
      readonly nullable: false;
      readonly filterable: true;
      readonly sortable: true;
      readonly groupable: true;
    }>;
  }>;
}

const db = new Kysoql<DemoSchema>();
const query = db.selectFrom("Account").select(["Id", "Name"]).limit(10);

console.log(query.compile().soql);
```

Do not copy guessed capabilities into a production schema. A field marked
`filterable` or `groupable` in TypeScript can still be rejected by Salesforce if
the metadata is wrong or stale.

## Field metadata [#field-metadata]

`SalesforceField<Value, Metadata>` carries the field value type plus the
capabilities Kysoql needs while building a query.

| Property                          | What it controls                                              |
| --------------------------------- | ------------------------------------------------------------- |
| `salesforceType`                  | Salesforce field type such as `id`, `currency`, or `datetime` |
| `nullable`                        | Whether the value can be `null`                               |
| `filterable`                      | Use in typed `WHERE` expressions                              |
| `sortable`                        | Use in `ORDER BY`                                             |
| `groupable`                       | Use in `GROUP BY`                                             |
| `aggregatable`                    | Use with supported aggregate functions                        |
| `custom`                          | Standard or custom grouping for `FIELDS(...)`                 |
| `polymorphic`                     | Supported polymorphic relationship paths                      |
| `referenceTo`, `relationshipName` | Parent relationship metadata                                  |
| `activePicklistValue`             | Active multipicklist values used by typed filters             |

Codegen maps Salesforce values to useful TypeScript types. Numeric fields become
`number`, booleans become `boolean`, common text and temporal fields become
`string`, and geolocation uses `SalesforceGeolocation`. Nullability is added from
the field metadata.

## Object metadata [#object-metadata]

`SalesforceObject` also carries relationship and object-level capabilities such
as scopes, Knowledge data categories, MRU support, Data 360 options, and whether
the generated field list is complete.

A schema generated with an explicit [field rule](/docs/codegen/field-filtering)
marks that object as incomplete. Kysoql then disables `selectFields(...)` for the
object because Salesforce expands `FIELDS(...)` from live metadata, not from your
generated subset. Explicit `.select(...)` calls continue to work normally.

<Accordions type="single">
  <Accordion title="Relationship metadata">
    `SalesforceParentRelationship` records the referenced object, lookup field,
    and nullability. `SalesforceChildRelationship` records the child object and
    its reference field. The map keys are the relationship names used in SOQL.
  </Accordion>

  <Accordion title="Data 360 metadata">
    Generated objects can expose `"data360-dlo"` or `"data360-dmo"` capabilities.
    Use the generated value rather than enabling Data 360 options manually on a
    normal CRM object.
  </Accordion>
</Accordions>

## Useful exported types [#useful-exported-types]

| Type                                | Use                                                        |
| ----------------------------------- | ---------------------------------------------------------- |
| `SalesforceFieldValue<Field>`       | Returned field value with nullability                      |
| `SalesforceFieldFilterValue<Field>` | Value accepted by typed filters                            |
| `SalesforceRow<Object>`             | All direct fields on an object                             |
| `SalesforceQueryResult<Row>`        | Salesforce query envelope with records and paging metadata |
| `SalesforceGeolocation`             | `{ latitude, longitude }`                                  |
| `CompiledQuery<Output>`             | Compiled SOQL with a type-only output carrier              |

## Infer result types from the query [#infer-result-types-from-the-query]

Avoid maintaining a second interface for selected rows when TypeScript can infer
it from the query itself:

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

export function loadAccountNames() {
  return db.selectFrom("Account").select(["Id", "Name"]).limit(100).execute();
}

export type AccountNames = Awaited<ReturnType<typeof loadAccountNames>>;
export type AccountName = AccountNames[number];
```

The inferred type describes the expected result shape. It does not deeply
validate or freeze values returned by an external transport at runtime.
