Virtual & Computed Fields
Read this page in the documentation
Virtual & Computed Fields Attributes that live only on the model instance - computed in JavaScript from other fields, never stored in a database column. A virtual field is declared with DataTypes.VIRTUAL (or DataTypes.VIRTUALSTRING). It produces no column at sync() time and is stripped out of every INSERT/UPDATE, but it participates in reads through a getter and in writes through a setter. Getters are perfect for values you can derive on demand (fullName, totalWithTax, inStock); setters let a single assignment fan out into several real columns. Declaring a virtual field There are two equivalent shapes. You can pass the accessors to the type factory: Or place get / set directly on the attribute alongside a bare DataTypes.VIRTUAL: Both forms resolve to the same accessors. Inside a getter or setter this is the model instance, so you can read siblings either through the proxy (this.firstName) or explicitly with this.getDataValue('firstName'). The explicit form is safest inside virtual getters because it always returns the stored value without re-triggering another accessor. You can also attach a virtual field to an already-defined model with defineAttribute: Reading: getters A getter runs every time the field is accessed - there is no caching, so it always reflects the current column values. This is why the dependencies array is documentation/reactivity metadata rather than a memoization key. Because the getter recomputes on every read, updating a dependency updates the virtual value automatically: A virtual field is not limited to strings or numbers - return booleans, arrays, or objects as needed. Virtual fields that depend on other virtual fields A getter may call this.get('otherVirtual'), so computed fields can be layered. List every underlying real column in dependencies so the dependency graph stays accurate. totalWithTax reads totalValue, which itself reads price and quantity. Nothing is cached, so each layer stays consistent after an update. Writing: setters that derive columns A setter lets you assign to a virtual field and have it write real columns via setDataValue. setDataValue writes straight into dataValues, marks the key as changed, and flags the instance dirty - so the derived columns persist on the next save(). Assigning through the proxy triggers the setter too, and setters may write to sibling fields through the proxy instead of setDataValue: You can pass a virtual field to create() and it will run its setter to populate the underlying columns, while the virtual itself is filtered out of the SQL. VIRTUALSTRING DataTypes.VIRTUALSTRING behaves like VIRTUAL but is specialised for string values and accepts an optional length for validation. Use it when the computed value is always a string. Serialization: virtual fields and toJSON Virtual fields are excluded from toJSON() by default - toJSON walks the real columns and skips anything whose type key is VIRTUAL or VIRTUALSTRING. When you want a virtual value in your serialized output, add it explicitly after toJSON(): To include every virtual field programmatically, use the model helpers. getVirtualFields() lists the virtual attribute names and filterVirtualFields(values) returns a copy of an object with the virtual keys removed (the same filtering the ORM applies before writing to the database): If you would rather centralise this, define a toJSON hook on the model. It receives the plain result object (already free of virtual fields) plus the instance, and returns the final shape: Summary DataTypes.VIRTUAL / DataTypes.VIRTUALSTRING declare fields with no database column. Accessors can be passed to the factory (DataTypes.VIRTUAL({ get, set })) or placed on the attribute; inside them this is the instance. Getters run on every read, so a virtual always reflects live column values; dependencies documents the columns it derives from. A getter may call this.get('otherVirtual') to compose virtual fields. Setters use this.setDataValue(...) (or proxy assignment) to derive and persist real columns from one assignment. Virtuals are stripped from every INSERT/UPDATE (filterVirtualFields) and from toJSON() output; add them back manually, via getVirtualFields(), or with a toJSON hook. Related reading Core concepts — the reading path these belong to Going further — the specialised material