Kysoql

Field filtering

Generate only the fields your application uses, with strict validation and safe relationship metadata.

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

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

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

ConfigurationResult
No rule for an objectKeep 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 excludeReject the rule.
Empty include or a rule that removes every fieldReject the rule.
Unknown field nameReject generation before replacing the output file.
Duplicate namesDeduplicate 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

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.

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:

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

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 for TYPEOF and .Type behaviour.

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:

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

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

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

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 for the actual trust boundaries.

On this page