Virtual Attributes in Sequelize

2017-03-10

You can specify an attribute as VIRTUAL while defining the model. A VIRTUAL attribute is not actually stored in the database but is available to your NodeJS code as if it is part of the model.

Definition

To define a virtual attribute:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
module.exports = function (sequelize, Sequelize) {
const inventoryItems = sequelize.define('inventory_items', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
field: 'id'
},
restaurantId: {
type: Sequelize.INTEGER,
field: 'restaurant_id',
references: {
model: 'restaurants',
key: 'id'
},
onUpdate: 'cascade',
onDelete: 'cascade'
},
availableQuantity: {
type: new Sequelize.VIRTUAL
},
quantityIn: Sequelize.INTEGER,
quantityOut: Sequelize.INTEGER
},
{
timestamps: false,
instanceMethods: {},
defaultScope: {},
scopes: {
view: {}
}
});

return inventoryItems;
};

Note: If you define the VIRTUAL key in migration, it will throw an error

Getters, Setters and Validations

You can apply setters, getters and validations to the virtual field just like you would to a regular attribute:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
module.exports = function (sequelize, Sequelize) {
const inventoryItems = sequelize.define('inventory_items', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
field: 'id'
},
restaurantId: {
type: Sequelize.INTEGER,
field: 'restaurant_id',
references: {
model: 'restaurants',
key: 'id'
},
onUpdate: 'cascade',
onDelete: 'cascade'
},
availableQuantity: {
type: Sequelize.VIRTUAL,
set: function (value) {
this.setDataValue('available_quantity', value);
this.setDataValue('amount_out', this.get('amount_in') - value);
},
validate: {
isSmallerThanAmountIn: function (value) {
if (value > this.get('amount_in')) {
throw new Error('availableQuantity cannot be greater than amountIn');
}
}
},
get: function () {
return (this.get('amount_in') - this.get('amount_out'));
}
},
amountIn: {
type: Sequelize.INTEGER,
field: 'amount_in'
},
amountOut: {
type: Sequelize.INTEGER,
field: 'amount_out'
}
},
{
timestamps: false,
instanceMethods: {},
defaultScope: {},
scopes: {
view: {}
}
});

return inventoryItems;
};

Return Types and Dependencies

Virtual attributes can also take return types and dependency fields. To specify types and dependent fields, call the Virtual constructor:

1
2
3
availableQuantity: {
type: new Sequelize.VIRTUAL(Sequelize.INTEGER, ['amount_in', 'amount_out'])
}

Specifying dependency fields automatically loads the dependent fields even if you haven’t referred to them while selecting attributes.