Skip to content

Validate options at runtime: reject unsupported and unknown options, stricter typings #18450

Description

@wikirik-agent

Issue Creation Checklist

  • I understand that my issue will be automatically closed if I don't fill in the requested information
  • I have read the contribution guidelines

Feature Description

Describe the feature you'd like to see implemented

Sequelize is an option-driven API, but how options are checked at runtime is inconsistent.

  • Unsupported options are sometimes silently dropped. For example, addIndexQuery does delete options.type when the dialect has no supports.index.type. So type: 'FULLTEXT' on Postgres quietly creates a regular B-tree index instead of failing.
  • Unknown options are mostly ignored. A typo ({ uniqe: true }), an option that belongs somewhere else (Model.findAll({ name: 'x' }) instead of where), or an option from another dialect goes through without any feedback for JavaScript users, and for TypeScript users who cast or spread loosely typed objects.
  • Some places already do better.
    • rejectInvalidOptions (packages/core/src/utils/check.ts, used by ~50 query generator and query interface methods) throws when an option is declared in the typings but not supported by the current dialect. It deliberately ignores keys it doesn't know about.
    • A few spots have hand-written checks, such as @Table({ abstract }) and inverse.type on associations.

The same question was raised in #3645 (2015) and closed without a general solution.

This issue is meant to agree on a direction before anyone implements it. Roughly:

  1. Unsupported options throw. An option that exists in the typings but isn't supported by the current dialect throws, instead of being dropped. Extending rejectInvalidOptions to the places that still silently drop (index options first) looks like the obvious first step.
  2. Unknown keys are checked. Decide where unknown keys should throw, warn or be ignored. Public API entry points (model definition, data type options, query methods, query interface methods) probably need different answers. Nested bags such as dialectOptions, pass-through driver options, and objects shared between methods need care.
  3. Typings and runtime guards agree. Make the TypeScript types stricter (discriminated unions, closed string unions instead of string, exact option types per method), and back each with a runtime guard, so JavaScript users get the same errors TypeScript users get at compile time.
  4. One source of truth. Consider deriving both the types and the runtime validation from a single schema, instead of maintaining typings, Sets of option names and hand-written checks separately. Candidates to evaluate include Effect Schema (v4) and Zod. The trade-offs to weigh:
    • bundle size and dependency weight
    • runtime cost on hot paths such as query building
    • error-message quality
    • how well each fits the existing AbstractDialect / supports model
    • whether it can express "valid in general, but not on this dialect"

Questions to discuss:

  • Should unknown keys throw by default, or warn in development only (process.env.NODE_ENV !== 'production', as some checks already do)? Or should there be an opt-out?
  • How do we roll this out without breaking many users at once? Per area, behind a deprecation warning first, or in a major version?
  • Which areas come first? Model and attribute definitions, data type options, index options, find* options, query interface?
  • Is a schema library acceptable as a core dependency, or should validation stay hand-written with shared helpers?

Describe why you would like this feature to be added to Sequelize

Silent drops and ignored typos are among the hardest bugs for users to track down: nothing fails, the result is just wrong (an index of the wrong kind, a filter that never applied). Validation has to happen in the core, because only the core knows which options each method accepts and which ones each dialect supports.

A consistent approach also makes new features cheaper to add safely. Each new option (for example vector data type and index options in #18227) would be declared once, typed once and validated once, instead of every PR inventing its own checks.

Is this feature dialect-specific?

  • No. This feature is relevant to Sequelize as a whole.
  • Yes. This feature only applies to the following dialect(s):

Would you be willing to resolve this issue by submitting a Pull Request?

  • Yes, I have the time and I know how to start.
  • Yes, I have the time but I will need guidance.
  • No, I don't have the time, but my company or I are supporting Sequelize through donations on OpenCollective.
  • No, I don't have the time, and I understand that I will need to wait until someone from the community or maintainers is interested in implementing my feature.

Indicate your interest in the addition of this feature by adding the 👍 reaction. Comments such as "+1" will be removed.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    hardFor issues and PRs.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions