Defining a Model
In this tutorial you will learn what models are in Sequelize and how to use them.
Concept
Models are the essence of Sequelize. A model is an abstraction that represents a table in your database.
In Sequelize, it is a class that extends the Model
class.
The model tells Sequelize several things about the entity it represents, such as the name of the table in the database and which columns it has (and their data types).
A model in Sequelize has a name. This name does not have to be the same name of the table it represents in the database. Usually, models have singular names (such as User
)
while tables have pluralized names (such as users
), although this is fully configurable.
Each instance of a model maps to one row of the table in the database. Basically, model instances are DAOs.
Defining a Model
This chapter will go into detail about how to create a model in Sequelize. Sequelize uses a class-based approach to define models, and makes heavy uses of decorators to define the model's metadata. You can choose to avoid using decorators, but you should read this chapter first.
Sequelize currently only supports the legacy/experimental decorator format. Support for the new decorator format will be added in a future release.
All decorators must be imported from @sequelize/core/decorators-legacy
:
import { Attribute, Table } from '@sequelize/core/decorators-legacy';
Using legacy decorators requires to use a transpiler such as TypeScript, Babel or others to compile them to JavaScript. Alternatively, Sequelize also supports a legacy approach that does not require using decorators, but this is discouraged.
If you're using TypeScript, you will also need to configure it to be able to resolve this export, see our Getting Started guide for more information.
To learn with an example, we will consider that we want to create a model to represent users, which have a firstName
and a lastName
.
We want our model to be called User
, and the table it represents is called users
in the database.
Defining a model is simple: Create a class that extends the Model
class and add instance properties to it.
- TypeScript
- JavaScript
import {
Sequelize,
DataTypes,
Model,
InferAttributes,
InferCreationAttributes,
CreationOptional,
} from '@sequelize/core';
import { Attribute, PrimaryKey, AutoIncrement, NotNull } from '@sequelize/core/decorators-legacy';
import { SqliteDialect } from '@sequelize/sqlite3';
const sequelize = new Sequelize({ dialect: SqliteDialect });
export class User extends Model<InferAttributes<User>, InferCreationAttributes<User>> {
@Attribute(DataTypes.INTEGER)
@PrimaryKey
@AutoIncrement
declare id: CreationOptional<number>;
@Attribute(DataTypes.STRING)
@NotNull
declare firstName: string;
@Attribute(DataTypes.STRING)
declare lastName: string | null;
}
import { Sequelize, DataTypes, Model } from '@sequelize/core';
import { Attribute, PrimaryKey, AutoIncrement, NotNull } from '@sequelize/core/decorators-legacy';
import { SqliteDialect } from '@sequelize/sqlite3';
const sequelize = new Sequelize({ dialect: SqliteDialect });
export class User extends Model {
@Attribute(DataTypes.INTEGER)
@PrimaryKey
@AutoIncrement
id;
@Attribute(DataTypes.STRING)
@NotNull
firstName;
@Attribute(DataTypes.STRING)
lastName;
}
Many decorators are available to configure your attributes. You can find the list of decorators here. You can also define associations between models, which will be covered in a following chapter.
Initializing the Model
After defining the model, you need to initialize it. This is done by passing it to the Sequelize constructor:
import { Sequelize } from '@sequelize/core';
import { SqliteDialect } from '@sequelize/sqlite3';
import { User } from './models/user.model.js';
export const sequelize = new Sequelize({
dialect: SqliteDialect,
// add all your models here
models: [User],
});
You can also load all your models dynamically by using importModels
. Note that this method is async,
and uses import
(esm), not require
(commonjs):
import { Sequelize, importModels } from '@sequelize/core';
import { SqliteDialect } from '@sequelize/sqlite3';
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
const __dirname = dirname(fileURLToPath(import.meta.url));
export const sequelize = new Sequelize({
dialect: SqliteDialect,
// this will load all model classes found in files matching this glob pattern.
models: await importModels(__dirname + '/**/*.model.{ts,js}'),
});
importModels
uses fast-glob
, and expects a glob pattern (or an array of glob patterns) as its first argument.
Without Decorators
If you're working on a project that was created before Sequelize 7, you're likely using the old way of defining models. This is still supported, but we recommend to use the new way of defining models.
If you do not want to use decorators, you can continue to use the old way of defining models. Head to our page about Legacy Model Definitions to learn more about the old API.
Column Data Types
Every attribute you define in your model must have a data type. You can use the @Attribute
decorator to define the data type of an attribute:
The data type defines the type of your column. e.g. an attribute with a type DataTypes.INTEGER
will result in a column of type INTEGER
.
The list of supported Data Types is available here.
Nullability
Just like in SQL, attributes are nullable by default in Sequelize
You can change this behavior by using the @NotNull
decorator:
- TypeScript
- JavaScript
import { DataTypes, Model, InferAttributes, InferCreationAttributes } from '@sequelize/core';
import { Attribute, NotNull } from '@sequelize/core/decorators-legacy';
class User extends Model<InferAttributes<User>, InferCreationAttributes<User>> {
@Attribute(DataTypes.STRING)
@NotNull
declare firstName: string;
@Attribute(DataTypes.STRING)
declare lastName: string | null;
}
import { DataTypes, Model } from '@sequelize/core';
import { Attribute, NotNull, Default } from '@sequelize/core/decorators-legacy';
class User extends Model {
// This attribute is non-null
@Attribute(DataTypes.STRING)
@NotNull
firstName;
// This attribute is nullable
@Attribute(DataTypes.STRING)
lastName;
}
Default Values
By default, Sequelize assumes that the default value of a column is null
.
This behavior can be changed by using the @Default
decorator:
- TypeScript
- JavaScript
import {
DataTypes,
Model,
InferAttributes,
InferCreationAttributes,
CreationOptional,
} from '@sequelize/core';
import { Attribute, NotNull, Default } from '@sequelize/core/decorators-legacy';
class User extends Model<InferAttributes<User>, InferCreationAttributes<User>> {
@Attribute(DataTypes.STRING)
@NotNull
@Default('John')
declare firstName: CreationOptional<string>;
}
CreationOptional
If you use @Default
, your attribute won't need to be specified when calling methods such as User.create
.
To let TypeScript know that this attribute has a default value, you can use the CreationOptional
type, like in the example above.
import { DataTypes, Model } from '@sequelize/core';
import { Attribute, NotNull, Default } from '@sequelize/core/decorators-legacy';
class User extends Model {
@Attribute(DataTypes.STRING)
@NotNull
@Default('John')
firstName;
}
Dynamic JS default values
Instead of a static value, you can also use a JavaScript function that sequelize will call every time a new instance is created.
Sequelize provides the built-in DataTypes.NOW
for the current timestamp,
but a custom function can be used as well:
- TypeScript
- JavaScript
import uniqid from 'uniqid';
import {
DataTypes,
Model,
InferAttributes,
InferCreationAttributes,
CreationOptional,
} from '@sequelize/core';
import { Attribute, NotNull, Default, PrimaryKey } from '@sequelize/core/decorators-legacy';
class User extends Model<InferAttributes<User>, InferCreationAttributes<User>> {
@PrimaryKey
@Attribute(DataTypes.STRING(18))
// Generate a new ID every time a new instance is created
@Default(() => uniqid())
declare id: CreationOptional<string>;
@Attribute(DataTypes.DATE)
@NotNull
// The current date/time will be used to populate this column at the moment of insertion
@Default(DataTypes.NOW)
declare registeredAt: CreationOptional<Date>;
}
import uniqid from 'uniqid';
import { DataTypes, Model } from '@sequelize/core';
import { Attribute, NotNull, Default, PrimaryKey } from '@sequelize/core/decorators-legacy';
class User extends Model {
@PrimaryKey
@Attribute(DataTypes.STRING(18))
// Generate a new ID every time a new instance is created
@Default(() => uniqid())
id;
@Attribute(DataTypes.DATE)
@NotNull
// The current date/time will be used to populate this column at the moment of insertion
@Default(DataTypes.NOW)
registeredAt;
}
The generation of values for DataTypes.NOW
and other JavaScript functions are not handled by the Database,
but by Sequelize itself. This means that they will only be used when using Model methods. They will not be used in raw queries,
in migrations, and all other places where Sequelize does not have access to the Model.
Read about SQL based alternatives in Dynamic SQL default values.
Dynamic SQL default values
You can also use a SQL function as a default value using raw SQL. This has the advantage over dynamic JS default values that the function will always be called, since it is handled by the database, not by Sequelize.
For instance, you could use the sql.fn
function to make the database generate a random number for you:
- TypeScript
- JavaScript
import {
DataTypes,
Model,
sql,
InferAttributes,
InferCreationAttributes,
CreationOptional,
} from '@sequelize/core';
import { Attribute, Default } from '@sequelize/core/decorators-legacy';
class User extends Model<InferAttributes<User>, InferCreationAttributes<User>> {
@Attribute(DataTypes.FLOAT)
@Default(sql.fn('random'))
declare randomNumber: CreationOptional<number>;
}
import { DataTypes, Model, sql } from '@sequelize/core';
import { Attribute, Default } from '@sequelize/core/decorators-legacy';
class User extends Model {
@Attribute(DataTypes.FLOAT)
@Default(sql.fn('random'))
randomNumber;
}
Sequelize also provides 2 built-in functions, sql.uuidV1
and sql.uuidV4
, that will
generate a UUID in SQL if possible, and fallback to a JavaScript implementation otherwise.
Primary Keys
Sequelize automatically adds an auto-incremented integer attribute called id
as primary key if none is specified.
If you use TypeScript, you can declare the typing of this attribute like this:
import {
DataTypes,
Model,
InferAttributes,
InferCreationAttributes,
CreationOptional,
} from '@sequelize/core';
class User extends Model<InferAttributes<User>, InferCreationAttributes<User>> {
declare id: CreationOptional<number>;
}
If you want to use a different attribute, type, or want to use a composite primary key, you can use the @PrimaryKey
decorator.
This will stop Sequelize from adding the id
attribute automatically:
- TypeScript
- JavaScript
import {
DataTypes,
Model,
InferAttributes,
InferCreationAttributes,
CreationOptional,
} from '@sequelize/core';
import { Attribute, PrimaryKey, AutoIncrement } from '@sequelize/core/decorators-legacy';
class User extends Model<InferAttributes<User>, InferCreationAttributes<User>> {
@Attribute(DataTypes.INTEGER)
@PrimaryKey
@AutoIncrement
declare internalId: CreationOptional<number>;
}
CreationOptional
If you use @AutoIncrement
, your attribute won't need to be specified when calling methods such as User.create
.
To let TypeScript know that this attribute has a default value, you can use the CreationOptional
type, like in the example above.
import { DataTypes, Model } from '@sequelize/core';
import { Attribute, PrimaryKey, AutoIncrement } from '@sequelize/core/decorators-legacy';
class User extends Model {
@Attribute(DataTypes.INTEGER)
@PrimaryKey
@AutoIncrement
internalId;
}
The @AutoIncrement
decorator can also be used on primary key columns to make them auto-incremented.
You table cannot have a primary key? See How to remove the primary key.
Composite Primary Keys
Composite primary keys are supported by using the @PrimaryKey
decorator on multiple attributes:
- TypeScript
- JavaScript
import { DataTypes, Model, InferAttributes, InferCreationAttributes } from '@sequelize/core';
import { Attribute, PrimaryKey } from '@sequelize/core/decorators-legacy';
class UserRole extends Model<InferAttributes<UserRole>, InferCreationAttributes<UserRole>> {
@Attribute(DataTypes.INTEGER)
@PrimaryKey
declare userId: number;
@Attribute(DataTypes.INTEGER)
@PrimaryKey
declare roleId: number;
}
import { DataTypes, Model } from '@sequelize/core';
import { Attribute, PrimaryKey } from '@sequelize/core/decorators-legacy';
class UserRole extends Model {
@Attribute(DataTypes.INTEGER)
@PrimaryKey
userId;
@Attribute(DataTypes.INTEGER)
@PrimaryKey
roleId;
}
Table & Column names
Head to the Naming Strategies API page to learn how to customize the names of your tables and columns.
Database schema
By default, Sequelize places your table in your database's default schema. You can specify a different schema by using the schema
model option:
import { Model } from '@sequelize/core';
import { Table } from '@sequelize/core/decorators-legacy';
// This model's table will be created in the "public" schema
@Table({ schema: 'public' })
export class User extends Model {}
You can also change the default schema for all models by setting the schema
option in the Sequelize
constructor:
import { Model, Sequelize } from '@sequelize/core';
import { SqliteDialect } from '@sequelize/sqlite3';
class User extends Model {}
const sequelize = new Sequelize({
dialect: SqliteDialect,
// All models that don't specify a schema will be created in the "public" schema by default
schema: 'public',
models: [User],
});
Model Methods
Sequelize models are classes. You can very easily add custom instance or class level methods.
- TypeScript
- JavaScript
import { Model, InferCreationAttributes, InferAttributes } from '@sequelize/core';
import { SqliteDialect } from '@sequelize/sqlite3';
import { Attribute, NotNull } from '@sequelize/core/decorators-legacy';
class User extends Model<InferAttributes<User>, InferCreationAttributes<User>> {
@Attribute(DataTypes.STRING)
@NotNull
declare firstname: string;
@Attribute(DataTypes.STRING)
@NotNull
declare lastname: string;
instanceLevelMethod() {
return 'bar';
}
getFullname() {
return [this.firstname, this.lastname].join(' ');
}
static classLevelMethod() {
return 'foo';
}
}
const sequelize = new Sequelize({
dialect: SqliteDialect,
models: [User],
});
console.log(User.classLevelMethod()); // 'foo'
const user = User.build({ firstname: 'Jane', lastname: 'Doe' });
console.log(user.instanceLevelMethod()); // 'bar'
console.log(user.getFullname()); // 'Jane Doe'
import { Model } from '@sequelize/core';
import { SqliteDialect } from '@sequelize/sqlite3';
import { Attribute, NotNull } from '@sequelize/core/decorators-legacy';
class User extends Model {
@Attribute(DataTypes.STRING)
@NotNull
firstname;
@Attribute(DataTypes.STRING)
@NotNull
lastname;
instanceLevelMethod() {
return 'bar';
}
getFullname() {
return [this.firstname, this.lastname].join(' ');
}
static classLevelMethod() {
return 'foo';
}
}
const sequelize = new Sequelize({
dialect: SqliteDialect,
models: [User],
});
console.log(User.classLevelMethod()); // 'foo'
const user = User.build({ firstname: 'Jane', lastname: 'Doe' });
console.log(user.instanceLevelMethod()); // 'bar'
console.log(user.getFullname()); // 'Jane Doe'
Where to go from here?
A good next step is to synchronise your models with the database, after which you'll be able to create, read, update and delete your models.
Alternatively, find out how to define associations between models, learn about indexes, constraints, and other advanced features,
or take a look at the API Reference of the @Attribute
and @Table
decorators to learn about the many more options they offer to customize your attributes and models.