4.0 Model definition syntax
See original GitHub issueBecause the dicussion is spread across many issues and PRs, I wanted to open a new issue to discuss the future syntax for model definition.
Currently, besides the old define(), this syntax is possible in v4.0.0-1:
export class User extends Model {
get fullName() {
return this.firstName + ' ' + this.lastName;
}
set fullName(fullName) {
const names = fullName.split(' ');
this.lastName = names.pop();
this.firstName = names.join(' ');
}
}
User.init(({
username: {
type: DataTypes.STRING,
primaryKey: true
},
lastName: DataTypes.STRING,
firstName: DataTypes.STRING,
}, { sequelize })
If you prefer to not do the initialization (which also attaches the connection to the model) inside the model file, but rather from a model index file, this is also possible:
export class User extends Model {
get fullName() {
return this.firstName + ' ' + this.lastName;
}
set fullName(fullName) {
const names = fullName.split(' ');
this.lastName = names.pop();
this.firstName = names.join(' ');
}
static init(sequelize) {
super.init({
username: {
type: DataTypes.STRING,
primaryKey: true
},
lastName: DataTypes.STRING,
firstName: DataTypes.STRING,
}, { sequelize })
}
}
With the help of a decorator library and Babel transpilation, this is also possible:
@Options({ sequelize })
@Attributes({
username: {
type: DataTypes.STRING,
primaryKey: true
},
lastName: DataTypes.STRING,
firstName: DataTypes.STRING,
})
export class User extends Model {
get fullName() {
return this.firstName + ' ' + this.lastName;
}
set fullName(fullName) {
const names = fullName.split(' ');
this.lastName = names.pop();
this.firstName = names.join(' ');
}
}
And with TypeScript you can go even further:
@Options({ sequelize })
export class User extends Model {
@Attribute({
type: DataTypes.STRING,
primaryKey: true
})
public username: string;
@Attribute(DataTypes.STRING)
public firstName: string;
@Attribute() // Type is inferred as DataTypes.STRING
public lastName: string;
get fullName(): string {
return this.firstName + ' ' + this.lastName;
}
set fullName(fullName: string) {
const names = fullName.split(' ');
this.lastName = names.pop();
this.firstName = names.join(' ');
}
}
BUT we definitely do not want to require the usage of any transpiler. The Model.init() API feels a bit weird, especially how the sequelize connection is passed. Some thoughts:
- If we want to make models independent one day from connections, this is has the benefit of easily omitting the
sequelizeoption - But we would probably still need to require that models are registered in a model manager, so we could inverse the syntax (something like
sequelize.add(Model)instead of passing the connection. That is not implemented atm though, currently the model needs to know about the connection.
Please chime in and share your thoughts and ideas!
Issues/PRs for reference: https://github.com/sequelize/sequelize/issues/5898 https://github.com/sequelize/sequelize/pull/6439 https://github.com/sequelize/sequelize/pull/5877 https://github.com/sequelize/sequelize/pull/5924 https://github.com/sequelize/sequelize/issues/5205 https://github.com/sequelize/sequelize/issues/4728
Issue Analytics
- State:
- Created 7 years ago
- Reactions:17
- Comments:83 (30 by maintainers)

Top Related StackOverflow Question
After all night of tweaking, I finally have a working example, based on @leebenson and @felixfbecker’s posts on this thread. @snewell92 take a look at this.
@felixfbecker one needs to explicitly
returnsuper.init({...}, {sequelize});instatic init(...). The childinitfunction should also take a sequelize instance.To use, put all 4 files in an empty directory and do
node index.js.@mickhansen In my example, using Node 6.11.3 LTS, I did not need a transpiler for
static.index.js
Post.js
User.js
Comment.js
I understand that your example above is the way it works now. I provided alternate syntax because it doesn’t seem like you’re settled a pattern yet, and IMO, my way was cleaner.
FWIW, here’s a more complete example of what my
Usermodel currently looks like (in production; cut for brevity)There are a few things here, IMO, that are improved over the syntax I’ve seen so far:
sequelizeobject or pass it around models. That’s defined in another file, where I also have a ‘model manager’ that pulls in the models. IMO, models should be loaded into the connection… not the other way around. Otherwise every model winds up importing the same object and repeating the boilerplate.associatestatic (pulled from the CLI) is actually one of the best parts of the model loader, IMO. It ensures that Model A can always reference Model B, and it neatly avoids circular dependencies. In your example above, ifUser.belongsTo(UserGroup)andUserGroup.hasMany(User), you’re relying on both model files being fully instantiated before exporting and importing into sister models. It’s neater to define this behaviour in a model manager when you’re already working with loaded data.fields()andattributes()satisfies an interface-like pattern for Sequelize, the same waymutations()does for GraphQL.user.js
db.js
app.js
Instead, a user has to:
.babelrcto get the right combination of plug-ins (if they’re following the decorate route).class.init()initialisation, that takes two parameters defining field types and asequelizeobject- both completely disconnected from the class that’s being initialised OR…{ sequelize }in the ‘options’ object, which a model manager could inject behind the scenes.I’m just providing friendly feedback, as a user of Sequelize since the very early days. In v4 you have a chance to drastically reduce boilerplate. I think what you currently have just adds noise. Why is it any better than defining
instanceMethodsorclassMethodson a giant object todefine()?Again, I’m brand new to v4, so maybe you’ve figured those kinds of things out and I’m way off the mark. Maybe you already have a model manager that wires stuff up. I’m still figuring out my way around v4.