Skip to main content

STI Child Model Generator

The STI (Single Table Inheritance) child generator creates new Dream models that extend another Dream model, enabling you to leverage single table inheritance patterns. This generator creates complete models, just like the regular model generator, but with inheritance relationships.

Prerequisites: Creating the STI Parent​

Before generating STI children, you must first create the parent model with the --sti-base-serializer flag:

pnpm psy g:resource --sti-base-serializer --owning-model=Place v1/host/places/\{\}/rooms Room type:enum:room_types:Bathroom,Bedroom,Kitchen,Den,LivingRoom Place:belongs_to position:integer:optional

Why --sti-base-serializer? This flag creates generic base serializers that accept the child class as a parameter. Each summary serializer renders type right after id, with an OpenAPI enum holding only the child's class name, and the default serializer inherits it, so clients can tell which child shape they received. Child serializers extend these base serializers, passing their own class.

Usage​

You can also create an STI parent using the model generator:

pnpm psy g:model --sti-base-serializer Room type:enum:room_types:Bathroom,Bedroom,Kitchen,Den,LivingRoom Place:belongs_to position:integer:optional
pnpm psy g:sti-child <childModelName> extends <parentModelName> [columnsWithTypes...]

Arguments​

  • <childModelName>: The name of the model to create, e.g. Post or Settings/CommunicationPreferences.

  • extends: Just the word "extends".

  • <parentModelName>: Fully qualified name of the parent model, e.g.:

    • To extend the Room model in src/app/models/Room: Room
    • To extend the Coach model in src/app/models/Health/Coach: Health/Coach
  • [columnsWithTypes...]: Space separated snake-case properties like this: title:citext subtitle:string body_markdown:text style:enum:post_styles:formal,informal

    All properties default to not nullable; null can be allowed by appending :optional: subtitle:string:optional

    Supported types​

    • uuid, uuid[]: a column optimized for storing UUIDs
    • citext, citext[]: case insensitive text (indexes and queries are automatically case insensitive)
    • encrypted: encrypted text (used in conjunction with the @deco.Encrypted decorator)
    • string, string[]: varchar; allowed length defaults to 255, but may be customized, e.g.: subtitle:string:128 or subtitle:string:128:optional
    • text, text[]
    • date, date[]
    • datetime, datetime[]
    • time, time[]
    • timetz, timetz[]
    • integer, integer[]
    • decimal, decimal[]: precision,scale is required, e.g.: volume:decimal:3,2 or volume:decimal:3,2:optional
      • Leveraging arrays, add the "[]" suffix, e.g.: volume:decimal[]:3,2
    • enum, enum[]: include the enum name to automatically create the enum: type:enum:room_types:bathroom,kitchen,bedroom or type:enum:room_types:bathroom,kitchen,bedroom:optional
      • Omit the enum values to leverage an existing enum (omits the enum type creation): type:enum:room_types or type:enum:room_types:optional
      • Leveraging arrays, add the "[]" suffix, e.g.: type:enum[]:room_types:bathroom,kitchen,bedroom
    • belongs_to is not supported: STI children cannot declare associations, so declare every BelongsTo, HasOne, and HasMany on the STI parent instead.

Options​

  • --no-serializer: Skip generating a serializer for the child model.
  • --model-name <modelName>: Explicit model class name to use instead of the auto-generated one (e.g. --model-name=Kitchen for Room/Kitchen).
  • --admin-serializers: Generate admin serializer variants (AdminSerializer and AdminSummarySerializer) in addition to the default serializers.
  • --internal-serializers: Generate internal serializer variants (InternalSerializer and InternalSummarySerializer) in addition to the default serializers.
  • -h, --help: Display help for command.

STI Example​

This example creates an STI model for various rooms.

STI Base​

Create the base Room model using the resource generator with the --sti-base-serializer flag:

pnpm psy g:resource --sti-base-serializer --owning-model=Place v1/host/places/\{\}/rooms Room type:enum:room_types:Bathroom,Bedroom,Kitchen,Den,LivingRoom Place:belongs_to position:integer:optional

STI Child​

Then create an STI child:

pnpm psy g:sti-child Room/Kitchen extends Room appliances:enum[]:appliance_types:stove,oven,microwave,dishwasher

Supported Column Types​

STI child models support the same column types as the model generator. Child-specific columns are added to the parent table; STI children should not declare their own associations, @SoftDelete(), @Sortable(), or @ReplicaSafe() decorators. g:sti-child enforces the associations rule at generation time by rejecting belongs_to columns — declare the association on the parent instead.

What Gets Generated​

  • A Dream STI child model decorated with @STI(ParentClass)
  • Child serializers that extend the parent's base serializers (unless --no-serializer is passed)
  • Factory and spec skeletons
  • A migration that ALTERs the parent table (never a new table) to add any child-specific columns, plus, for each column that is not :optional, a boolean, or an array, a check constraint that requires a value only on rows of that child type. The migration goes in the migrations folder of the parent model's database connection, which the child shares. When the child declares no additional columns, no migration is emitted at all — STI children share the parent's table, so a columnless child needs no schema change.

STI children never receive @SoftDelete() — soft delete is enforced at the STI parent level — and g:sti-child does not accept a --no-soft-delete flag.

@STI() always names the base model, even when the child's TypeScript extends chain runs deeper than one level (e.g. a Bunkroom extends Bedroom must still be decorated @STI(Room), not @STI(Bedroom)). See single table inheritance for what breaks when this rule is missed.