# Field filtering (/docs/codegen/field-filtering)



Field filtering is useful when a Salesforce object is so large that generating every field makes the schema noisy or expensive to work with.

It is optional. Without a field rule, codegen keeps every field visible through Describe.

## Include or exclude exact fields [#include-or-exclude-exact-fields]

```ts title="kysoql.config.ts"
import { defineConfig } from "@kysoql/codegen";

export default defineConfig({
  objects: ["Account", "Contact", "User"],
  fields: {
    Account: {
      include: ["Id", "Name", "Industry", "OwnerId"],
    },
    Contact: {
      exclude: ["Description"],
    },
  },
  output: "src/kysoql/salesforce.generated.ts",
});
```

`Account` keeps only the listed fields. `Contact` keeps every described field except `Description`. `User` has no rule, so it keeps its full described field set.

Field names are exact, case-sensitive Salesforce API names. Wildcards, relationship paths, regular expressions, and callbacks are not supported.

## Include every field the query needs [#include-every-field-the-query-needs]

An include list needs more than the fields you return to the caller. Keep fields used by:

* `WHERE`
* `ORDER BY`
* `GROUP BY` and aggregates
* semi-joins
* required object-specific predicates
* lookup relationships
* child relationship metadata

No field is silently added back, including `Id`.

## Validation rules [#validation-rules]

| Configuration                                      | Result                                                             |
| -------------------------------------------------- | ------------------------------------------------------------------ |
| No rule for an object                              | Keep every described field.                                        |
| `include: ["Id", "Name"]`                          | Keep exactly those fields.                                         |
| `exclude: ["Description"]`                         | Keep every field except `Description`.                             |
| `exclude: []`                                      | Keep every field but still mark the object as explicitly filtered. |
| Both `include` and `exclude`                       | Reject the rule.                                                   |
| Empty `include` or a rule that removes every field | Reject the rule.                                                   |
| Unknown field name                                 | Reject generation before replacing the output file.                |
| Duplicate names                                    | Deduplicate them deterministically.                                |

A field can be unknown because it was renamed, removed, hidden from the authenticated user, or simply misspelled.

## Field rules do not add objects [#field-rules-do-not-add-objects]

The `objects` list still decides which objects are generated. A field rule for `Contact` does not add `Contact` when the selected object list contains only `Account`.

```bash
pnpm exec kysoql generate --object Account
```

That command keeps the configured `Account` field rule and ignores inactive rules for generation.

To generate a full object without loading configured field rules, use `--no-config`:

```bash
pnpm exec kysoql generate --no-config \
  --auth config/salesforce.auth.ts \
  --object Account \
  --output src/account-full.generated.ts
```

## Relationship fields matter [#relationship-fields-matter]

For `Contact.Account.Name`, you need:

* `Contact` in the generated object list
* `Contact.AccountId` retained
* `Account` in the generated object list
* `Account.Name` retained

For a child relationship such as `Account.Contacts`, retain the lookup field on the child object. There is no `Contacts` field to place in the parent object's include list because child relationships come from relationship metadata.

Removing a lookup removes the parent path derived from it and can also remove the inverse child relationship.

Polymorphic reference metadata keeps its original target union. Filtering out one possible target object does not rewrite Salesforce metadata to pretend the reference has only one target.

See [polymorphic relationships](/docs/framework/guides/polymorphism) for `TYPEOF` and `.Type` behaviour.

## Why `FIELDS()` is disabled on filtered objects [#why-fields-is-disabled-on-filtered-objects]

An object with an explicit field rule is marked as a partial generated schema. Typed `selectFields("standard")`, `selectFields("custom")`, and `selectFields("all")` are disabled for that object.

Salesforce expands `FIELDS(...)` on the server. A local TypeScript field filter cannot change what Salesforce considers part of `FIELDS(ALL)`.

Use explicit selections instead:

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

const query = db.selectFrom("Account").select(["Id", "Name"]);
```

Even `exclude: []` marks the object as partial because a rule was explicitly applied. Remove the rule and regenerate when you want to restore typed `FIELDS()` support.

## Programmatic generation [#programmatic-generation]

The same `ObjectFieldFilters` type works with `generateSchema()` and `loadSchema()`:

```ts
import {
  generateSchema,
  type ObjectFieldFilters,
  type SalesforceDescribeClient,
} from "@kysoql/codegen";

const fields = {
  Account: { include: ["Id", "Name", "OwnerId"] },
  Contact: { exclude: ["Description"] },
} satisfies ObjectFieldFilters;

export async function generate(client: SalesforceDescribeClient) {
  await generateSchema({
    client,
    objects: ["Account", "Contact"],
    fields,
    output: "src/kysoql/salesforce.generated.ts",
  });
}
```

## What field filtering does not do [#what-field-filtering-does-not-do]

Field filtering narrows generated metadata and the TypeScript query surface. It does not:

* enforce Salesforce field-level security
* change the fields Salesforce returns unless your SOQL selection changes
* reduce the metadata requests made during generation
* validate runtime Salesforce response fields

Treat it as a developer-experience and schema-size tool, not a security feature. See [security and production use](/docs/core/reference/security) for the actual trust boundaries.
