diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 00000000..a86b051e
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1 @@
+* @sequelize/code-reviewers
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 1bad4b3d..c30a9c2c 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,6 +1,6 @@
# These are supported funding model platforms
-github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+github: sequelize
patreon: # Replace with a single Patreon username
open_collective: sequelize
ko_fi: # Replace with a single Ko-fi username
diff --git a/.github/workflows/autoupdate.yml b/.github/workflows/autoupdate.yml
new file mode 100644
index 00000000..841aee7a
--- /dev/null
+++ b/.github/workflows/autoupdate.yml
@@ -0,0 +1,40 @@
+name: auto-update PRs & label conflicts
+on:
+ push:
+ branches:
+ - main
+ # can also be used with the pull_request event
+ pull_request_target:
+ types:
+ - synchronize
+ # allow the workflow to correct any label incorrectly added or removed by a user.
+ - labeled
+ - unlabeled
+ # update the PR as soon as the auto-merge is enabled.
+ - auto_merge_enabled
+ # process the PR once they appear in the search filters
+ - ready_for_review
+ - opened
+ - reopened
+jobs:
+ autoupdate:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Generate Sequelize Bot Token
+ id: generate-token
+ uses: actions/create-github-app-token@v1
+ with:
+ app-id: '${{ secrets.SEQUELIZE_BOT_APP_ID }}'
+ private-key: '${{ secrets.SEQUELIZE_BOT_PRIVATE_KEY }}'
+ - uses: sequelize/pr-auto-update-and-handle-conflicts@257ac5f68859672393e3320495164251140bd801 # 1.0.1
+ with:
+ conflict-label: 'conflicted'
+ conflict-requires-ready-state: 'ready_for_review'
+ conflict-excluded-authors: 'bot/renovate'
+ update-pr-branches: true
+ update-requires-auto-merge: true
+ update-requires-ready-state: 'ready_for_review'
+ update-excluded-authors: 'bot/renovate'
+ update-excluded-labels: 'no-autoupdate'
+ env:
+ GITHUB_TOKEN: '${{ steps.generate-token.outputs.token }}'
diff --git a/.github/workflows/draft.yml b/.github/workflows/draft.yml
index 2924d57c..f073abce 100644
--- a/.github/workflows/draft.yml
+++ b/.github/workflows/draft.yml
@@ -14,13 +14,14 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
- - uses: actions/setup-node@v3
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
with:
- node-version: 16.x
+ node-version: 20.x
cache: yarn
- run: yarn install --frozen-lockfile
- run: yarn lint-no-fix
+ - run: yarn lint-scss-no-fix
- run: yarn typecheck
- run: yarn sync
- run: yarn build
@@ -33,7 +34,7 @@ jobs:
shell: bash
run: |
netlify deploy --dir build --message '${{ github.event.commits[0].message }}' > log.tmp.txt 2>&1
- cat log.tmp.txt | grep -E 'Logs|Website Draft URL' > log.txt
+ cat log.tmp.txt | grep -E 'Website draft URL:' > log.txt
- name: Read deployment log
if: '! github.event.pull_request.head.repo.fork'
id: logs
@@ -43,7 +44,7 @@ jobs:
- name: Comment PR with draft publish logs
if: '! github.event.pull_request.head.repo.fork'
id: create-comment
- uses: peter-evans/create-or-update-comment@v3
+ uses: peter-evans/create-or-update-comment@v4
with:
issue-number: ${{ github.event.pull_request.number }}
body: |
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 971b30c4..1d733d24 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -12,10 +12,10 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
- - uses: actions/setup-node@v3
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
with:
- node-version: 16.x
+ node-version: 20.x
cache: yarn
- run: yarn install --frozen-lockfile
- run: yarn lint-no-fix
diff --git a/.husky/pre-commit b/.husky/pre-commit
index 36af2198..2312dc58 100755
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -1,4 +1 @@
-#!/bin/sh
-. "$(dirname "$0")/_/husky.sh"
-
npx lint-staged
diff --git a/.prettierrc.json b/.prettierrc.json
index 8db60caa..544138be 100644
--- a/.prettierrc.json
+++ b/.prettierrc.json
@@ -1,3 +1,3 @@
{
- "singleQuote": true
+ "singleQuote": true
}
diff --git a/docs/_fragments/_decorator-info.mdx b/docs/_fragments/_decorator-info.mdx
new file mode 100644
index 00000000..7f47d8c4
--- /dev/null
+++ b/docs/_fragments/_decorator-info.mdx
@@ -0,0 +1,16 @@
+:::info
+
+Sequelize currently only supports the [_legacy/experimental decorator format_](https://github.com/tc39/proposal-decorators#how-does-this-proposal-compare-to-other-versions-of-decorators).
+Support for the [_new decorator format_](https://github.com/tc39/proposal-decorators) will be added in a future release.
+
+All decorators must be imported from `@sequelize/core/decorators-legacy`:
+
+```ts
+import { Attribute, Table } from '@sequelize/core/decorators-legacy';
+```
+
+Using legacy decorators requires to use a transpiler such as [TypeScript](https://www.typescriptlang.org/docs/handbook/decorators.html#decorators),
+[Babel](https://babeljs.io/docs/babel-plugin-proposal-decorators) or others to compile them to JavaScript.
+Alternatively, Sequelize also supports [a legacy approach](../other-topics/legacy-model-definitions.mdx) that does not require using decorators, but this is discouraged.
+
+:::
diff --git a/docs/_fragments/_js-default-caution.mdx b/docs/_fragments/_js-default-caution.mdx
new file mode 100644
index 00000000..20abee07
--- /dev/null
+++ b/docs/_fragments/_js-default-caution.mdx
@@ -0,0 +1,9 @@
+:::caution
+
+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](../querying/raw-queries.mdx),
+in [migrations](../models/migrations.md), and all other places where Sequelize does not have access to the Model.
+
+Read about SQL based alternatives in [Dynamic SQL default values](../models/defining-models.mdx#dynamic-sql-default-values).
+
+:::
diff --git a/docs/_fragments/_uuid-support-table.mdx b/docs/_fragments/_uuid-support-table.mdx
new file mode 100644
index 00000000..809b31ff
--- /dev/null
+++ b/docs/_fragments/_uuid-support-table.mdx
@@ -0,0 +1,10 @@
+import { DialectTableFilter } from '@site/src/components/dialect-table-filter.tsx';
+
+
+
+| | PostgreSQL | MariaDB | MySQL | MSSQL | SQLite | Snowflake | db2 | ibmi |
+|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------|----------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------|--------|-----------|-----|------|
+| `uuidV1` | [`uuid_generate_v1`](https://www.postgresql.org/docs/current/uuid-ossp.html) (requires `uuid-ossp`) | [`UUID`](https://mariadb.com/kb/en/uuid/) | [`UUID`](https://dev.mysql.com/doc/refman/8.0/en/miscellaneous-functions.html#function_uuid) | N/A | N/A | N/A | N/A | N/A |
+| `uuidV4` | __pg >= v13__: [`gen_random_uuid`](https://www.postgresql.org/docs/current/functions-uuid.html) __pg < v13__: [`uuid_generate_v4`](https://www.postgresql.org/docs/current/uuid-ossp.html) (requires `uuid-ossp`) | N/A | N/A | [`NEWID`](https://learn.microsoft.com/en-us/sql/t-sql/functions/newid-transact-sql?view=sql-server-ver16) | N/A | N/A | N/A | N/A |
+
+
diff --git a/docs/advanced-association-concepts/advanced-many-to-many.md b/docs/advanced-association-concepts/advanced-many-to-many.md
deleted file mode 100644
index 4ad22b1e..00000000
--- a/docs/advanced-association-concepts/advanced-many-to-many.md
+++ /dev/null
@@ -1,670 +0,0 @@
----
-sidebar_position: 3
-title: Advanced M:N Associations
----
-
-Make sure you have read the [associations guide](../core-concepts/assocs.md) before reading this guide.
-
-Let's start with an example of a Many-to-Many relationship between `User` and `Profile`.
-
-```js
-const User = sequelize.define('user', {
- username: DataTypes.STRING,
- points: DataTypes.INTEGER
-}, { timestamps: false });
-const Profile = sequelize.define('profile', {
- name: DataTypes.STRING
-}, { timestamps: false });
-```
-
-The simplest way to define the Many-to-Many relationship is:
-
-```js
-User.belongsToMany(Profile, { through: 'User_Profiles' });
-Profile.belongsToMany(User, { through: 'User_Profiles' });
-```
-
-By passing a string to `through` above, we are asking Sequelize to automatically generate a model named `User_Profiles` as the *through table* (also known as junction table), with only two columns: `userId` and `profileId`. A composite unique key will be established on these two columns.
-
-We can also define ourselves a model to be used as the through table.
-
-```js
-const User_Profile = sequelize.define('User_Profile', {}, { timestamps: false });
-User.belongsToMany(Profile, { through: User_Profile });
-Profile.belongsToMany(User, { through: User_Profile });
-```
-
-The above has the exact same effect. Note that we didn't define any attributes on the `User_Profile` model. The fact that we passed it into a `belongsToMany` call tells sequelize to create the two attributes `userId` and `profileId` automatically, just like other associations also cause Sequelize to automatically add a column to one of the involved models.
-
-However, defining the model by ourselves has several advantages. We can, for example, define more columns on our through table:
-
-```js
-const User_Profile = sequelize.define('User_Profile', {
- selfGranted: DataTypes.BOOLEAN
-}, { timestamps: false });
-User.belongsToMany(Profile, { through: User_Profile });
-Profile.belongsToMany(User, { through: User_Profile });
-```
-
-With this, we can now track an extra information at the through table, namely the `selfGranted` boolean. For example, when calling the `user.addProfile()` we can pass values for the extra columns using the `through` option.
-
-Example:
-
-```js
-const amidala = await User.create({ username: 'p4dm3', points: 1000 });
-const queen = await Profile.create({ name: 'Queen' });
-await amidala.addProfile(queen, { through: { selfGranted: false } });
-const result = await User.findOne({
- where: { username: 'p4dm3' },
- include: Profile
-});
-console.log(result);
-```
-
-Output:
-
-```json
-{
- "id": 4,
- "username": "p4dm3",
- "points": 1000,
- "profiles": [
- {
- "id": 6,
- "name": "queen",
- "User_Profile": {
- "userId": 4,
- "profileId": 6,
- "selfGranted": false
- }
- }
- ]
-}
-```
-
-You can create all relationship in single `create` call too.
-
-Example:
-
-```js
-const amidala = await User.create({
- username: 'p4dm3',
- points: 1000,
- profiles: [{
- name: 'Queen',
- User_Profile: {
- selfGranted: true
- }
- }]
-}, {
- include: Profile
-});
-
-const result = await User.findOne({
- where: { username: 'p4dm3' },
- include: Profile
-});
-
-console.log(result);
-```
-
-Output:
-
-```json
-{
- "id": 1,
- "username": "p4dm3",
- "points": 1000,
- "profiles": [
- {
- "id": 1,
- "name": "Queen",
- "User_Profile": {
- "selfGranted": true,
- "userId": 1,
- "profileId": 1
- }
- }
- ]
-}
-```
-
-You probably noticed that the `User_Profiles` table does not have an `id` field. As mentioned above, it has a composite unique key instead. The name of this composite unique key is chosen automatically by Sequelize but can be customized with the `uniqueKey` option:
-
-```js
-User.belongsToMany(Profile, { through: User_Profiles, uniqueKey: 'my_custom_unique' });
-```
-
-Another possibility, if desired, is to force the through table to have a primary key just like other standard tables. To do this, simply define the primary key in the model:
-
-```js
-const User_Profile = sequelize.define('User_Profile', {
- id: {
- type: DataTypes.INTEGER,
- primaryKey: true,
- autoIncrement: true,
- allowNull: false
- },
- selfGranted: DataTypes.BOOLEAN
-}, { timestamps: false });
-User.belongsToMany(Profile, { through: User_Profile });
-Profile.belongsToMany(User, { through: User_Profile });
-```
-
-The above will still create two columns `userId` and `profileId`, of course, but instead of setting up a composite unique key on them, the model will use its `id` column as primary key. Everything else will still work just fine.
-
-## Through tables versus normal tables and the "Super Many-to-Many association"
-
-Now we will compare the usage of the last Many-to-Many setup shown above with the usual One-to-Many relationships, so that in the end we conclude with the concept of a *"Super Many-to-Many relationship"*.
-
-### Models recap (with minor rename)
-
-To make things easier to follow, let's rename our `User_Profile` model to `grant`. Note that everything works in the same way as before. Our models are:
-
-```js
-const User = sequelize.define('user', {
- username: DataTypes.STRING,
- points: DataTypes.INTEGER
-}, { timestamps: false });
-
-const Profile = sequelize.define('profile', {
- name: DataTypes.STRING
-}, { timestamps: false });
-
-const Grant = sequelize.define('grant', {
- id: {
- type: DataTypes.INTEGER,
- primaryKey: true,
- autoIncrement: true,
- allowNull: false
- },
- selfGranted: DataTypes.BOOLEAN
-}, { timestamps: false });
-```
-
-We established a Many-to-Many relationship between `User` and `Profile` using the `Grant` model as the through table:
-
-```js
-User.belongsToMany(Profile, { through: Grant });
-Profile.belongsToMany(User, { through: Grant });
-```
-
-This automatically added the columns `userId` and `profileId` to the `Grant` model.
-
-**Note:** As shown above, we have chosen to force the `grant` model to have a single primary key (called `id`, as usual). This is necessary for the *Super Many-to-Many relationship* that will be defined soon.
-
-### Using One-to-Many relationships instead
-
-Instead of setting up the Many-to-Many relationship defined above, what if we did the following instead?
-
-```js
-// Setup a One-to-Many relationship between User and Grant
-User.hasMany(Grant);
-Grant.belongsTo(User);
-
-// Also setup a One-to-Many relationship between Profile and Grant
-Profile.hasMany(Grant);
-Grant.belongsTo(Profile);
-```
-
-The result is essentially the same! This is because `User.hasMany(Grant)` and `Profile.hasMany(Grant)` will automatically add the `userId` and `profileId` columns to `Grant`, respectively.
-
-This shows that one Many-to-Many relationship isn't very different from two One-to-Many relationships. The tables in the database look the same.
-
-The only difference is when you try to perform an eager load with Sequelize.
-
-```js
-// With the Many-to-Many approach, you can do:
-User.findAll({ include: Profile });
-Profile.findAll({ include: User });
-// However, you can't do:
-User.findAll({ include: Grant });
-Profile.findAll({ include: Grant });
-Grant.findAll({ include: User });
-Grant.findAll({ include: Profile });
-
-// On the other hand, with the double One-to-Many approach, you can do:
-User.findAll({ include: Grant });
-Profile.findAll({ include: Grant });
-Grant.findAll({ include: User });
-Grant.findAll({ include: Profile });
-// However, you can't do:
-User.findAll({ include: Profile });
-Profile.findAll({ include: User });
-// Although you can emulate those with nested includes, as follows:
-User.findAll({
- include: {
- model: Grant,
- include: Profile
- }
-}); // This emulates the `User.findAll({ include: Profile })`, however
- // the resulting object structure is a bit different. The original
- // structure has the form `user.profiles[].grant`, while the emulated
- // structure has the form `user.grants[].profiles[]`.
-```
-
-### The best of both worlds: the Super Many-to-Many relationship
-
-We can simply combine both approaches shown above!
-
-```js
-// The Super Many-to-Many relationship
-User.belongsToMany(Profile, { through: Grant });
-Profile.belongsToMany(User, { through: Grant });
-User.hasMany(Grant);
-Grant.belongsTo(User);
-Profile.hasMany(Grant);
-Grant.belongsTo(Profile);
-```
-
-This way, we can do all kinds of eager loading:
-
-```js
-// All these work:
-User.findAll({ include: Profile });
-Profile.findAll({ include: User });
-User.findAll({ include: Grant });
-Profile.findAll({ include: Grant });
-Grant.findAll({ include: User });
-Grant.findAll({ include: Profile });
-```
-
-We can even perform all kinds of deeply nested includes:
-
-```js
-User.findAll({
- include: [
- {
- model: Grant,
- include: [User, Profile]
- },
- {
- model: Profile,
- include: {
- model: User,
- include: {
- model: Grant,
- include: [User, Profile]
- }
- }
- }
- ]
-});
-```
-
-## Aliases and custom key names
-
-Similarly to the other relationships, aliases can be defined for Many-to-Many relationships.
-
-Before proceeding, please recall [the aliasing example for `belongsTo`](../core-concepts/assocs.md#defining-an-alias) on the [associations guide](../core-concepts/assocs.md). Note that, in that case, defining an association impacts both the way includes are done (i.e. passing the association name) and the name Sequelize chooses for the foreign key (in that example, `leaderId` was created on the `Ship` model).
-
-Defining an alias for a `belongsToMany` association also impacts the way includes are performed:
-
-```js
-Product.belongsToMany(Category, { as: 'groups', through: 'product_categories' });
-Category.belongsToMany(Product, { as: 'items', through: 'product_categories' });
-
-// [...]
-
-await Product.findAll({ include: Category }); // This doesn't work
-
-await Product.findAll({ // This works, passing the alias
- include: {
- model: Category,
- as: 'groups'
- }
-});
-
-await Product.findAll({ include: 'groups' }); // This also works
-```
-
-However, defining an alias here has nothing to do with the foreign key names. The names of both foreign keys created in the through table are still constructed by Sequelize based on the name of the models being associated. This can readily be seen by inspecting the generated SQL for the through table in the example above:
-
-```sql
-CREATE TABLE IF NOT EXISTS `product_categories` (
- `createdAt` DATETIME NOT NULL,
- `updatedAt` DATETIME NOT NULL,
- `productId` INTEGER NOT NULL REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
- `categoryId` INTEGER NOT NULL REFERENCES `categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
- PRIMARY KEY (`productId`, `categoryId`)
-);
-```
-
-We can see that the foreign keys are `productId` and `categoryId`. To change these names, Sequelize accepts the options `foreignKey` and `otherKey` respectively (i.e., the `foreignKey` defines the key for the source model in the through relation, and `otherKey` defines it for the target model):
-
-```js
-Product.belongsToMany(Category, {
- through: 'product_categories',
- foreignKey: 'objectId', // replaces `productId`
- otherKey: 'typeId' // replaces `categoryId`
-});
-Category.belongsToMany(Product, {
- through: 'product_categories',
- foreignKey: 'typeId', // replaces `categoryId`
- otherKey: 'objectId' // replaces `productId`
-});
-```
-
-Generated SQL:
-
-```sql
-CREATE TABLE IF NOT EXISTS `product_categories` (
- `createdAt` DATETIME NOT NULL,
- `updatedAt` DATETIME NOT NULL,
- `objectId` INTEGER NOT NULL REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
- `typeId` INTEGER NOT NULL REFERENCES `categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
- PRIMARY KEY (`objectId`, `typeId`)
-);
-```
-
-As shown above, when you define a Many-to-Many relationship with two `belongsToMany` calls (which is the standard way), you should provide the `foreignKey` and `otherKey` options appropriately in both calls. If you pass these options in only one of the calls, the Sequelize behavior will be unreliable.
-
-## Self-references
-
-Sequelize supports self-referential Many-to-Many relationships, intuitively:
-
-```js
-Person.belongsToMany(Person, { as: 'Children', through: 'PersonChildren' })
-// This will create the table PersonChildren which stores the ids of the objects.
-```
-
-## Specifying attributes from the through table
-
-By default, when eager loading a many-to-many relationship, Sequelize will return data in the following structure (based on the first example in this guide):
-
-```json
-// User.findOne({ include: Profile })
-{
- "id": 4,
- "username": "p4dm3",
- "points": 1000,
- "profiles": [
- {
- "id": 6,
- "name": "queen",
- "grant": {
- "userId": 4,
- "profileId": 6,
- "selfGranted": false
- }
- }
- ]
-}
-```
-
-Notice that the outer object is an `User`, which has a field called `profiles`, which is a `Profile` array, such that each `Profile` comes with an extra field called `grant` which is a `Grant` instance. This is the default structure created by Sequelize when eager loading from a Many-to-Many relationship.
-
-However, if you want only some of the attributes of the through table, you can provide an array with the attributes you want in the `attributes` option. For example, if you only want the `selfGranted` attribute from the through table:
-
-```js
-User.findOne({
- include: {
- model: Profile,
- through: {
- attributes: ['selfGranted']
- }
- }
-});
-```
-
-Output:
-
-```json
-{
- "id": 4,
- "username": "p4dm3",
- "points": 1000,
- "profiles": [
- {
- "id": 6,
- "name": "queen",
- "grant": {
- "selfGranted": false
- }
- }
- ]
-}
-```
-
-If you don't want the nested `grant` field at all, use `attributes: []`:
-
-```js
-User.findOne({
- include: {
- model: Profile,
- through: {
- attributes: []
- }
- }
-});
-```
-
-Output:
-
-```json
-{
- "id": 4,
- "username": "p4dm3",
- "points": 1000,
- "profiles": [
- {
- "id": 6,
- "name": "queen"
- }
- ]
-}
-```
-
-If you are using mixins (such as `user.getProfiles()`) instead of finder methods (such as `User.findAll()`), you have to use the `joinTableAttributes` option instead:
-
-```js
-someUser.getProfiles({ joinTableAttributes: ['selfGranted'] });
-```
-
-Output:
-
-```json
-[
- {
- "id": 6,
- "name": "queen",
- "grant": {
- "selfGranted": false
- }
- }
-]
-```
-
-## Many-to-many-to-many relationships and beyond
-
-Consider you are trying to model a game championship. There are players and teams. Teams play games. However, players can change teams in the middle of the championship (but not in the middle of a game). So, given one specific game, there are certain teams participating in that game, and each of these teams has a set of players (for that game).
-
-So we start by defining the three relevant models:
-
-```js
-const Player = sequelize.define('Player', { username: DataTypes.STRING });
-const Team = sequelize.define('Team', { name: DataTypes.STRING });
-const Game = sequelize.define('Game', { name: DataTypes.STRING });
-```
-
-Now, the question is: how to associate them?
-
-First, we note that:
-
-* One game has many teams associated to it (the ones that are playing that game);
-* One team may have participated in many games.
-
-The above observations show that we need a Many-to-Many relationship between Game and Team. Let's use the Super Many-to-Many relationship as explained earlier in this guide:
-
-```js
-// Super Many-to-Many relationship between Game and Team
-const GameTeam = sequelize.define('GameTeam', {
- id: {
- type: DataTypes.INTEGER,
- primaryKey: true,
- autoIncrement: true,
- allowNull: false
- }
-});
-Team.belongsToMany(Game, { through: GameTeam });
-Game.belongsToMany(Team, { through: GameTeam });
-GameTeam.belongsTo(Game);
-GameTeam.belongsTo(Team);
-Game.hasMany(GameTeam);
-Team.hasMany(GameTeam);
-```
-
-The part about players is trickier. We note that the set of players that form a team depends not only on the team (obviously), but also on which game is being considered. Therefore, we don't want a Many-to-Many relationship between Player and Team. We also don't want a Many-to-Many relationship between Player and Game. Instead of associating a Player to any of those models, what we need is an association between a Player and something like a *"team-game pair constraint"*, since it is the pair (team plus game) that defines which players belong there. So what we are looking for turns out to be precisely the junction model, GameTeam, itself! And, we note that, since a given *game-team pair* specifies many players, and on the other hand that the same player can participate of many *game-team pairs*, we need a Many-to-Many relationship between Player and GameTeam!
-
-To provide the greatest flexibility, let's use the Super Many-to-Many relationship construction here again:
-
-```js
-// Super Many-to-Many relationship between Player and GameTeam
-const PlayerGameTeam = sequelize.define('PlayerGameTeam', {
- id: {
- type: DataTypes.INTEGER,
- primaryKey: true,
- autoIncrement: true,
- allowNull: false
- }
-});
-Player.belongsToMany(GameTeam, { through: PlayerGameTeam });
-GameTeam.belongsToMany(Player, { through: PlayerGameTeam });
-PlayerGameTeam.belongsTo(Player);
-PlayerGameTeam.belongsTo(GameTeam);
-Player.hasMany(PlayerGameTeam);
-GameTeam.hasMany(PlayerGameTeam);
-```
-
-The above associations achieve precisely what we want. Here is a full runnable example of this:
-
-```js
-const { Sequelize, Op, Model, DataTypes } = require('@sequelize/core');
-const sequelize = new Sequelize('sqlite::memory:', {
- define: { timestamps: false } // Just for less clutter in this example
-});
-const Player = sequelize.define('Player', { username: DataTypes.STRING });
-const Team = sequelize.define('Team', { name: DataTypes.STRING });
-const Game = sequelize.define('Game', { name: DataTypes.STRING });
-
-// We apply a Super Many-to-Many relationship between Game and Team
-const GameTeam = sequelize.define('GameTeam', {
- id: {
- type: DataTypes.INTEGER,
- primaryKey: true,
- autoIncrement: true,
- allowNull: false
- }
-});
-Team.belongsToMany(Game, { through: GameTeam });
-Game.belongsToMany(Team, { through: GameTeam });
-GameTeam.belongsTo(Game);
-GameTeam.belongsTo(Team);
-Game.hasMany(GameTeam);
-Team.hasMany(GameTeam);
-
-// We apply a Super Many-to-Many relationship between Player and GameTeam
-const PlayerGameTeam = sequelize.define('PlayerGameTeam', {
- id: {
- type: DataTypes.INTEGER,
- primaryKey: true,
- autoIncrement: true,
- allowNull: false
- }
-});
-Player.belongsToMany(GameTeam, { through: PlayerGameTeam });
-GameTeam.belongsToMany(Player, { through: PlayerGameTeam });
-PlayerGameTeam.belongsTo(Player);
-PlayerGameTeam.belongsTo(GameTeam);
-Player.hasMany(PlayerGameTeam);
-GameTeam.hasMany(PlayerGameTeam);
-
-(async () => {
-
- await sequelize.sync();
- await Player.bulkCreate([
- { username: 's0me0ne' },
- { username: 'empty' },
- { username: 'greenhead' },
- { username: 'not_spock' },
- { username: 'bowl_of_petunias' }
- ]);
- await Game.bulkCreate([
- { name: 'The Big Clash' },
- { name: 'Winter Showdown' },
- { name: 'Summer Beatdown' }
- ]);
- await Team.bulkCreate([
- { name: 'The Martians' },
- { name: 'The Earthlings' },
- { name: 'The Plutonians' }
- ]);
-
- // Let's start defining which teams were in which games. This can be done
- // in several ways, such as calling `.setTeams` on each game. However, for
- // brevity, we will use direct `create` calls instead, referring directly
- // to the IDs we want. We know that IDs are given in order starting from 1.
- await GameTeam.bulkCreate([
- { GameId: 1, TeamId: 1 }, // this GameTeam will get id 1
- { GameId: 1, TeamId: 2 }, // this GameTeam will get id 2
- { GameId: 2, TeamId: 1 }, // this GameTeam will get id 3
- { GameId: 2, TeamId: 3 }, // this GameTeam will get id 4
- { GameId: 3, TeamId: 2 }, // this GameTeam will get id 5
- { GameId: 3, TeamId: 3 } // this GameTeam will get id 6
- ]);
-
- // Now let's specify players.
- // For brevity, let's do it only for the second game (Winter Showdown).
- // Let's say that that s0me0ne and greenhead played for The Martians, while
- // not_spock and bowl_of_petunias played for The Plutonians:
- await PlayerGameTeam.bulkCreate([
- // In 'Winter Showdown' (i.e. GameTeamIds 3 and 4):
- { PlayerId: 1, GameTeamId: 3 }, // s0me0ne played for The Martians
- { PlayerId: 3, GameTeamId: 3 }, // greenhead played for The Martians
- { PlayerId: 4, GameTeamId: 4 }, // not_spock played for The Plutonians
- { PlayerId: 5, GameTeamId: 4 } // bowl_of_petunias played for The Plutonians
- ]);
-
- // Now we can make queries!
- const game = await Game.findOne({
- where: {
- name: "Winter Showdown"
- },
- include: {
- model: GameTeam,
- include: [
- {
- model: Player,
- through: { attributes: [] } // Hide unwanted `PlayerGameTeam` nested object from results
- },
- Team
- ]
- }
- });
-
- console.log(`Found game: "${game.name}"`);
- for (let i = 0; i < game.GameTeams.length; i++) {
- const team = game.GameTeams[i].Team;
- const players = game.GameTeams[i].Players;
- console.log(`- Team "${team.name}" played game "${game.name}" with the following players:`);
- console.log(players.map(p => `--- ${p.username}`).join('\n'));
- }
-
-})();
-```
-
-Output:
-
-```text
-Found game: "Winter Showdown"
-- Team "The Martians" played game "Winter Showdown" with the following players:
---- s0me0ne
---- greenhead
-- Team "The Plutonians" played game "Winter Showdown" with the following players:
---- not_spock
---- bowl_of_petunias
-```
-
-So this is how we can achieve a *many-to-many-to-many* relationship between three models in Sequelize, by taking advantage of the Super Many-to-Many relationship technique!
-
-This idea can be applied recursively for even more complex, *many-to-many-to-...-to-many* relationships (although at some point queries might become slow).
diff --git a/docs/advanced-association-concepts/association-scopes.md b/docs/advanced-association-concepts/association-scopes.md
deleted file mode 100644
index 06a147e3..00000000
--- a/docs/advanced-association-concepts/association-scopes.md
+++ /dev/null
@@ -1,67 +0,0 @@
----
-sidebar_position: 4
-title: Association Scopes
----
-
-This section concerns association scopes, which are similar but not the same as [model scopes](../other-topics/scopes.md).
-
-Association scopes can be placed both on the associated model (the target of the association) and on the through table for Many-to-Many relationships.
-
-## Concept
-
-Similarly to how a [model scope](../other-topics/scopes.md) is automatically applied on the model static calls, such as `Model.scope('foo').findAll()`, an association scope is a rule (more precisely, a set of default attributes and options) that is automatically applied on instance calls from the model. Here, *instance calls* mean method calls that are called from an instance (rather than from the Model itself). Mixins are the main example of instance methods (`instance.getSomething`, `instance.setSomething`, `instance.addSomething` and `instance.createSomething`).
-
-Association scopes behave just like model scopes, in the sense that both cause an automatic application of things like `where` clauses to finder calls; the difference being that instead of applying to static finder calls (which is the case for model scopes), the association scopes automatically apply to instance finder calls (such as mixins).
-
-## Example
-
-A basic example of an association scope for the One-to-Many association between models `Foo` and `Bar` is shown below.
-
-* Setup:
-
- ```js
- const Foo = sequelize.define('foo', { name: DataTypes.STRING });
- const Bar = sequelize.define('bar', { status: DataTypes.STRING });
- Foo.hasMany(Bar, {
- scope: {
- status: 'open'
- },
- as: 'openBars'
- });
- await sequelize.sync();
- const myFoo = await Foo.create({ name: "My Foo" });
- ```
-
-* After this setup, calling `myFoo.getOpenBars()` generates the following SQL:
-
- ```sql
- SELECT
- `id`, `status`, `createdAt`, `updatedAt`, `fooId`
- FROM `bars` AS `bar`
- WHERE `bar`.`status` = 'open' AND `bar`.`fooId` = 1;
- ```
-
-With this we can see that upon calling the `.getOpenBars()` mixin, the association scope `{ status: 'open' }` was automatically applied into the `WHERE` clause of the generated SQL.
-
-## Achieving the same behavior with standard scopes
-
-We could have achieved the same behavior with standard scopes:
-
-```js
-// Foo.hasMany(Bar, {
-// scope: {
-// status: 'open'
-// },
-// as: 'openBars'
-// });
-
-Bar.addScope('open', {
- where: {
- status: 'open'
- }
-});
-Foo.hasMany(Bar);
-Foo.hasMany(Bar.scope('open'), { as: 'openBars' });
-```
-
-With the above code, `myFoo.getOpenBars()` yields the same SQL shown above.
diff --git a/docs/advanced-association-concepts/creating-with-associations.md b/docs/advanced-association-concepts/creating-with-associations.md
deleted file mode 100644
index e152e485..00000000
--- a/docs/advanced-association-concepts/creating-with-associations.md
+++ /dev/null
@@ -1,133 +0,0 @@
----
-sidebar_position: 2
-title: Creating with Associations
----
-
-An instance can be created with nested association in one step, provided all elements are new.
-
-In contrast, performing updates and deletions involving nested objects is currently not possible. For that, you will have to perform each separate action explicitly.
-
-## BelongsTo / HasMany / HasOne association
-
-Consider the following models:
-
-```js
-class Product extends Model {}
-Product.init({
- title: DataTypes.STRING
-}, { sequelize, modelName: 'product' });
-class User extends Model {}
-User.init({
- firstName: DataTypes.STRING,
- lastName: DataTypes.STRING
-}, { sequelize, modelName: 'user' });
-class Address extends Model {}
-Address.init({
- type: DataTypes.STRING,
- line1: DataTypes.STRING,
- line2: DataTypes.STRING,
- city: DataTypes.STRING,
- state: DataTypes.STRING,
- zip: DataTypes.STRING,
-}, { sequelize, modelName: 'address' });
-
-// We save the return values of the association setup calls to use them later
-Product.User = Product.belongsTo(User);
-User.Addresses = User.hasMany(Address);
-// Also works for `hasOne`
-```
-
-A new `Product`, `User`, and one or more `Address` can be created in one step in the following way:
-
-```js
-return Product.create({
- title: 'Chair',
- user: {
- firstName: 'Mick',
- lastName: 'Broadstone',
- addresses: [{
- type: 'home',
- line1: '100 Main St.',
- city: 'Austin',
- state: 'TX',
- zip: '78704'
- }]
- }
-}, {
- include: [{
- association: Product.User,
- include: [ User.Addresses ]
- }]
-});
-```
-
-Observe the usage of the `include` option in the `Product.create` call. That is necessary for Sequelize to understand what you are trying to create along with the association.
-
-Note: here, our user model is called `user`, with a lowercase `u` - This means that the property in the object should also be `user`. If the name given to `sequelize.define` was `User`, the key in the object should also be `User`. Likewise for `addresses`, except it's pluralized being a `hasMany` association.
-
-## BelongsTo association with an alias
-
-The previous example can be extended to support an association alias.
-
-```js
-const Creator = Product.belongsTo(User, { as: 'creator' });
-
-return Product.create({
- title: 'Chair',
- creator: {
- firstName: 'Matt',
- lastName: 'Hansen'
- }
-}, {
- include: [ Creator ]
-});
-```
-
-## HasMany / BelongsToMany association
-
-Let's introduce the ability to associate a product with many tags. Setting up the models could look like:
-
-```js
-class Tag extends Model {}
-Tag.init({
- name: DataTypes.STRING
-}, { sequelize, modelName: 'tag' });
-
-Product.hasMany(Tag);
-// Also works for `belongsToMany`.
-```
-
-Now we can create a product with multiple tags in the following way:
-
-```js
-Product.create({
- id: 1,
- title: 'Chair',
- tags: [
- { name: 'Alpha'},
- { name: 'Beta'}
- ]
-}, {
- include: [ Tag ]
-})
-```
-
-And, we can modify this example to support an alias as well:
-
-```js
-const Categories = Product.hasMany(Tag, { as: 'categories' });
-
-Product.create({
- id: 1,
- title: 'Chair',
- categories: [
- { id: 1, name: 'Alpha' },
- { id: 2, name: 'Beta' }
- ]
-}, {
- include: [{
- association: Categories,
- as: 'categories'
- }]
-})
-```
diff --git a/docs/advanced-association-concepts/eager-loading.md b/docs/advanced-association-concepts/eager-loading.md
deleted file mode 100644
index 0cfabd3a..00000000
--- a/docs/advanced-association-concepts/eager-loading.md
+++ /dev/null
@@ -1,669 +0,0 @@
----
-sidebar_position: 1
-title: Eager Loading
----
-
-As briefly mentioned in [the associations guide](../core-concepts/assocs.md), eager Loading is the act of querying data of several models at once (one 'main' model and one or more associated models). At the SQL level, this is a query with one or more [joins](https://en.wikipedia.org/wiki/Join_\(SQL\)).
-
-When this is done, the associated models will be added by Sequelize in appropriately named, automatically created field(s) in the returned objects.
-
-In Sequelize, eager loading is mainly done by using the `include` option on a model finder query (such as `findOne`, `findAll`, etc).
-
-## Basic example
-
-Let's assume the following setup:
-
-```js
-const User = sequelize.define('user', { name: DataTypes.STRING }, { timestamps: false });
-const Task = sequelize.define('task', { name: DataTypes.STRING }, { timestamps: false });
-const Tool = sequelize.define('tool', {
- name: DataTypes.STRING,
- size: DataTypes.STRING
-}, { timestamps: false });
-User.hasMany(Task);
-Task.belongsTo(User);
-User.hasMany(Tool, { as: 'Instruments' });
-```
-
-### Fetching a single associated element
-
-OK. So, first of all, let's load all tasks with their associated user:
-
-```js
-const tasks = await Task.findAll({ include: User });
-console.log(JSON.stringify(tasks, null, 2));
-```
-
-Output:
-
-```json
-[{
- "name": "A Task",
- "id": 1,
- "userId": 1,
- "user": {
- "name": "John Doe",
- "id": 1
- }
-}]
-```
-
-Here, `tasks[0].user instanceof User` is `true`. This shows that when Sequelize fetches associated models, they are added to the output object as model instances.
-
-Above, the associated model was added to a new field called `user` in the fetched task. The name of this field was automatically chosen by Sequelize based on the name of the associated model, where its pluralized form is used when applicable (i.e., when the association is `hasMany` or `belongsToMany`). In other words, since `Task.belongsTo(User)`, a task is associated to one user, therefore the logical choice is the singular form (which Sequelize follows automatically).
-
-### Fetching all associated elements
-
-Now, instead of loading the user that is associated to a given task, we will do the opposite - we will find all tasks associated to a given user.
-
-The method call is essentially the same. The only difference is that now the extra field created in the query result uses the pluralized form (`tasks` in this case), and its value is an array of task instances (instead of a single instance, as above).
-
-```js
-const users = await User.findAll({ include: Task });
-console.log(JSON.stringify(users, null, 2));
-```
-
-Output:
-
-```json
-[{
- "name": "John Doe",
- "id": 1,
- "tasks": [{
- "name": "A Task",
- "id": 1,
- "userId": 1
- }]
-}]
-```
-
-Notice that the accessor (the `tasks` property in the resulting instance) is pluralized since the association is one-to-many.
-
-### Fetching an Aliased association
-
-If an association is aliased (using the `as` option), you must specify this alias when including the model. Instead of passing the model directly to the `include` option, you should instead provide an object with two options: `model` and `as`.
-
-Notice how the user's `Tool`s are aliased as `Instruments` above. In order to get that right you have to specify the model you want to load, as well as the alias:
-
-```js
-const users = await User.findAll({
- include: { model: Tool, as: 'Instruments' }
-});
-console.log(JSON.stringify(users, null, 2));
-```
-
-Output:
-
-```json
-[{
- "name": "John Doe",
- "id": 1,
- "Instruments": [{
- "name": "Scissor",
- "id": 1,
- "userId": 1
- }]
-}]
-```
-
-You can also include by alias name by specifying a string that matches the association alias:
-
-```js
-User.findAll({ include: 'Instruments' }); // Also works
-User.findAll({ include: { association: 'Instruments' } }); // Also works
-```
-
-### Required eager loading
-
-When eager loading, we can force the query to return only records which have an associated model, effectively converting the query from the default `OUTER JOIN` to an `INNER JOIN`. This is done with the `required: true` option, as follows:
-
-```js
-User.findAll({
- include: {
- model: Task,
- required: true
- }
-});
-```
-
-This option also works on nested includes.
-
-### Eager loading filtered at the associated model level
-
-When eager loading, we can also filter the associated model using the `where` option, as in the following example:
-
-```js
-User.findAll({
- include: {
- model: Tool,
- as: 'Instruments',
- where: {
- size: {
- [Op.ne]: 'small'
- }
- }
- }
-});
-```
-
-Generated SQL:
-
-```sql
-SELECT
- `user`.`id`,
- `user`.`name`,
- `Instruments`.`id` AS `Instruments.id`,
- `Instruments`.`name` AS `Instruments.name`,
- `Instruments`.`size` AS `Instruments.size`,
- `Instruments`.`userId` AS `Instruments.userId`
-FROM `users` AS `user`
-INNER JOIN `tools` AS `Instruments` ON
- `user`.`id` = `Instruments`.`userId` AND
- `Instruments`.`size` != 'small';
-```
-
-Note that the SQL query generated above will only fetch users that have at least one tool that matches the condition (of not being `small`, in this case). This is the case because, when the `where` option is used inside an `include`, Sequelize automatically sets the `required` option to `true`. This means that, instead of an `OUTER JOIN`, an `INNER JOIN` is done, returning only the parent models with at least one matching children.
-
-Note also that the `where` option used was converted into a condition for the `ON` clause of the `INNER JOIN`. In order to obtain a *top-level* `WHERE` clause, instead of an `ON` clause, something different must be done. This will be shown next.
-
-#### Referring to other columns
-
-If you want to apply a `WHERE` clause in an included model referring to a value from an associated model, you can simply use the `Sequelize.col` function, as show in the example below:
-
-```js
-// Find all projects with a least one task where task.state === project.state
-Project.findAll({
- include: {
- model: Task,
- where: {
- state: Sequelize.col('project.state')
- }
- }
-})
-```
-
-### Complex where clauses at the top-level
-
-To obtain top-level `WHERE` clauses that involve nested columns, Sequelize provides a way to reference nested columns: the `'$nested.column$'` syntax.
-
-It can be used, for example, to move the where conditions from an included model from the `ON` condition to a top-level `WHERE` clause.
-
-```js
-User.findAll({
- where: {
- '$Instruments.size$': { [Op.ne]: 'small' }
- },
- include: [{
- model: Tool,
- as: 'Instruments'
- }]
-});
-```
-
-Generated SQL:
-
-```sql
-SELECT
- `user`.`id`,
- `user`.`name`,
- `Instruments`.`id` AS `Instruments.id`,
- `Instruments`.`name` AS `Instruments.name`,
- `Instruments`.`size` AS `Instruments.size`,
- `Instruments`.`userId` AS `Instruments.userId`
-FROM `users` AS `user`
-LEFT OUTER JOIN `tools` AS `Instruments` ON
- `user`.`id` = `Instruments`.`userId`
-WHERE `Instruments`.`size` != 'small';
-```
-
-The `$nested.column$` syntax also works for columns that are nested several levels deep, such as `$some.super.deeply.nested.column$`. Therefore, you can use this to make complex filters on deeply nested columns.
-
-For a better understanding of all differences between the inner `where` option (used inside an `include`), with and without the `required` option, and a top-level `where` using the `$nested.column$` syntax, below we have four examples for you:
-
-```js
-// Inner where, with default `required: true`
-await User.findAll({
- include: {
- model: Tool,
- as: 'Instruments',
- where: {
- size: { [Op.ne]: 'small' }
- }
- }
-});
-
-// Inner where, `required: false`
-await User.findAll({
- include: {
- model: Tool,
- as: 'Instruments',
- where: {
- size: { [Op.ne]: 'small' }
- },
- required: false
- }
-});
-
-// Top-level where, with default `required: false`
-await User.findAll({
- where: {
- '$Instruments.size$': { [Op.ne]: 'small' }
- },
- include: {
- model: Tool,
- as: 'Instruments'
- }
-});
-
-// Top-level where, `required: true`
-await User.findAll({
- where: {
- '$Instruments.size$': { [Op.ne]: 'small' }
- },
- include: {
- model: Tool,
- as: 'Instruments',
- required: true
- }
-});
-```
-
-Generated SQLs, in order:
-
-```sql
--- Inner where, with default `required: true`
-SELECT [...] FROM `users` AS `user`
-INNER JOIN `tools` AS `Instruments` ON
- `user`.`id` = `Instruments`.`userId`
- AND `Instruments`.`size` != 'small';
-
--- Inner where, `required: false`
-SELECT [...] FROM `users` AS `user`
-LEFT OUTER JOIN `tools` AS `Instruments` ON
- `user`.`id` = `Instruments`.`userId`
- AND `Instruments`.`size` != 'small';
-
--- Top-level where, with default `required: false`
-SELECT [...] FROM `users` AS `user`
-LEFT OUTER JOIN `tools` AS `Instruments` ON
- `user`.`id` = `Instruments`.`userId`
-WHERE `Instruments`.`size` != 'small';
-
--- Top-level where, `required: true`
-SELECT [...] FROM `users` AS `user`
-INNER JOIN `tools` AS `Instruments` ON
- `user`.`id` = `Instruments`.`userId`
-WHERE `Instruments`.`size` != 'small';
-```
-
-### Fetching with `RIGHT OUTER JOIN` (MySQL, MariaDB, PostgreSQL and MSSQL only)
-
-By default, associations are loaded using a `LEFT OUTER JOIN` - that is to say it only includes records from the parent table. You can change this behavior to a `RIGHT OUTER JOIN` by passing the `right` option, if the dialect you are using supports it.
-
-Currently, SQLite does not support [right joins](https://www.sqlite.org/omitted.html).
-
-*Note:* `right` is only respected if `required` is false.
-
-```js
-User.findAll({
- include: [{
- model: Task // will create a left join
- }]
-});
-User.findAll({
- include: [{
- model: Task,
- right: true // will create a right join
- }]
-});
-User.findAll({
- include: [{
- model: Task,
- required: true,
- right: true // has no effect, will create an inner join
- }]
-});
-User.findAll({
- include: [{
- model: Task,
- where: { name: { [Op.ne]: 'empty trash' } },
- right: true // has no effect, will create an inner join
- }]
-});
-User.findAll({
- include: [{
- model: Tool,
- where: { name: { [Op.ne]: 'empty trash' } },
- required: false // will create a left join
- }]
-});
-User.findAll({
- include: [{
- model: Tool,
- where: { name: { [Op.ne]: 'empty trash' } },
- required: false
- right: true // will create a right join
- }]
-});
-```
-
-## Multiple eager loading
-
-The `include` option can receive an array in order to fetch multiple associated models at once:
-
-```js
-Foo.findAll({
- include: [
- {
- model: Bar,
- required: true
- },
- {
- model: Baz,
- where: /* ... */
- },
- Qux // Shorthand syntax for { model: Qux } also works here
- ]
-})
-```
-
-## Eager loading with Many-to-Many relationships
-
-When you perform eager loading on a model with a Belongs-to-Many relationship, Sequelize will fetch the junction table data as well, by default. For example:
-
-```js
-const Foo = sequelize.define('Foo', { name: DataTypes.TEXT });
-const Bar = sequelize.define('Bar', { name: DataTypes.TEXT });
-Foo.belongsToMany(Bar, { through: 'Foo_Bar' });
-Bar.belongsToMany(Foo, { through: 'Foo_Bar' });
-
-await sequelize.sync();
-const foo = await Foo.create({ name: 'foo' });
-const bar = await Bar.create({ name: 'bar' });
-await foo.addBar(bar);
-const fetchedFoo = await Foo.findOne({ include: Bar });
-console.log(JSON.stringify(fetchedFoo, null, 2));
-```
-
-Output:
-
-```json
-{
- "id": 1,
- "name": "foo",
- "Bars": [
- {
- "id": 1,
- "name": "bar",
- "Foo_Bar": {
- "FooId": 1,
- "BarId": 1
- }
- }
- ]
-}
-```
-
-Note that every bar instance eager loaded into the `"Bars"` property has an extra property called `Foo_Bar` which is the relevant Sequelize instance of the junction model. By default, Sequelize fetches all attributes from the junction table in order to build this extra property.
-
-However, you can specify which attributes you want fetched. This is done with the `attributes` option applied inside the `through` option of the include. For example:
-
-```js
-Foo.findAll({
- include: [{
- model: Bar,
- through: {
- attributes: [/* list the wanted attributes here */]
- }
- }]
-});
-```
-
-If you don't want anything from the junction table, you can explicitly provide an empty array to the `attributes` option inside the `through` option of the `include` option, and in this case nothing will be fetched and the extra property will not even be created:
-
-```js
-Foo.findOne({
- include: {
- model: Bar,
- through: {
- attributes: []
- }
- }
-});
-```
-
-Output:
-
-```json
-{
- "id": 1,
- "name": "foo",
- "Bars": [
- {
- "id": 1,
- "name": "bar"
- }
- ]
-}
-```
-
-Whenever including a model from a Many-to-Many relationship, you can also apply a filter on the junction table. This is done with the `where` option applied inside the `through` option of the include. For example:
-
-```js
-User.findAll({
- include: [{
- model: Project,
- through: {
- where: {
- // Here, `completed` is a column present at the junction table
- completed: true
- }
- }
- }]
-});
-```
-
-Generated SQL (using SQLite):
-
-```sql
-SELECT
- `User`.`id`,
- `User`.`name`,
- `Projects`.`id` AS `Projects.id`,
- `Projects`.`name` AS `Projects.name`,
- `Projects->User_Project`.`completed` AS `Projects.User_Project.completed`,
- `Projects->User_Project`.`UserId` AS `Projects.User_Project.UserId`,
- `Projects->User_Project`.`ProjectId` AS `Projects.User_Project.ProjectId`
-FROM `Users` AS `User`
-LEFT OUTER JOIN `User_Projects` AS `Projects->User_Project` ON
- `User`.`id` = `Projects->User_Project`.`UserId`
-LEFT OUTER JOIN `Projects` AS `Projects` ON
- `Projects`.`id` = `Projects->User_Project`.`ProjectId` AND
- `Projects->User_Project`.`completed` = 1;
-```
-
-## Including everything
-
-To include all associated models, you can use the `all` and `nested` options:
-
-```js
-// Fetch all models associated with User
-User.findAll({ include: { all: true }});
-
-// Fetch all models associated with User and their nested associations (recursively)
-User.findAll({ include: { all: true, nested: true }});
-```
-
-## Including soft deleted records
-
-In case you want to eager load soft deleted records you can do that by setting `include.paranoid` to `false`:
-
-```js
-User.findAll({
- include: [{
- model: Tool,
- as: 'Instruments',
- where: { size: { [Op.ne]: 'small' } },
- paranoid: false
- }]
-});
-```
-
-## Ordering eager loaded associations
-
-When you want to apply `ORDER` clauses to eager loaded models, you must use the top-level `order` option with augmented arrays, starting with the specification of the nested model you want to sort.
-
-This is better understood with examples.
-
-```js
-Company.findAll({
- include: Division,
- order: [
- // We start the order array with the model we want to sort
- [Division, 'name', 'ASC']
- ]
-});
-Company.findAll({
- include: Division,
- order: [
- [Division, 'name', 'DESC']
- ]
-});
-Company.findAll({
- // If the include uses an alias...
- include: { model: Division, as: 'Div' },
- order: [
- // ...we use the same syntax from the include
- // in the beginning of the order array
- [{ model: Division, as: 'Div' }, 'name', 'DESC']
- ]
-});
-
-Company.findAll({
- // If we have includes nested in several levels...
- include: {
- model: Division,
- include: Department
- },
- order: [
- // ... we replicate the include chain of interest
- // at the beginning of the order array
- [Division, Department, 'name', 'DESC']
- ]
-});
-```
-
-In the case of many-to-many relationships, you are also able to sort by attributes in the through table. For example, assuming we have a Many-to-Many relationship between `Division` and `Department` whose junction model is `DepartmentDivision`, you can do:
-
-```js
-Company.findAll({
- include: {
- model: Division,
- include: Department
- },
- order: [
- [Division, DepartmentDivision, 'name', 'ASC']
- ]
-});
-```
-
-In all the above examples, you have noticed that the `order` option is used at the top-level. The only situation in which `order` also works inside the include option is when `separate: true` is used. In that case, the usage is as follows:
-
-```js
-// This only works for `separate: true` (which in turn
-// only works for has-many relationships).
-User.findAll({
- include: {
- model: Post,
- separate: true,
- order: [
- ['createdAt', 'DESC']
- ]
- }
-});
-```
-
-### Complex ordering involving sub-queries
-
-Take a look at the [guide on sub-queries](../other-topics/sub-queries.md) for an example of how to use a sub-query to assist a more complex ordering.
-
-## Nested eager loading
-
-You can use nested eager loading to load all related models of a related model:
-
-```js
-const users = await User.findAll({
- include: {
- model: Tool,
- as: 'Instruments',
- include: {
- model: Teacher,
- include: [ /* etc */ ]
- }
- }
-});
-console.log(JSON.stringify(users, null, 2));
-```
-
-Output:
-
-```json
-[{
- "name": "John Doe",
- "id": 1,
- "Instruments": [{ // 1:M and N:M association
- "name": "Scissor",
- "id": 1,
- "userId": 1,
- "Teacher": { // 1:1 association
- "name": "Jimi Hendrix"
- }
- }]
-}]
-```
-
-This will produce an outer join. However, a `where` clause on a related model will create an inner join and return only the instances that have matching sub-models. To return all parent instances, you should add `required: false`.
-
-```js
-User.findAll({
- include: [{
- model: Tool,
- as: 'Instruments',
- include: [{
- model: Teacher,
- where: {
- school: "Woodstock Music School"
- },
- required: false
- }]
- }]
-});
-```
-
-The query above will return all users, and all their instruments, but only those teachers associated with `Woodstock Music School`.
-
-## Using `findAndCountAll` with includes
-
-The `findAndCountAll` utility function supports includes. Only the includes that are marked as `required` will be considered in `count`. For example, if you want to find and count all users who have a profile:
-
-```js
-User.findAndCountAll({
- include: [
- { model: Profile, required: true }
- ],
- limit: 3
-});
-```
-
-Because the include for `Profile` has `required` set it will result in an inner join, and only the users who have a profile will be counted. If we remove `required` from the include, both users with and without profiles will be counted. Adding a `where` clause to the include automatically makes it required:
-
-```js
-User.findAndCountAll({
- include: [
- { model: Profile, where: { active: true } }
- ],
- limit: 3
-});
-```
-
-The query above will only count users who have an active profile, because `required` is implicitly set to true when you add a where clause to the include.
diff --git a/docs/advanced-association-concepts/polymorphic-associations.md b/docs/advanced-association-concepts/polymorphic-associations.md
deleted file mode 100644
index 4ae9855c..00000000
--- a/docs/advanced-association-concepts/polymorphic-associations.md
+++ /dev/null
@@ -1,430 +0,0 @@
----
-sidebar_position: 5
-title: Polymorphic Associations
----
-
-_**Note:** the usage of polymorphic associations in Sequelize, as outlined in this guide, should be done with caution. Don't just copy-paste code from here, otherwise you might easily make mistakes and introduce bugs in your code. Make sure you understand what is going on._
-
-## Concept
-
-A **polymorphic association** consists on two (or more) associations happening with the same foreign key.
-
-For example, consider the models `Image`, `Video` and `Comment`. The first two represent something that a user might post. We want to allow comments to be placed in both of them. This way, we immediately think of establishing the following associations:
-
-* A One-to-Many association between `Image` and `Comment`:
-
- ```js
- Image.hasMany(Comment);
- Comment.belongsTo(Image);
- ```
-
-* A One-to-Many association between `Video` and `Comment`:
-
- ```js
- Video.hasMany(Comment);
- Comment.belongsTo(Video);
- ```
-
-However, the above would cause Sequelize to create two foreign keys on the `Comment` table: `ImageId` and `VideoId`. This is not ideal because this structure makes it look like a comment can be attached at the same time to one image and one video, which isn't true. Instead, what we really want here is precisely a polymorphic association, in which a `Comment` points to a single **Commentable**, an abstract polymorphic entity that represents one of `Image` or `Video`.
-
-Before proceeding to how to configure such an association, let's see how using it looks like:
-
-```js
-const image = await Image.create({ url: "https://placekitten.com/408/287" });
-const comment = await image.createComment({ content: "Awesome!" });
-
-console.log(comment.commentableId === image.id); // true
-
-// We can also retrieve which type of commentable a comment is associated to.
-// The following prints the model name of the associated commentable instance.
-console.log(comment.commentableType); // "Image"
-
-// We can use a polymorphic method to retrieve the associated commentable, without
-// having to worry whether it's an Image or a Video.
-const associatedCommentable = await comment.getCommentable();
-
-// In this example, `associatedCommentable` is the same thing as `image`:
-const isDeepEqual = require('deep-equal');
-console.log(isDeepEqual(image, commentable)); // true
-```
-
-## Configuring a One-to-Many polymorphic association
-
-To setup the polymorphic association for the example above (which is an example of One-to-Many polymorphic association), we have the following steps:
-
-* Define a string field called `commentableType` in the `Comment` model;
-* Define the `hasMany` and `belongsTo` association between `Image`/`Video` and `Comment`:
- * Disabling constraints (i.e. using `{ constraints: false }`), since the same foreign key is referencing multiple tables;
- * Specifying the appropriate [association scopes](./association-scopes.md);
-* To properly support lazy loading, define a new instance method on the `Comment` model called `getCommentable` which calls, under the hood, the correct mixin to fetch the appropriate commentable;
-* To properly support eager loading, define an `afterFind` hook on the `Comment` model that automatically populates the `commentable` field in every instance;
-* To prevent bugs/mistakes in eager loading, you can also delete the concrete fields `image` and `video` from Comment instances in the same `afterFind` hook, leaving only the abstract `commentable` field available.
-
-Here is an example:
-
-```js
-// Helper function
-const uppercaseFirst = str => `${str[0].toUpperCase()}${str.substr(1)}`;
-
-class Image extends Model {}
-Image.init({
- title: DataTypes.STRING,
- url: DataTypes.STRING
-}, { sequelize, modelName: 'image' });
-
-class Video extends Model {}
-Video.init({
- title: DataTypes.STRING,
- text: DataTypes.STRING
-}, { sequelize, modelName: 'video' });
-
-class Comment extends Model {
- getCommentable(options) {
- if (!this.commentableType) return Promise.resolve(null);
- const mixinMethodName = `get${uppercaseFirst(this.commentableType)}`;
- return this[mixinMethodName](options);
- }
-}
-Comment.init({
- title: DataTypes.STRING,
- commentableId: DataTypes.INTEGER,
- commentableType: DataTypes.STRING
-}, { sequelize, modelName: 'comment' });
-
-Image.hasMany(Comment, {
- foreignKey: 'commentableId',
- constraints: false,
- scope: {
- commentableType: 'image'
- }
-});
-Comment.belongsTo(Image, { foreignKey: 'commentableId', constraints: false });
-
-Video.hasMany(Comment, {
- foreignKey: 'commentableId',
- constraints: false,
- scope: {
- commentableType: 'video'
- }
-});
-Comment.belongsTo(Video, { foreignKey: 'commentableId', constraints: false });
-
-Comment.hooks.addListener("afterFind", findResult => {
- if (!Array.isArray(findResult)) findResult = [findResult];
- for (const instance of findResult) {
- if (instance.commentableType === "image" && instance.image !== undefined) {
- instance.commentable = instance.image;
- } else if (instance.commentableType === "video" && instance.video !== undefined) {
- instance.commentable = instance.video;
- }
- // To prevent mistakes:
- delete instance.image;
- delete instance.dataValues.image;
- delete instance.video;
- delete instance.dataValues.video;
- }
-});
-```
-
-Since the `commentableId` column references several tables (two in this case), we cannot add a `REFERENCES` constraint to it. This is why the `constraints: false` option was used.
-
-Note that, in the code above:
-
-* The *Image -> Comment* association defined an association scope: `{ commentableType: 'image' }`
-* The *Video -> Comment* association defined an association scope: `{ commentableType: 'video' }`
-
-These scopes are automatically applied when using the association functions (as explained in the [Association Scopes](./association-scopes.md) guide). Some examples are below, with their generated SQL statements:
-
-* `image.getComments()`:
-
- ```sql
- SELECT "id", "title", "commentableType", "commentableId", "createdAt", "updatedAt"
- FROM "comments" AS "comment"
- WHERE "comment"."commentableType" = 'image' AND "comment"."commentableId" = 1;
- ```
-
- Here we can see that `` `comment`.`commentableType` = 'image'`` was automatically added to the `WHERE` clause of the generated SQL. This is exactly the behavior we want.
-
-* `image.createComment({ title: 'Awesome!' })`:
-
- ```sql
- INSERT INTO "comments" (
- "id", "title", "commentableType", "commentableId", "createdAt", "updatedAt"
- ) VALUES (
- DEFAULT, 'Awesome!', 'image', 1,
- '2018-04-17 05:36:40.454 +00:00', '2018-04-17 05:36:40.454 +00:00'
- ) RETURNING *;
- ```
-
-* `image.addComment(comment)`:
-
- ```sql
- UPDATE "comments"
- SET "commentableId"=1, "commentableType"='image', "updatedAt"='2018-04-17 05:38:43.948 +00:00'
- WHERE "id" IN (1)
- ```
-
-### Polymorphic lazy loading
-
-The `getCommentable` instance method on `Comment` provides an abstraction for lazy loading the associated commentable - working whether the comment belongs to an Image or a Video.
-
-It works by simply converting the `commentableType` string into a call to the correct mixin (either `getImage` or `getVideo`).
-
-Note that the `getCommentable` implementation above:
-
-* Returns `null` when no association is present (which is good);
-* Allows you to pass an options object to `getCommentable(options)`, just like any other standard Sequelize method. This is useful to specify where-conditions or includes, for example.
-
-### Polymorphic eager loading
-
-Now, we want to perform a polymorphic eager loading of the associated commentables for one (or more) comments. We want to achieve something similar to the following idea:
-
-```js
-const comment = await Comment.findOne({
- include: [ /* What to put here? */ ]
-});
-console.log(comment.commentable); // This is our goal
-```
-
-The solution is to tell Sequelize to include both Images and Videos, so that our `afterFind` hook defined above will do the work, automatically adding the `commentable` field to the instance object, providing the abstraction we want.
-
-For example:
-
-```js
-const comments = await Comment.findAll({
- include: [Image, Video]
-});
-for (const comment of comments) {
- const message = `Found comment #${comment.id} with ${comment.commentableType} commentable:`;
- console.log(message, comment.commentable.toJSON());
-}
-```
-
-Output example:
-
-```text
-Found comment #1 with image commentable: { id: 1,
- title: 'Meow',
- url: 'https://placekitten.com/408/287',
- createdAt: 2019-12-26T15:04:53.047Z,
- updatedAt: 2019-12-26T15:04:53.047Z }
-```
-
-### Caution - possibly invalid eager/lazy loading!
-
-Consider a comment `Foo` whose `commentableId` is 2 and `commentableType` is `image`. Consider also that `Image A` and `Video X` both happen to have an id equal to 2. Conceptually, it is clear that `Video X` is not associated to `Foo`, because even though its id is 2, the `commentableType` of `Foo` is `image`, not `video`. However, this distinction is made by Sequelize only at the level of the abstractions performed by `getCommentable` and the hook we created above.
-
-This means that if you call `Comment.findAll({ include: Video })` in the situation above, `Video X` will be eager loaded into `Foo`. Thankfully, our `afterFind` hook will delete it automatically, to help prevent bugs, but regardless it is important that you understand what is going on.
-
-The best way to prevent this kind of mistake is to **avoid using the concrete accessors and mixins directly at all costs** (such as `.image`, `.getVideo()`, `.setImage()`, etc), always preferring the abstractions we created, such as `.getCommentable()` and `.commentable`. If you really need to access eager-loaded `.image` and `.video` for some reason, make sure you wrap that in a type check such as `comment.commentableType === 'image'`.
-
-## Configuring a Many-to-Many polymorphic association
-
-In the above example, we had the models `Image` and `Video` being abstractly called *commentables*, with one *commentable* having many comments. However, one given comment would belong to a single *commentable* - this is why the whole situation is a One-to-Many polymorphic association.
-
-Now, to consider a Many-to-Many polymorphic association, instead of considering comments, we will consider tags. For convenience, instead of calling Image and Video as *commentables*, we will now call them *taggables*. One *taggable* may have several tags, and at the same time one tag can be placed in several *taggables*.
-
-The setup for this goes as follows:
-
-* Define the junction model explicitly, specifying the two foreign keys as `tagId` and `taggableId` (this way it is a junction model for a Many-to-Many relationship between `Tag` and the abstract concept of *taggable*);
-* Define a string field called `taggableType` in the junction model;
-* Define the `belongsToMany` associations between the two models and `Tag`:
- * Disabling constraints (i.e. using `{ constraints: false }`), since the same foreign key is referencing multiple tables;
- * Specifying the appropriate [association scopes](./association-scopes.md);
-* Define a new instance method on the `Tag` model called `getTaggables` which calls, under the hood, the correct mixin to fetch the appropriate taggables.
-
-Implementation:
-
-```js
-class Tag extends Model {
- getTaggables(options) {
- const images = await this.getImages(options);
- const videos = await this.getVideos(options);
- // Concat images and videos in a single array of taggables
- return images.concat(videos);
- }
-}
-Tag.init({
- name: DataTypes.STRING
-}, { sequelize, modelName: 'tag' });
-
-// Here we define the junction model explicitly
-class Tag_Taggable extends Model {}
-Tag_Taggable.init({
- tagId: {
- type: DataTypes.INTEGER,
- unique: 'tt_unique_constraint'
- },
- taggableId: {
- type: DataTypes.INTEGER,
- unique: 'tt_unique_constraint',
- references: null
- },
- taggableType: {
- type: DataTypes.STRING,
- unique: 'tt_unique_constraint'
- }
-}, { sequelize, modelName: 'tag_taggable' });
-
-Image.belongsToMany(Tag, {
- through: {
- model: Tag_Taggable,
- unique: false,
- scope: {
- taggableType: 'image'
- }
- },
- foreignKey: 'taggableId',
- constraints: false
-});
-Tag.belongsToMany(Image, {
- through: {
- model: Tag_Taggable,
- unique: false
- },
- foreignKey: 'tagId',
- constraints: false
-});
-
-Video.belongsToMany(Tag, {
- through: {
- model: Tag_Taggable,
- unique: false,
- scope: {
- taggableType: 'video'
- }
- },
- foreignKey: 'taggableId',
- constraints: false
-});
-Tag.belongsToMany(Video, {
- through: {
- model: Tag_Taggable,
- unique: false
- },
- foreignKey: 'tagId',
- constraints: false
-});
-```
-
-The `constraints: false` option disables references constraints, as the `taggableId` column references several tables, we cannot add a `REFERENCES` constraint to it.
-
-Note that:
-
-* The *Image -> Tag* association defined an association scope: `{ taggableType: 'image' }`
-* The *Video -> Tag* association defined an association scope: `{ taggableType: 'video' }`
-
-These scopes are automatically applied when using the association functions. Some examples are below, with their generated SQL statements:
-
-* `image.getTags()`:
-
- ```sql
- SELECT
- `tag`.`id`,
- `tag`.`name`,
- `tag`.`createdAt`,
- `tag`.`updatedAt`,
- `tag_taggable`.`tagId` AS `tag_taggable.tagId`,
- `tag_taggable`.`taggableId` AS `tag_taggable.taggableId`,
- `tag_taggable`.`taggableType` AS `tag_taggable.taggableType`,
- `tag_taggable`.`createdAt` AS `tag_taggable.createdAt`,
- `tag_taggable`.`updatedAt` AS `tag_taggable.updatedAt`
- FROM `tags` AS `tag`
- INNER JOIN `tag_taggables` AS `tag_taggable` ON
- `tag`.`id` = `tag_taggable`.`tagId` AND
- `tag_taggable`.`taggableId` = 1 AND
- `tag_taggable`.`taggableType` = 'image';
- ```
-
- Here we can see that `` `tag_taggable`.`taggableType` = 'image'`` was automatically added to the `WHERE` clause of the generated SQL. This is exactly the behavior we want.
-
-* `tag.getTaggables()`:
-
- ```sql
- SELECT
- `image`.`id`,
- `image`.`url`,
- `image`.`createdAt`,
- `image`.`updatedAt`,
- `tag_taggable`.`tagId` AS `tag_taggable.tagId`,
- `tag_taggable`.`taggableId` AS `tag_taggable.taggableId`,
- `tag_taggable`.`taggableType` AS `tag_taggable.taggableType`,
- `tag_taggable`.`createdAt` AS `tag_taggable.createdAt`,
- `tag_taggable`.`updatedAt` AS `tag_taggable.updatedAt`
- FROM `images` AS `image`
- INNER JOIN `tag_taggables` AS `tag_taggable` ON
- `image`.`id` = `tag_taggable`.`taggableId` AND
- `tag_taggable`.`tagId` = 1;
-
- SELECT
- `video`.`id`,
- `video`.`url`,
- `video`.`createdAt`,
- `video`.`updatedAt`,
- `tag_taggable`.`tagId` AS `tag_taggable.tagId`,
- `tag_taggable`.`taggableId` AS `tag_taggable.taggableId`,
- `tag_taggable`.`taggableType` AS `tag_taggable.taggableType`,
- `tag_taggable`.`createdAt` AS `tag_taggable.createdAt`,
- `tag_taggable`.`updatedAt` AS `tag_taggable.updatedAt`
- FROM `videos` AS `video`
- INNER JOIN `tag_taggables` AS `tag_taggable` ON
- `video`.`id` = `tag_taggable`.`taggableId` AND
- `tag_taggable`.`tagId` = 1;
- ```
-
-Note that the above implementation of `getTaggables()` allows you to pass an options object to `getCommentable(options)`, just like any other standard Sequelize method. This is useful to specify where-conditions or includes, for example.
-
-### Applying scopes on the target model
-
-In the example above, the `scope` options (such as `scope: { taggableType: 'image' }`) were applied to the *through* model, not the *target* model, since it was used under the `through` option.
-
-We can also apply an association scope on the target model. We can even do both at the same time.
-
-To illustrate this, consider an extension of the above example between tags and taggables, where each tag has a status. This way, to get all pending tags of an image, we could establish another `belognsToMany` relationship between `Image` and `Tag`, this time applying a scope on the through model and another scope on the target model:
-
-```js
-Image.belongsToMany(Tag, {
- through: {
- model: Tag_Taggable,
- unique: false,
- scope: {
- taggableType: 'image'
- }
- },
- scope: {
- status: 'pending'
- },
- as: 'pendingTags',
- foreignKey: 'taggableId',
- constraints: false
-});
-```
-
-This way, when calling `image.getPendingTags()`, the following SQL query will be generated:
-
-```sql
-SELECT
- `tag`.`id`,
- `tag`.`name`,
- `tag`.`status`,
- `tag`.`createdAt`,
- `tag`.`updatedAt`,
- `tag_taggable`.`tagId` AS `tag_taggable.tagId`,
- `tag_taggable`.`taggableId` AS `tag_taggable.taggableId`,
- `tag_taggable`.`taggableType` AS `tag_taggable.taggableType`,
- `tag_taggable`.`createdAt` AS `tag_taggable.createdAt`,
- `tag_taggable`.`updatedAt` AS `tag_taggable.updatedAt`
-FROM `tags` AS `tag`
-INNER JOIN `tag_taggables` AS `tag_taggable` ON
- `tag`.`id` = `tag_taggable`.`tagId` AND
- `tag_taggable`.`taggableId` = 1 AND
- `tag_taggable`.`taggableType` = 'image'
-WHERE (
- `tag`.`status` = 'pending'
-);
-```
-
-We can see that both scopes were applied automatically:
-
-* `` `tag_taggable`.`taggableType` = 'image'`` was added automatically to the `INNER JOIN`;
-* `` `tag`.`status` = 'pending'`` was added automatically to an outer where clause.
diff --git a/docs/core-concepts/_category_.json b/docs/associations/_category_.json
similarity index 67%
rename from docs/core-concepts/_category_.json
rename to docs/associations/_category_.json
index aec74de9..db1f1d5b 100644
--- a/docs/core-concepts/_category_.json
+++ b/docs/associations/_category_.json
@@ -1,6 +1,6 @@
{
- "position": 3,
- "label": "Core Concepts",
+ "position": 6,
+ "label": "Associations",
"collapsible": true,
"collapsed": false,
"link": {
diff --git a/docs/associations/association-scopes.md b/docs/associations/association-scopes.md
new file mode 100644
index 00000000..9e28790e
--- /dev/null
+++ b/docs/associations/association-scopes.md
@@ -0,0 +1,128 @@
+---
+title: Association Scopes
+---
+
+:::info
+
+This section concerns association scopes, not to be confused with [model scopes](../other-topics/scopes.md).
+
+:::
+
+Association scopes are a way to automatically apply default filters on associated models.
+
+For instance, you could define an association from `City` to `Restaurant` with a scope that only returns restaurants that are open:
+
+```js
+class City extends Model {
+ @Attribute(DataTypes.STRING)
+ name;
+
+ /** this association returns all restaurants */
+ @HasMany(() => Restaurant, 'cityId')
+ restaurants;
+
+ /** this association only returns open restaurants */
+ @HasMany(() => Restaurant, {
+ foreignKey: 'cityId',
+ // highlight-next-line
+ scope: { status: 'open' },
+ })
+ openRestaurants;
+}
+
+class Restaurant extends Model {
+ @Attribute(DataTypes.STRING)
+ status;
+}
+
+const city = await City.findByPk(1);
+
+// this will return all restaurants
+const restaurants = await city.getRestaurants();
+
+// this will return only open restaurants
+const openRestaurants = await city.getOpenRestaurants();
+```
+
+This last query would roughly generate the following SQL:
+
+```sql
+SELECT * FROM `restaurants` WHERE `restaurants`.`status` = 'open' AND `restaurants`.`cityId` = 1;
+```
+
+## BelongsToMany scope
+
+All associations support specifying a scope to filter the target model, but the `BelongsToMany` association
+also supports specifying a scope to filter the join table. This is useful when you want to filter based on extra information
+stored in the join table.
+
+It is done by setting the `through.scope` option.
+
+Here is a simple example. We want to store which person worked on a game, but we also want to store the role they had in its creation:
+
+```js
+class GameAuthor extends Model {
+ @Attribute(DataTypes.STRING)
+ role;
+}
+
+class Person extends Model {}
+
+class Game extends Model {
+ /** This association will list everyone that worked on the game */
+ @BelongsToMany(() => Person, {
+ through: GameAuthor
+ })
+ allAuthors;
+}
+```
+
+In the above example, we can use the `allAuthors` association to list everyone that worked on the game, but we can
+also add other associations to filter the authors based on their role:
+
+```js
+class Game extends Model {
+ /** This association will list everyone that worked on the game */
+ @BelongsToMany(() => Person, {
+ through: GameAuthor,
+ foreignKey: 'gameId',
+ otherKey: 'personId',
+ })
+ allAuthors;
+
+ /** This association will list everyone that worked on the game as a programmer */
+ @BelongsToMany(() => Person, {
+ through: {
+ model: GameAuthor,
+ foreignKey: 'gameId',
+ otherKey: 'personId',
+ // highlight-next-line
+ scope: { role: 'programmer' },
+ },
+ })
+ programmers;
+
+ /** This association will list everyone that worked on the game as a designer */
+ @BelongsToMany(() => Person, {
+ through: {
+ model: GameAuthor,
+ foreignKey: 'gameId',
+ otherKey: 'personId',
+ // highlight-next-line
+ scope: { role: 'designer' },
+ },
+ })
+ designers;
+}
+
+const game = await Game.findByPk(1);
+
+// this will return all authors
+const allAuthors = await game.getAllAuthors();
+
+// this will return only programmers
+const programmers = await game.getProgrammers();
+
+// this will return only designers
+const designers = await game.getDesigners();
+```
diff --git a/docs/associations/basics.md b/docs/associations/basics.md
new file mode 100644
index 00000000..aee3872e
--- /dev/null
+++ b/docs/associations/basics.md
@@ -0,0 +1,54 @@
+---
+sidebar_position: 1
+title: Basics
+---
+
+# Association Basics
+
+Sequelize provides what are called __associations__.
+These can be declared on your models to define common [__relationships__](https://en.wikipedia.org/wiki/Cardinality_(data_modeling)) between your tables.
+
+The two concepts are closely related, but not the same. __Associations__ are defined in JavaScript between your _models_, while
+__relationships__ are defined in your database between your _tables_.
+
+Sequelize supports the standard associations: [One-To-One](https://en.wikipedia.org/wiki/One-to-one_%28data_model%29), [One-To-Many](https://en.wikipedia.org/wiki/One-to-many_%28data_model%29) and [Many-To-Many](https://en.wikipedia.org/wiki/Many-to-many_%28data_model%29).
+
+## One-to-one Relationships
+
+In a One-To-One relationship, a row of one table is associated with a single row of another table.
+
+The most common type of One-To-One relationship is one where one side is mandatory, and the other side is optional.
+For instance, a driving license always belongs to a single person, but a person can have zero or one driving licenses (from the same place) .
+
+```mermaid
+erDiagram
+ people ||--o| driving_licenses : drivingLicense
+```
+
+One-To-One relationships can be created by using __the [`HasOne`](./has-one.md) association__.
+
+## One-to-many Relationships
+
+In a One-To-Many relationship, a row of one table is associated with _zero, one or more_ rows of another table.
+
+For instance, a person is always born in one city, but a city can have zero or more people born in it.
+
+```mermaid
+erDiagram
+ people ||--o{ cities : birthplace
+```
+
+One-To-Many relationships can be created by using __the [`HasMany`](./has-many.md) association__.
+
+## Many-to-many Relationships
+
+In a Many-To-Many relationship, a row of one table is associated with _zero, one or more_ rows of another table, and vice versa.
+
+For instance, a person can have liked zero or more Toots, and a Toot can have been liked by zero or more people.
+
+```mermaid
+erDiagram
+ people }o--o{ toots : likedToots
+```
+
+Many-To-Many relationships can be created by using __the [`BelongsToMany`](./belongs-to-many.md) association__.
diff --git a/docs/associations/belongs-to-many.md b/docs/associations/belongs-to-many.md
new file mode 100644
index 00000000..68f5084a
--- /dev/null
+++ b/docs/associations/belongs-to-many.md
@@ -0,0 +1,542 @@
+---
+sidebar_position: 5
+title: BelongsToMany
+---
+
+# The BelongsToMany Association
+
+The BelongsToMany association is used to create a [Many-To-Many relationship](https://en.wikipedia.org/wiki/Many-to-many_(data_model)) between two models.
+
+In a Many-To-Many relationship, a row of one table is associated with _zero, one or more_ rows of another table, and vice versa.
+
+For instance, a person can have liked zero or more Toots, and a Toot can have been liked by zero or more people.
+
+```mermaid
+erDiagram
+ people }o--o{ toots : likedToots
+```
+
+Because foreign keys can only point to a single row, Many-To-Many relationships are implemented using a junction table (called __through table__ in Sequelize), and are
+really just two One-To-Many relationships.
+
+```mermaid
+erDiagram
+ people }o--|| liked_toots : user
+ liked_toots ||--o{ toots : toot
+```
+
+The junction table is used to store the foreign keys of the two associated models.
+
+## Defining the Association
+
+Here is how you would define the `Person` and `Toot` models in Sequelize:
+
+```ts
+import { Model, InferAttributes, InferCreationAttributes, NonAttribute } from '@sequelize/core';
+import { BelongsToMany } from '@sequelize/core/decorators-legacy';
+
+class Person extends Model, InferCreationAttributes> {
+ // highlight-start
+ @BelongsToMany(() => Toot, {
+ through: 'LikedToot',
+ })
+ declare likedToots?: NonAttribute;
+ // highlight-end
+}
+
+class Toot extends Model, InferCreationAttributes> {}
+```
+
+In the example above, the `Person` model has a Many-To-Many relationship with the `Toot` model, using the `LikedToot` junction model.
+
+The `LikedToot` model is automatically generated by Sequelize, if it does not already exist,
+and will receive the two foreign keys: `userId` and `tootId`.
+
+:::caution String `through` option
+
+The `through` option is used to specify the through __model__, not the through __table__.
+We recommend that you follow the same naming conventions as other models (i.e. PascalCase & singular):
+
+```ts
+class Person extends Model, InferCreationAttributes> {
+ @BelongsToMany(() => Toot, {
+ // You should name this LikedToot instead.
+ // error-next-line
+ through: 'liked_toots',
+ })
+ declare likedToots?: NonAttribute;
+}
+```
+
+:::
+
+## Customizing the Junction Table
+
+The junction table can be customized by creating the model yourself, and passing it to the `through` option.
+This is useful if you want to add additional attributes to the junction table.
+
+```ts
+import { Model, DataTypes, InferAttributes, InferCreationAttributes, NonAttribute } from '@sequelize/core';
+import { BelongsToMany, Attribute, NotNull } from '@sequelize/core/decorators-legacy';
+import { PrimaryKey } from './attribute.js';
+
+class Person extends Model, InferCreationAttributes> {
+ @BelongsToMany(() => Toot, {
+ through: () => LikedToot,
+ })
+ declare likedToots?: NonAttribute;
+}
+
+class LikedToot extends Model, InferCreationAttributes> {
+ declare likerId: number;
+ declare likedTootId: number;
+}
+
+class Toot extends Model, InferCreationAttributes> {}
+```
+
+In TypeScript, you need to declare the typing of your foreign keys, but they will still be configured by Sequelize automatically.
+You can still, of course, use any [attribute decorator](../models/defining-models.mdx) to customize them.
+
+## Inverse Association
+
+The `BelongsToMany` association automatically creates the inverse association on the target model, which is also a `BelongsToMany` association.
+
+You can customize the inverse association by using the `inverse` option:
+
+```ts
+import { Model, InferAttributes, InferCreationAttributes, NonAttribute } from '@sequelize/core';
+import { BelongsToMany } from '@sequelize/core/decorators-legacy';
+
+class Person extends Model, InferCreationAttributes> {
+ @BelongsToMany(() => Toot, {
+ through: 'LikedToot',
+ inverse: {
+ as: 'likers',
+ },
+ })
+ declare likedToots?: NonAttribute;
+}
+
+class Toot extends Model, InferCreationAttributes> {
+ /** Declared by {@link Person.likedToots} */
+ declare likers?: NonAttribute;
+}
+```
+
+The above would result in the following model configuration:
+
+```mermaid
+erDiagram
+ Person }o--o{ Toot : "⬇️ likedToots / ⬆️ likers"
+```
+
+## Intermediary associations
+
+As explained in previous sections, Many-To-Many relationships are implemented as multiple One-To-Many relationships
+and a junction table.
+
+In Sequelize, the BelongsToMany association creates four associations:
+
+- 1️⃣ One [HasMany](./has-many.md) association going from the Source Model to the Through Model.
+- 2️⃣ One [BelongsTo](./belongs-to.md) association going from the Through Model to the Source Model.
+- 3️⃣ One [HasMany](./has-many.md) association going from the Target Model to the Through Model.
+- 4️⃣ One [BelongsTo](./belongs-to.md) association going from the Through Model to the Target Model.
+
+```mermaid
+erDiagram
+ Person }o--|| LikedToot : "⬇️ 1️⃣ likedTootsLikers / ⬆️ 2️⃣ liker"
+ LikedToot ||--o{ Toot : " ⬇️ 3️⃣ likedToot / ⬆️ 4️⃣ likersLikedToots"
+ Person }o--o{ Toot : "⬇️ likedToots / ⬆️ likers"
+```
+
+Their names are automatically generated based on the name of the BelongsToMany association,
+and the name of its inverse association.
+
+You can customize the names of these associations by using the `throughAssociations` options:
+
+```ts
+class Person extends Model, InferCreationAttributes> {
+ @BelongsToMany(() => Toot, {
+ through: 'LikedToot',
+ inverse: {
+ as: 'likers',
+ },
+ // highlight-start
+ throughAssociations: {
+ // 1️⃣ The name of the association going from the source model (Person)
+ // to the through model (LikedToot)
+ fromSource: 'likedTootsLikers',
+
+ // 2️⃣ The name of the association going from the through model (LikedToot)
+ // to the source model (Person)
+ toSource: 'liker',
+
+ // 3️⃣ The name of the association going from the target model (Toot)
+ // to the through model (LikedToot)
+ fromTarget: 'likersLikedToots',
+
+ // 4️⃣ The name of the association going from the through model (LikedToot)
+ // to the target model (Toot)
+ toTarget: 'likedToot',
+ },
+ // highlight-end
+ })
+ declare likedToots?: NonAttribute;
+}
+```
+
+## Foreign Keys Names
+
+Sequelize will generate foreign keys automatically based on the names of your associations.
+It is the name of your association + the name of the attribute the association is pointing to (which defaults to the primary key).
+
+In the example above, the foreign keys would be `likerId` and `likedTootId`, because the associations are called `likedToots` and `likers`,
+and the primary keys referenced by the foreign keys are both called `id`.
+
+You can customize the foreign keys by using the `foreignKey` and `otherKey` options. The `foreignKey` option is the foreign key that
+points to the source model, and the `otherKey` is the foreign key that points to the target model.
+
+```ts
+class Person extends Model, InferCreationAttributes> {
+ @BelongsToMany(() => Toot, {
+ through: 'LikedToot',
+ inverse: {
+ as: 'likers',
+ },
+ // highlight-start
+ // This foreign key points to the Person model
+ foreignKey: 'personId',
+ // This foreign key points to the Toot model
+ otherKey: 'tootId',
+ // highlight-end
+ })
+ declare likedToots?: NonAttribute;
+}
+```
+
+## Foreign Key targets (`sourceKey`, `targetKey`)
+
+By default, Sequelize will use the primary key of the source & target models as the attribute the foreign key references.
+You can customize this by using the `sourceKey` & `targetKey` option.
+
+The `sourceKey` option is the attribute from the model on which the association is defined,
+and the `targetKey` is the attribute from the target model.
+
+```ts
+class Person extends Model, InferCreationAttributes> {
+ @BelongsToMany(() => Toot, {
+ through: 'LikedToot',
+ inverse: {
+ as: 'likers',
+ },
+ // highlight-start
+ // The foreignKey will reference the 'id' attribute of the Person model
+ sourceKey: 'id',
+ // The otherKey will reference the 'id' attribute of the Toot model
+ targetKey: 'id',
+ // highlight-end
+ })
+ declare likedToots?: NonAttribute;
+}
+```
+
+## Through Pair Unique Constraint
+
+The BelongsToMany association creates a unique key on the foreign keys of the through model.
+
+This unique key name can be changed using the `through.unique` option. You can also set it to `false` to disable the unique constraint altogether.
+
+```ts
+class Person extends Model, InferCreationAttributes> {
+ @BelongsToMany(() => Toot, {
+ through: {
+ model: 'LikedToot',
+ // highlight-next-line
+ unique: false,
+ },
+ })
+ declare likedToots?: NonAttribute;
+}
+```
+
+## Association Methods
+
+All associations add methods to the source model[^1]. These methods can be used to fetch, create, and delete associated models.
+
+If you use TypeScript, you will need to declare these methods on your model class.
+
+### Association Getter (`getX`)
+
+The association getter is used to fetch the associated models. It is always named `get`:
+
+```ts
+import { BelongsToManyGetAssociationsMixin } from '@sequelize/core';
+
+class Author extends Model, InferCreationAttributes> {
+ @BelongsToMany(() => Book, { through: 'BookAuthor' })
+ declare books?: NonAttribute;
+
+ // highlight-start
+ declare getBooks: BelongsToManyGetAssociationsMixin;
+ // highlight-end
+}
+
+// ...
+
+const author = await Author.findByPk(1);
+
+// highlight-start
+const books: Book[] = await author.getBooks();
+// highlight-end
+```
+
+### Association Setter (`setX`)
+
+The association setter is used to set the associated models. It is always named `set`.
+
+If the model is already associated to one or more models, the old associations are removed before the new ones are added.
+
+```ts
+import { BelongsToManySetAssociationsMixin } from '@sequelize/core';
+
+class Author extends Model, InferCreationAttributes> {
+ @BelongsToMany(() => Book, { through: 'BookAuthor' })
+ declare books?: NonAttribute;
+
+ // highlight-start
+ declare setBooks: BelongsToManySetAssociationsMixin<
+ Book,
+ /* this is the type of the primary key of the target */
+ Book['id']
+ >;
+ // highlight-end
+}
+
+// ...
+
+const author = await Author.findByPk(1);
+const [book1, book2, book3] = await Book.findAll({ limit: 3 });
+
+// highlight-start
+// Remove all previous associations and set the new ones
+await author.setBooks([book1, book2, book3]);
+
+// You can also use the primary key of the newly associated model as a way to identify it
+// without having to fetch it first.
+await author.setBooks([1, 2, 3]);
+// highlight-end
+```
+
+### Association Adder (`addX`)
+
+The association adder is used to add one or more new associated models without removing existing ones.
+There are two versions of this method:
+
+- `add`: Associates a single new model.
+- `add`: Associates multiple new models.
+
+```ts
+import { BelongsToManyAddAssociationMixin, BelongsToManyAddAssociationsMixin } from '@sequelize/core';
+
+class Author extends Model, InferCreationAttributes> {
+ @BelongsToMany(() => Book, { through: 'BookAuthor' })
+ declare books?: NonAttribute;
+
+ // highlight-start
+ declare addBook: BelongsToManyAddAssociationMixin<
+ Book,
+ /* this is the type of the primary key of the target */
+ Book['id']
+ >;
+
+ declare addBooks: BelongsToManyAddAssociationsMixin<
+ Book,
+ /* this is the type of the primary key of the target */
+ Book['id']
+ >;
+ // highlight-end
+}
+
+// ...
+
+const author = await Author.findByPk(1);
+const [book1, book2, book3] = await Book.findAll({ limit: 3 });
+
+// highlight-start
+// Add a single book, without removing existing ones
+await author.addBook(book1);
+
+// Add multiple books, without removing existing ones
+await author.addBooks([book1, book2]);
+
+// You can also use the primary key of the newly associated model as a way to identify it
+// without having to fetch it first.
+await author.addBook(1);
+await author.addBooks([1, 2, 3]);
+// highlight-end
+```
+
+### Association Remover (`removeX`)
+
+The association remover is used to remove one or more associated models.
+
+There are two versions of this method:
+
+- `remove`: Removes a single associated model.
+- `remove`: Removes multiple associated models.
+
+
+```ts
+import { BelongsToManyRemoveAssociationMixin, BelongsToManyRemoveAssociationsMixin } from '@sequelize/core';
+
+class Author extends Model, InferCreationAttributes> {
+ @BelongsToMany(() => Book, { through: 'BookAuthor' })
+ declare books?: NonAttribute;
+
+ // highlight-start
+ declare removeBook: BelongsToManyRemoveAssociationMixin<
+ Book,
+ /* this is the type of the primary key of the target */
+ Book['id']
+ >;
+
+ declare removeBooks: BelongsToManyRemoveAssociationsMixin<
+ Book,
+ /* this is the type of the primary key of the target */
+ Book['id']
+ >;
+ // highlight-end
+}
+
+// ...
+
+const author = await Author.findByPk(1);
+const [book1, book2, book3] = await Book.findAll({ limit: 3 });
+
+// highlight-start
+// Remove a single book, without removing existing ones
+await author.removeBook(book1);
+
+// Remove multiple books, without removing existing ones
+await author.removeBooks([book1, book2]);
+
+// You can also use the primary key of the newly associated model as a way to identify it
+// without having to fetch it first.
+await author.removeBook(1);
+await author.removeBooks([1, 2, 3]);
+// highlight-end
+```
+
+### Association Creator (`createX`)
+
+The association creator is used to create a new associated model and associate it with the source model. It is always named `create`.
+
+```ts
+import { BelongsToManyCreateAssociationMixin } from '@sequelize/core';
+
+class Author extends Model, InferCreationAttributes> {
+ @BelongsToMany(() => Book, { through: 'BookAuthor' })
+ declare books?: NonAttribute;
+
+ // highlight-start
+ declare createBook: BelongsToManyCreateAssociationMixin;
+ // highlight-end
+}
+
+// ...
+
+const author = await Author.findByPk(1);
+
+// highlight-start
+const book = await author.createBook({
+ content: 'This is a book',
+});
+// highlight-end
+```
+
+:::info Omitting the foreign key
+
+In the example above, we did not need to specify the `postId` attribute. This is because Sequelize will automatically add it to the creation attributes.
+
+If you use TypeScript, you need to let TypeScript know that the foreign key is not required. You can do so using the second generic argument of the `BelongsToManyCreateAssociationMixin` type.
+
+```ts
+BelongsToManyCreateAssociationMixin
+ ^ Here
+```
+
+:::
+
+### Association Checker (`hasX`)
+
+The association checker is used to check if a model is associated with another model. It has two versions:
+
+- `has`: Checks if a single model is associated.
+- `has`: Checks whether all the specified models are associated.
+
+```ts
+import { BelongsToManyHasAssociationMixin, BelongsToManyHasAssociationsMixin } from '@sequelize/core';
+
+class Author extends Model, InferCreationAttributes> {
+ @BelongsToMany(() => Book, { through: 'BookAuthor' })
+ declare books?: NonAttribute;
+
+ // highlight-start
+ declare hasBook: BelongsToManyHasAssociationMixin<
+ Book,
+ /* this is the type of the primary key of the target */
+ Book['id']
+ >;
+
+ declare hasBooks: BelongsToManyHasAssociationsMixin<
+ Book,
+ /* this is the type of the primary key of the target */
+ Book['id']
+ >;
+ // highlight-end
+}
+
+// ...
+
+const author = await Author.findByPk(1);
+
+// highlight-start
+// Returns true if the post has a book with id 1
+const isAssociated = await author.hasBook(book1);
+
+// Returns true if the post is associated to all specified books
+const isAssociated = await author.hasBooks([book1, book2, book3]);
+
+// Like other association methods, you can also use the primary key of the associated model as a way to identify it
+const isAssociated = await author.hasBooks([1, 2, 3]);
+// highlight-end
+```
+
+### Association Counter (`countX`)
+
+The association counter is used to count the number of associated models. It is always named `count`.
+
+```ts
+import { BelongsToManyCountAssociationsMixin } from '@sequelize/core';
+
+class Author extends Model, InferCreationAttributes> {
+ @BelongsToMany(() => Book, { through: 'BookAuthor' })
+ declare books?: NonAttribute;
+
+ // highlight-start
+ declare countBooks: BelongsToManyCountAssociationsMixin;
+ // highlight-end
+}
+
+// ...
+
+const author = await Author.findByPk(1);
+
+// highlight-start
+// Returns the number of associated books
+const count = await author.countBooks();
+// highlight-end
+```
+
+[^1]: The source model is the model that defines the association.
diff --git a/docs/associations/belongs-to.md b/docs/associations/belongs-to.md
new file mode 100644
index 00000000..2f294df7
--- /dev/null
+++ b/docs/associations/belongs-to.md
@@ -0,0 +1,262 @@
+---
+sidebar_position: 4
+title: BelongsTo
+---
+
+# The BelongsTo Association
+
+The `BelongsTo` association is the association all other associations are based on. It's the simplest form of
+association, and is meant to be used as a way to add a foreign key to a model.
+
+We recommend reading the guides on [`HasOne`](./has-one.md) and [`HasMany`](./has-many.md) before reading this guide.
+
+## Defining a BelongsTo Association
+
+The `BelongsTo` association is used on the opposite side of where you would use a `HasOne` or `HasMany` association.
+It is capable of creating both One-To-One and One-To-Many relationships.
+
+For instance, here is how you would create the association we described in the [`HasMany`](./has-many.md) guide,
+using a `BelongsTo` association:
+
+```ts
+import { Model, DataTypes, InferAttributes, InferCreationAttributes, CreationOptional, NonAttribute } from '@sequelize/core';
+import { PrimaryKey, Attribute, AutoIncrement, NotNull, BelongsTo } from '@sequelize/core/decorators-legacy';
+
+class Post extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.INTEGER)
+ @AutoIncrement
+ @PrimaryKey
+ declare id: CreationOptional;
+}
+
+class Comment extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.INTEGER)
+ @AutoIncrement
+ @PrimaryKey
+ declare id: CreationOptional;
+
+ // highlight-start
+ @BelongsTo(() => Post, 'postId')
+ declare post?: NonAttribute;
+
+ // This is the foreign key
+ @Attribute(DataTypes.INTEGER)
+ @NotNull
+ declare postId: number;
+ // highlight-end
+}
+```
+
+And here is how you would create the association we described in the [`HasOne`](./has-one.md) guide:
+
+```ts
+import { Model, DataTypes, InferAttributes, InferCreationAttributes, CreationOptional, NonAttribute } from '@sequelize/core';
+import { PrimaryKey, Attribute, AutoIncrement, NotNull, HasOne, BelongsTo } from '@sequelize/core/decorators-legacy';
+
+class Person extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.INTEGER)
+ @AutoIncrement
+ @PrimaryKey
+ declare id: CreationOptional;
+}
+
+class DrivingLicense extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.INTEGER)
+ @AutoIncrement
+ @PrimaryKey
+ declare id: CreationOptional;
+
+ // highlight-start
+ @BelongsTo(() => Person, /* foreign key */ 'ownerId')
+ declare owner?: NonAttribute;
+
+ // This is the foreign key
+ @Attribute(DataTypes.INTEGER)
+ @NotNull
+ declare ownerId: number;
+ // highlight-end
+}
+```
+
+## Inverse Association
+
+Unlike the other 3 associations, `BelongsTo` does _not_ automatically create the inverse association, because it does
+not know whether it should be a `HasOne` or a `HasMany` association.
+
+You can configure the inverse association by using the `inverse` option:
+
+```ts
+class Post extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.INTEGER)
+ @AutoIncrement
+ @PrimaryKey
+ declare id: CreationOptional;
+
+ // highlight-start
+ /** Declared by {@link Comment#post} */
+ declare comments?: Comment[];
+ // highlight-end
+}
+
+class Comment extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.INTEGER)
+ @AutoIncrement
+ @PrimaryKey
+ declare id: CreationOptional;
+
+ @BelongsTo(() => Post, {
+ foreignKey: 'postId',
+ // highlight-start
+ inverse: {
+ as: 'comments',
+ // Either 'hasOne' or 'hasMany'
+ type: 'hasMany',
+ },
+ // highlight-end
+ })
+ declare post?: NonAttribute;
+
+ // This is the foreign key
+ @Attribute(DataTypes.INTEGER)
+ @NotNull
+ declare postId: number;
+}
+```
+
+## Association Methods
+
+The `BelongsTo` association adds the following methods to the model it is defined on:
+
+### Association Getter (`getX`)
+
+The getter method is used to retrieve the associated model. It is always named `get`.
+
+```ts
+import { BelongsToGetAssociationMixin } from '@sequelize/core';
+class Comment extends Model {
+ @BelongsTo(() => Post, 'postId')
+ declare post?: NonAttribute;
+
+ // highlight-start
+ declare getPost: BelongsToGetAssociationMixin;
+ // highlight-end
+}
+
+const comment = await Comment.findByPk(1);
+const post = await comment.getPost();
+```
+
+### Association Setter (`setX`)
+
+The setter method is used to associate a model with another model. It is always named `set`.
+
+It is equivalent to setting the foreign key directly, then calling `save`.
+
+```ts
+import { BelongsToSetAssociationMixin } from '@sequelize/core';
+
+class Comment extends Model {
+ @BelongsTo(() => Post, 'postId')
+ declare post?: NonAttribute;
+
+ // highlight-start
+ declare setPost: BelongsToSetAssociationMixin;
+ // highlight-end
+}
+
+const comment = await Comment.findByPk(1);
+const post = await Post.findByPk(1);
+await comment.setPost(post);
+
+// Or, if you already have the foreign key
+await comment.setPost(1);
+```
+
+It is also possible to delay the call to `save` by setting the `save` option to `false`, however __this is not very useful__,
+as it is equivalent to setting the foreign key directly, but using a (pointlessly) asynchronous method.
+
+```ts
+await comment.setPost(post, { save: false });
+await comment.save();
+```
+
+### Association Creator (`createX`)
+
+The creator method is used to create a new associated model. It is always named `create`.
+
+It is equivalent to creating a new model, then setting the foreign key, then calling `save`.
+
+```ts
+import { BelongsToCreateAssociationMixin } from '@sequelize/core';
+
+class Comment extends Model {
+ @BelongsTo(() => Post, 'postId')
+ declare post?: NonAttribute;
+
+ // highlight-start
+ declare createPost: BelongsToCreateAssociationMixin;
+ // highlight-end
+}
+
+const comment = await Comment.create({ content: 'This is a comment' });
+
+// highlight-start
+const post = await comment.createPost({
+ title: 'New Post',
+ content: 'This is a new post',
+});
+// highlight-end
+```
+
+:::caution Inefficient method
+
+Using this method is discouraged, as it is less efficient than creating the associated model first,
+then creating or updating the current model.
+
+The following code is more efficient:
+
+```ts
+const post = await Post.create({
+ title: 'New Post',
+ content: 'This is a new post',
+});
+
+const comment = await Comment.create({
+ content: 'This is a comment',
+ postId: post.id,
+});
+```
+
+Or, if you have defined the inverse association, this is just as efficient:
+
+```ts
+const post = await Post.create({
+ title: 'New Post',
+ content: 'This is a new post',
+});
+
+const comment = await post.createComment({
+ content: 'This is a comment',
+});
+```
+
+:::
+
+## Foreign Key targets (`targetKey`)
+
+By default, Sequelize will use the primary key of the target model as the attribute the foreign key references.
+You can customize this by using the `targetKey` option.
+
+```ts
+class Comment extends Model {
+ declare id: CreationOptional;
+
+ @BelongsTo(() => Post, {
+ foreignKey: 'postId',
+ // highlight-next-line
+ // The foreign key will reference the 'id' attribute of the Post model
+ targetKey: 'id',
+ })
+ declare post?: NonAttribute;
+}
+```
\ No newline at end of file
diff --git a/docs/associations/faq.md b/docs/associations/faq.md
new file mode 100644
index 00000000..ff975db3
--- /dev/null
+++ b/docs/associations/faq.md
@@ -0,0 +1,102 @@
+# Association FAQ
+
+## Multiple associations to the same model
+
+If you need to create multiple [`HasOne`](./has-one.md) or [`HasMany`](./has-many.md) associations to the same model, make sure to name their inverse associations,
+otherwise Sequelize will attempt to use the same inverse association for both associations.
+
+```ts
+class Person extends Model, InferCreationAttributes> {
+ @HasOne(() => DrivingLicense, {
+ foreignKey: 'ownerId',
+ // highlight-start
+ inverse: {
+ as: 'owner',
+ },
+ // highlight-end
+ })
+ declare currentDrivingLicense?: NonAttribute;
+}
+```
+
+## Self-references
+
+Any association can be self-referencing. For example, a `Person` model can have a `parent`/`children` association to another `Person` model.
+
+```ts
+class Person extends Model, InferCreationAttributes> {
+ @BelongsToMany(() => Person, {
+ // highlight-start
+ inverse: {
+ as: 'parents',
+ },
+ // highlight-end
+ })
+ declare children?: NonAttribute;
+ declare parents?: NonAttribute;
+}
+```
+
+## Composite Foreign Keys
+
+Composite foreign keys are not currently supported by Sequelize's associations. See [issue #311](https://github.com/sequelize/sequelize/issues/311) for more information.
+
+## Customizing Foreign Keys
+
+Sequelize will generate foreign keys automatically, but you can customize how. The `foreignKey` option (as well as `otherKey` in [`BelongsToMany`](./belongs-to-many.md))
+can be set to a string to specify the name of the foreign key, or to an object to specify the name of the foreign key and other options.
+
+When set to an object, the `foreignKey` option accepts all options that regular attributes accept, including `allowNull` and `defaultValue`.
+See the [API reference](pathname:///api/v7/interfaces/_sequelize_core.index.ForeignKeyOptions.html).
+
+```ts
+class Person extends Model {
+ @HasOne(() => DrivingLicense, {
+ foreignKey: {
+ name: 'ownerId',
+ columnName: 'owner_id',
+ },
+ })
+ declare drivingLicense?: NonAttribute;
+}
+```
+
+You can also use [Attribute Decorators](../models/defining-models.mdx) on your foreign key attribute:
+
+```ts
+class Person extends Model {
+ @HasOne(() => DrivingLicense, 'ownerId')
+ declare drivingLicense?: NonAttribute;
+}
+
+class DrivingLicense extends Model {
+ @Attribute({
+ columnName: 'owner_id',
+ })
+ declare ownerId: number;
+}
+```
+
+### `onDelete` and `onUpdate`
+
+One of the most common use cases for customizing foreign keys is to set the `onDelete` and `onUpdate` behaviors.
+
+```ts
+class Person extends Model {
+ @HasOne(() => DrivingLicense, {
+ foreignKey: {
+ name: 'ownerId',
+ onDelete: 'CASCADE',
+ onUpdate: 'CASCADE',
+ },
+ })
+ declare drivingLicense?: NonAttribute;
+}
+```
+
+The possible choices are `RESTRICT`, `CASCADE`, `NO ACTION`, `SET DEFAULT` and `SET NULL`.
+
+By default, Sequelize will set the following values:
+
+- for `ON DELETE`: `SET NULL` if the foreign key is nullable, and `CASCADE` if it is not.
+- for `ON UPDATE`: `CASCADE`
diff --git a/docs/associations/has-many.md b/docs/associations/has-many.md
new file mode 100644
index 00000000..8b761c60
--- /dev/null
+++ b/docs/associations/has-many.md
@@ -0,0 +1,431 @@
+---
+sidebar_position: 3
+title: HasMany
+---
+
+# The HasMany Association
+
+The HasMany association is used to create a One-To-Many relationship between two models.
+
+In a One-To-Many relationship, a row of one table is associated with _zero, one or more_ rows of another table.
+
+For instance, a post can have zero or more comments, but a comment can only belong to one post.
+
+```mermaid
+erDiagram
+ posts ||--o{ comments : comments
+```
+
+## Defining the Association
+
+Here is how you would define the `Post` and `Comment` models in Sequelize:
+
+```ts
+import { Model, DataTypes, InferAttributes, InferCreationAttributes, CreationOptional, NonAttribute } from '@sequelize/core';
+import { PrimaryKey, Attribute, AutoIncrement, NotNull, HasMany, BelongsTo } from '@sequelize/core/decorators-legacy';
+
+class Post extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.INTEGER)
+ @AutoIncrement
+ @PrimaryKey
+ declare id: CreationOptional;
+
+ // highlight-start
+ @HasMany(() => Comment, /* foreign key */ 'postId')
+ declare comments?: NonAttribute;
+ // highlight-end
+}
+
+class Comment extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.INTEGER)
+ @AutoIncrement
+ @PrimaryKey
+ declare id: CreationOptional;
+
+ // highlight-start
+ // This is the foreign key
+ @Attribute(DataTypes.INTEGER)
+ @NotNull
+ declare postId: number;
+ // highlight-end
+}
+```
+
+Note that in the example above, the `Comment` model has a foreign key to the `Post` model. __`HasMany` adds the foreign key
+on the model the association targets.__
+
+## Inverse association
+
+The `HasMany` association automatically creates an inverse association on the target model.
+The inverse association is a [`BelongsTo`](./belongs-to.md) association.
+
+You can configure that inverse association by using the `inverse` option:
+
+```ts
+import { Model, DataTypes, InferAttributes, InferCreationAttributes, CreationOptional, NonAttribute } from '@sequelize/core';
+import { PrimaryKey, Attribute, AutoIncrement, NotNull, HasMany, BelongsTo } from '@sequelize/core/decorators-legacy';
+
+class Post extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.INTEGER)
+ @AutoIncrement
+ @PrimaryKey
+ declare id: CreationOptional;
+
+ @HasMany(() => Comment, {
+ foreignKey: 'postId',
+ // highlight-start
+ inverse: {
+ as: 'post',
+ },
+ // highlight-end
+ })
+ declare comments?: NonAttribute;
+}
+
+class Comment extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.INTEGER)
+ @AutoIncrement
+ @PrimaryKey
+ declare id: CreationOptional;
+
+ // highlight-start
+ /** Defined by {@link Post.comments} */
+ declare post?: NonAttribute;
+ // highlight-end
+
+ // This is the foreign key
+ @Attribute(DataTypes.INTEGER)
+ @NotNull
+ declare postId: number;
+}
+```
+
+## Association Methods
+
+All associations add methods to the source model[^1]. These methods can be used to fetch, create, and delete associated models.
+
+If you use TypeScript, you will need to declare these methods on your model class.
+
+### Association Getter (`getX`)
+
+The association getter is used to fetch the associated models. It is always named `get`:
+
+```ts
+import { HasManyGetAssociationsMixin } from '@sequelize/core';
+
+class Post extends Model, InferCreationAttributes> {
+ @HasMany(() => Comment, 'postId')
+ declare comments?: NonAttribute;
+
+ // highlight-start
+ declare getComments: HasManyGetAssociationsMixin;
+ // highlight-end
+}
+
+// ...
+
+const post = await Post.findByPk(1);
+
+// highlight-start
+const comments: Comment[] = await post.getComments();
+// highlight-end
+```
+
+### Association Setter (`setX`)
+
+The association setter is used to set the associated models. It is always named `set`.
+
+If the model is already associated to one or more models, the old associations are removed before the new ones are added.
+
+```ts
+import { HasManySetAssociationsMixin } from '@sequelize/core';
+
+class Post extends Model, InferCreationAttributes> {
+ @HasMany(() => Comment, 'postId')
+ declare comments?: NonAttribute;
+
+ // highlight-start
+ declare setComments: HasManySetAssociationsMixin<
+ Comment,
+ /* this is the type of the primary key of the target */
+ Comment['id']
+ >;
+ // highlight-end
+}
+
+// ...
+
+const post = await Post.findByPk(1);
+const [comment1, comment2, comment3] = await Comment.findAll({ limit: 3 });
+
+// highlight-start
+// Remove all previous associations and set the new ones
+await post.setComments([comment1, comment2, comment3]);
+
+// You can also use the primary key of the newly associated model as a way to identify it
+// without having to fetch it first.
+await post.setComments([1, 2, 3]);
+// highlight-end
+```
+
+:::caution
+
+If the foreign key is not nullable, calling this method will delete the previously associated models (if any),
+as setting their foreign key to `null` would result in a validation error.
+
+If the foreign key is nullable, it will by default set it to null on all previously associated models.
+You can use the `destroyPrevious` option to delete the previously associated models instead:
+
+```ts
+// this will delete all previously associated models
+await post.setComments([], { destroyPrevious: true });
+```
+
+:::
+
+### Association Adder (`addX`)
+
+The association adder is used to add one or more new associated models without removing existing ones.
+There are two versions of this method:
+
+- `add`: Associates a single new model.
+- `add`: Associates multiple new models.
+
+```ts
+import { HasManyAddAssociationMixin, HasManyAddAssociationsMixin } from '@sequelize/core';
+
+class Post extends Model, InferCreationAttributes> {
+ @HasMany(() => Comment, 'postId')
+ declare comments?: NonAttribute;
+
+ // highlight-start
+ declare addComment: HasManyAddAssociationMixin<
+ Comment,
+ /* this is the type of the primary key of the target */
+ Comment['id']
+ >;
+
+ declare addComments: HasManyAddAssociationsMixin<
+ Comment,
+ /* this is the type of the primary key of the target */
+ Comment['id']
+ >;
+ // highlight-end
+}
+
+// ...
+
+const post = await Post.findByPk(1);
+const [comment1, comment2, comment3] = await Comment.findAll({ limit: 3 });
+
+// highlight-start
+// Add a single comment, without removing existing ones
+await post.addComment(comment1);
+
+// Add multiple comments, without removing existing ones
+await post.addComments([comment1, comment2]);
+
+// You can also use the primary key of the newly associated model as a way to identify it
+// without having to fetch it first.
+await post.addComment(1);
+await post.addComments([1, 2, 3]);
+// highlight-end
+```
+
+### Association Remover (`removeX`)
+
+The association remover is used to remove one or more associated models.
+
+There are two versions of this method:
+
+- `remove`: Removes a single associated model.
+- `remove`: Removes multiple associated models.
+
+
+```ts
+import { HasManyRemoveAssociationMixin, HasManyRemoveAssociationsMixin } from '@sequelize/core';
+
+class Post extends Model, InferCreationAttributes> {
+ @HasMany(() => Comment, 'postId')
+ declare comments?: NonAttribute;
+
+ // highlight-start
+ declare removeComment: HasManyRemoveAssociationMixin<
+ Comment,
+ /* this is the type of the primary key of the target */
+ Comment['id']
+ >;
+
+ declare removeComments: HasManyRemoveAssociationsMixin<
+ Comment,
+ /* this is the type of the primary key of the target */
+ Comment['id']
+ >;
+ // highlight-end
+}
+
+// ...
+
+const post = await Post.findByPk(1);
+const [comment1, comment2, comment3] = await Comment.findAll({ limit: 3 });
+
+// highlight-start
+// Remove a single comment, without removing existing ones
+await post.removeComment(comment1);
+
+// Remove multiple comments, without removing existing ones
+await post.removeComments([comment1, comment2]);
+
+// You can also use the primary key of the newly associated model as a way to identify it
+// without having to fetch it first.
+await post.removeComment(1);
+await post.removeComments([1, 2, 3]);
+// highlight-end
+```
+
+:::caution
+
+If the foreign key is not nullable, calling this method will delete the specified models,
+as setting their foreign key to `null` would result in a validation error.
+
+If the foreign key is nullable, it will by default set it to null on all specified models.
+You can use the `destroy` option to delete the previously associated models instead:
+
+```ts
+// this will delete comments with PKs 1, 2 and 3
+await post.removeComments([1, 2, 3], { destroy: true });
+```
+
+:::
+
+### Association Creator (`createX`)
+
+The association creator is used to create a new associated model and associate it with the source model. It is always named `create`.
+
+```ts
+import { HasManyCreateAssociationMixin } from '@sequelize/core';
+
+class Post extends Model, InferCreationAttributes> {
+ @HasMany(() => Comment, 'postId')
+ declare comments?: NonAttribute;
+
+ // highlight-start
+ declare createComment: HasManyCreateAssociationMixin;
+ // highlight-end
+}
+
+// ...
+
+const post = await Post.findByPk(1);
+
+// highlight-start
+const comment = await post.createComment({
+ content: 'This is a comment',
+});
+// highlight-end
+```
+
+:::info Omitting the foreign key
+
+In the example above, we did not need to specify the `postId` attribute. This is because Sequelize will automatically add it to the creation attributes.
+
+If you use TypeScript, you need to let TypeScript know that the foreign key is not required. You can do so using the second generic argument of the `HasManyCreateAssociationMixin` type.
+
+```ts
+HasManyCreateAssociationMixin
+ ^ Here
+```
+
+:::
+
+### Association Checker (`hasX`)
+
+The association checker is used to check if a model is associated with another model. It has two versions:
+
+- `has`: Checks if a single model is associated.
+- `has`: Checks whether all the specified models are associated.
+
+```ts
+import { HasManyHasAssociationMixin, HasManyHasAssociationsMixin } from '@sequelize/core';
+
+class Post extends Model, InferCreationAttributes> {
+ @HasMany(() => Comment, 'postId')
+ declare comments?: NonAttribute;
+
+ // highlight-start
+ declare hasComment: HasManyHasAssociationMixin<
+ Comment,
+ /* this is the type of the primary key of the target */
+ Comment['id']
+ >;
+
+ declare hasComments: HasManyHasAssociationsMixin<
+ Comment,
+ /* this is the type of the primary key of the target */
+ Comment['id']
+ >;
+ // highlight-end
+}
+
+// ...
+
+const post = await Post.findByPk(1);
+
+// highlight-start
+// Returns true if the post has a comment with id 1
+const isAssociated = await post.hasComment(comment1);
+
+// Returns true if the post is associated to all specified comments
+const isAssociated = await post.hasComments([comment1, comment2, comment3]);
+
+// Like other association methods, you can also use the primary key of the associated model as a way to identify it
+const isAssociated = await post.hasComments([1, 2, 3]);
+// highlight-end
+```
+
+### Association Counter (`countX`)
+
+The association counter is used to count the number of associated models. It is always named `count`.
+
+```ts
+import { HasManyCountAssociationsMixin } from '@sequelize/core';
+
+class Post extends Model, InferCreationAttributes> {
+ @HasMany(() => Comment, 'postId')
+ declare comments?: NonAttribute;
+
+ // highlight-start
+ declare countComments: HasManyCountAssociationsMixin;
+ // highlight-end
+}
+
+// ...
+
+const post = await Post.findByPk(1);
+
+// highlight-start
+// Returns the number of associated comments
+const count = await post.countComments();
+// highlight-end
+```
+
+## Foreign Key targets (`sourceKey`)
+
+By default, Sequelize will use the primary key of the source model as the attribute the foreign key references.
+You can customize this by using the `sourceKey` option.
+
+```ts
+class Post extends Model {
+ declare id: CreationOptional;
+
+ @HasMany(() => Comment, {
+ foreignKey: 'postId',
+ // highlight-next-line
+ // The foreign key will reference the `id` attribute of the `Post` model
+ sourceKey: 'id',
+ })
+ declare comments?: NonAttribute;
+}
+```
+
+[^1]: The source model is the model that defines the association.
diff --git a/docs/associations/has-one.md b/docs/associations/has-one.md
new file mode 100644
index 00000000..488ddc49
--- /dev/null
+++ b/docs/associations/has-one.md
@@ -0,0 +1,289 @@
+---
+sidebar_position: 2
+title: HasOne
+---
+
+# The HasOne Association
+
+The HasOne association is used to create a One-To-One relationship between two models.
+
+In a One-To-One relationship, a row of one table is associated with a single row of another table.
+
+The most common type of One-To-One relationship is one where one side is mandatory, and the other side is optional.
+For instance, a driving license always belongs to a single person, but a person can have zero or one driving licenses (from the same place).
+
+```mermaid
+erDiagram
+ people ||--o| driving_licenses : drivingLicense
+```
+
+## Defining the Association
+
+Here is how you would define the `Person` and `DrivingLicense` models in Sequelize:
+
+```ts
+import { Model, DataTypes, InferAttributes, InferCreationAttributes, CreationOptional, NonAttribute } from '@sequelize/core';
+import { PrimaryKey, Attribute, AutoIncrement, NotNull, HasOne, BelongsTo } from '@sequelize/core/decorators-legacy';
+
+class Person extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.INTEGER)
+ @AutoIncrement
+ @PrimaryKey
+ declare id: CreationOptional;
+
+ // highlight-start
+ @HasOne(() => DrivingLicense, /* foreign key */ 'ownerId')
+ declare drivingLicense?: NonAttribute;
+ // highlight-end
+}
+
+class DrivingLicense extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.INTEGER)
+ @AutoIncrement
+ @PrimaryKey
+ declare id: CreationOptional;
+
+ // highlight-start
+ // This is the foreign key
+ @Attribute(DataTypes.INTEGER)
+ @NotNull
+ declare ownerId: number;
+ // highlight-end
+}
+```
+
+Note that in the example above, the `DrivingLicense` model has a foreign key to the `Person` model. __`HasOne` adds the foreign key
+on the model the association targets.__
+
+Always think about which model should own the foreign key, as the foreign key is the one that enforces the relationship.
+
+In this case, because the driving license model has a non-null foreign key,
+it is impossible to create a Driving License without assigning it to a Person.
+However, it's possible to create a Person without a Driving License.
+
+If you made the foreign key nullable, the relationship would be optional on both sides.
+
+:::info Unique FK
+
+When using `HasOne`, you may want to add a unique constraint on the foreign key to ensure that only one row can be associated with the source row.
+
+You can do this by using the `@Unique` decorator on the foreign key:
+
+```ts
+class DrivingLicense extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.INTEGER)
+ @NotNull
+ // highlight-next-line
+ @Unique
+ declare ownerId: number;
+}
+```
+
+:::
+
+## Inverse association
+
+The `HasOne` association automatically creates an inverse association on the target model.
+The inverse association is a [`BelongsTo`](./belongs-to.md) association.
+
+You can configure that inverse association by using the `inverse` option:
+
+```ts
+import { Model, DataTypes, InferAttributes, InferCreationAttributes, CreationOptional, NonAttribute } from '@sequelize/core';
+import { PrimaryKey, Attribute, AutoIncrement, NotNull, HasOne, BelongsTo } from '@sequelize/core/decorators-legacy';
+
+class Person extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.INTEGER)
+ @AutoIncrement
+ @PrimaryKey
+ declare id: CreationOptional;
+
+ @HasOne(() => DrivingLicense, {
+ foreignKey: 'ownerId',
+ // highlight-start
+ inverse: {
+ as: 'owner',
+ },
+ // highlight-end
+ })
+ declare drivingLicense?: NonAttribute;
+}
+
+class DrivingLicense extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.INTEGER)
+ @AutoIncrement
+ @PrimaryKey
+ declare id: CreationOptional;
+
+ // highlight-start
+ /** Defined by {@link Person.drivingLicense} */
+ declare owner?: NonAttribute;
+ // highlight-end
+
+ @Attribute(DataTypes.INTEGER)
+ @NotNull
+ declare ownerId: number;
+}
+```
+
+## Association Methods
+
+All associations add methods to the source model[^1]. These methods can be used to fetch, create, and delete associated models.
+
+If you use TypeScript, you will need to declare these methods on your model class.
+
+### Association Getter (`getX`)
+
+The association getter is used to fetch the associated model. It is always named `get`:
+
+```ts
+import { HasOneGetAssociationMixin } from '@sequelize/core';
+
+class Person extends Model, InferCreationAttributes> {
+ @HasOne(() => DrivingLicense, 'ownerId')
+ declare drivingLicense?: NonAttribute;
+
+ // highlight-start
+ declare getDrivingLicense: HasOneGetAssociationMixin;
+ // highlight-end
+}
+
+// ...
+
+const person = await Person.findByPk(1);
+
+// highlight-start
+const drivingLicense: DrivingLicense | null = await person.getDrivingLicense();
+// highlight-end
+```
+
+### Association Setter (`setX`)
+
+The association setter is used to set (or unset) which model is associated to this one. It is always named `set`.
+It accepts a single model instance, or `null` to unset the association.
+
+```ts
+import { HasOneSetAssociationMixin } from '@sequelize/core';
+
+class Person extends Model, InferCreationAttributes> {
+ @HasOne(() => DrivingLicense, 'ownerId')
+ declare drivingLicense?: NonAttribute;
+
+ // highlight-start
+ declare setDrivingLicense: HasOneSetAssociationMixin<
+ DrivingLicense,
+ /* this is the type of the primary key of the target */
+ DrivingLicense['id']
+ >;
+ // highlight-end
+}
+
+// ...
+
+const person = await Person.findByPk(1);
+const drivingLicense = await DrivingLicense.create({ /* ... */ });
+
+// highlight-start
+// Note: If the driving license already has an owner, it will be replaced by the new owner.
+await person.setDrivingLicense(drivingLicense);
+
+// You can also use the primary key of the newly associated model as a way to identify it
+// without having to fetch it first.
+await person.setDrivingLicense(5);
+
+// Removes the driving license from the person
+await person.setDrivingLicense(null);
+// highlight-end
+```
+
+:::caution
+
+If the foreign key is not nullable, calling this method will delete the previously associated model (if any),
+as setting its foreign key to `null` would result in a validation error.
+
+If the foreign key is nullable, it will by default set it to null. You can use the `destroyPrevious` option to delete
+the previously associated model instead:
+
+```ts
+await person.setDrivingLicense(newDrivingLicense, { destroyPrevious: true });
+```
+
+:::
+
+### Association Creator (`createX`)
+
+The association creator is used to create a new associated model. It is always named `create`.
+It accepts the same arguments as the associated model's `create` method.
+
+```ts
+import { HasOneCreateAssociationMixin } from '@sequelize/core';
+
+class Person extends Model, InferCreationAttributes> {
+ @HasOne(() => DrivingLicense, 'ownerId')
+ declare drivingLicense?: NonAttribute;
+
+ // highlight-start
+ declare createDrivingLicense: HasOneCreateAssociationMixin;
+ // highlight-end
+}
+
+// ...
+
+const person = await Person.findByPk(1);
+
+// highlight-start
+const drivingLicense: DrivingLicense = await person.createDrivingLicense({
+ number: '123456789',
+});
+// highlight-end
+```
+
+:::info Omitting the foreign key
+
+In the example above, we did not need to specify the `ownerId` attribute. This is because Sequelize will automatically add it to the creation attributes.
+
+If you use TypeScript, you need to let TypeScript know that the foreign key is not required. You can do so using the second generic argument of the `HasOneCreateAssociationMixin` type.
+
+```ts
+HasOneCreateAssociationMixin
+ ^ Here
+```
+
+:::
+
+### Inverse Association Methods
+
+The [inverse association](#inverse-association) also adds methods to its source model. See [the `BelongsTo` documentation](./belongs-to.md#association-methods) for the list of methods.
+
+## Making the association mandatory on both sides
+
+In theory, a One-To-One relationship can also be mandatory on both sides.
+For instance, a person always has a birth certificate, and a birth certificate always belongs to a person.
+
+```mermaid
+erDiagram
+ people ||--|| birth_certificates : birthCertificate
+```
+
+In practice however, it is impossible to enforce this relationship in a database. You need to handle this in your business logic.
+
+## Foreign Key targets (`sourceKey`)
+
+By default, Sequelize will use the primary key of the source model as the attribute the foreign key references.
+You can customize this by using the `sourceKey` option.
+
+```ts
+class Person extends Model {
+ declare id: CreationOptional;
+
+ @HasOne(() => DrivingLicense, {
+ foreignKey: 'ownerId',
+ // highlight-next-line
+ // The foreign key will reference the `id` attribute of the `Person` model
+ sourceKey: 'id',
+ })
+ declare drivingLicense?: NonAttribute;
+}
+```
+
+[^1]: The source model is the model that defines the association.
diff --git a/docs/associations/polymorphic-associations.md b/docs/associations/polymorphic-associations.md
new file mode 100644
index 00000000..402ae63a
--- /dev/null
+++ b/docs/associations/polymorphic-associations.md
@@ -0,0 +1,239 @@
+---
+title: Polymorphic Associations
+---
+
+A **polymorphic association** is an association that can target multiple models. For example, imagine a `Comment` model that can belong to either a `Article` or a `Video`.
+
+Sequelize offers three ways of implementing polymorphic associations, in order of recommendation:
+
+- A. Using [Model Inheritance](#inheritance-based-polymorphic-associations) (recommended)
+ - 👍 This solution supports foreign keys
+ - 👍 Tables are lighter, more performant
+ - 👍 Can easily add model-specific attributes
+ - ❌ This solution requires more tables
+- B. Using a [single model with multiple foreign keys](#single-model-multiple-foreign-key-polymorphic-associations)
+ - 👍 This solution supports foreign keys
+ - 👍 Uses a single table
+- C. Using a [single model with a single foreign key](#single-model-single-foreign-key-polymorphic-associations)
+ - 👍 Uses a single table
+ - ❌ Does not support foreign keys
+
+## Inheritance-based polymorphic associations
+
+The way this polymorphic association works is by creating a base model, such as `AbstractComment`,
+which defines the common fields between all comments.
+Then, we create models that [inherit](../models/inheritance.md) from it
+for each model that can have comments, such as `ArticleComment` and `VideoComment`.
+
+```ts
+// This is the base model, which defines the common fields between all comments.
+@AbstractModel
+abstract class AbstractComment extends Model {
+ declare id: number;
+
+ @Attributes(DataTypes.STRING)
+ @NotNull
+ declare content: string;
+
+ @Attributes(DataTypes.INTEGER)
+ @NotNull
+ declare targetId: number;
+}
+
+// This is the model for comments on articles.
+class ArticleComment extends AbstractComment, InferCreationAttributes> {
+ @BelongsTo(() => Article, 'targetId')
+ declare target?: Article;
+}
+
+// This is the model for comments on videos.
+class VideoComment extends AbstractComment, InferCreationAttributes> {
+ @BelongsTo(() => Video, 'targetId')
+ declare target?: Video;
+}
+```
+
+The above code will create two tables: `ArticleComments` and `VideoComments`.
+
+## Single-model, multiple-foreign-key polymorphic associations
+
+This solution only requires a single table, to which we add multiple, mutually-exclusive foreign keys:
+
+```ts
+class Comment extends Model, InferCreationAttributes> {
+ declare id: number;
+
+ @Attributes(DataTypes.STRING)
+ @NotNull
+ declare content: string;
+
+ @Attributes(DataTypes.INTEGER)
+ declare articleId: number | null;
+
+ @BelongsTo(() => Article, 'articleId')
+ declare article?: Article;
+
+ @Attributes(DataTypes.INTEGER)
+ declare videoId: number | null;
+
+ @BelongsTo(() => Video, 'videoId')
+ declare video?: Video;
+}
+```
+
+You can then determine which foreign key to use by checking which one is `null`.
+
+We recommend that you add a [`CHECK` constraint](../models/validations-and-constraints.md#check-constraints) on this table to ensure that only one of the foreign keys is not null at a time.
+
+## Single-model, single-foreign-key polymorphic associations
+
+:::caution
+
+This type of polymorphic associations cannot use foreign keys, as a single column can only ever reference one other table.
+This may be a problem if you want to use foreign keys for data integrity, as well as for `SELECT` performance.
+
+Due to using the same column for multiple associations, you also put yourself at greater risk of creating a data integrity issue.
+
+For these reasons, we highly recommend using one of the other two solutions instead. Proceed with caution.
+
+:::
+
+In this type of polymorphic association, we don't use foreign keys at all.
+Instead, we use two columns: one to store the type of the associated model, and one to store the ID of the associated model.
+
+As stated above, we must disable the foreign key constraints on the association, as the same column is referencing multiple tables.
+This can be done by using the `constraints: false`.
+
+We then use [association scopes](./association-scopes.md) to filter which comments belong to which models.
+
+```ts
+class Comment extends Model, InferCreationAttributes> {
+ declare id: number;
+
+ @Attributes(DataTypes.STRING)
+ @NotNull
+ declare content: string;
+
+ @Attributes(DataTypes.STRING)
+ @NotNull
+ declare targetModel: 'article' | 'video';
+
+ @Attributes(DataTypes.INTEGER)
+ @NotNull
+ declare targetId: number;
+
+ /** Defined by {@link Article#comments} */
+ declare article?: NonAttribute;
+
+ /** Defined by {@link Video#comments} */
+ declare video?: NonAttribute;
+
+ get target(): NonAttribute {
+ if (this.targetModel === 'article') {
+ return this.article;
+ } else {
+ return this.video;
+ }
+ }
+}
+
+class Video extends Model, InferCreationAttributes> {
+ declare id: number;
+
+ @HasMany(() => Comment, {
+ inverse: {
+ as: 'videos',
+ },
+ foreignKey: 'targetId',
+ // highlight-start
+ // Foreign Keys must be disabled.
+ constraints: false,
+ // This scope ensures that loading the "comments" association only loads comments that belong to videos.
+ scope: {
+ targetModel: 'video',
+ },
+ // highlight-end
+ })
+ declare comments: Comment[];
+}
+
+class Article extends Model, InferCreationAttributes> {
+ declare id: number;
+
+ @HasMany(() => Comment, {
+ inverse: {
+ as: 'articles',
+ },
+ foreignKey: 'targetId',
+ constraints: false,
+ scope: {
+ targetModel: 'article',
+ },
+ })
+ declare comments: Comment[];
+}
+```
+
+You can then eager-load `Comment` when loading `Article` or `Video`:
+
+```ts
+const article = await Article.findOne({
+ // this will only include the comments that belong to articles
+ include: ['comments'],
+});
+```
+
+Or lazy-load them:
+
+```ts
+const article = await Article.findOne();
+
+// this will only include the comments that belong to this article
+const comments = await article.getComments();
+```
+
+:::warning
+
+Do not use the inverse association without extra filtering!
+
+While using the association from Article or Video to Comment is safe,
+using the inverse association from Comment to Article or Video is not safe.
+
+The following is unsafe:
+
+```ts
+const comment = await Comment.findOne({
+ include: ['article'],
+});
+```
+
+This is because an association scope only applies to the target model, not the source model.
+The above query will try to load the "article" association of the comment, even if the comment belongs to a video.
+Worse yet, it will try to load the article using the primary key of the video.
+
+If you wish to do this, you must make sure that you filter the comment model yourself:
+
+```ts
+const comment = await Comment.findOne({
+ include: ['article'],
+ where: {
+ // highlight-next-line
+ targetModel: 'article',
+ },
+});
+```
+
+Or, when lazy-loading, that you use the right accessor:
+
+```ts
+const comment = await Comment.findOne();
+
+// highlight-start
+if (comment.targetModel === 'article') {
+ const article = await comment.getArticle();
+} else {
+ const video = await comment.getVideo();
+}
+```
+
+:::
diff --git a/docs/other-topics/migrations.md b/docs/cli.md
similarity index 71%
rename from docs/other-topics/migrations.md
rename to docs/cli.md
index 741db86a..8638e3c8 100644
--- a/docs/other-topics/migrations.md
+++ b/docs/cli.md
@@ -1,12 +1,22 @@
---
-title: Migrations
+title: Sequelize CLI
+sidebar_position: 9
---
-Just like you use [version control](https://en.wikipedia.org/wiki/Version_control) systems such as [Git](https://en.wikipedia.org/wiki/Git) to manage changes in your source code, you can use **migrations** to keep track of changes to the database. With migrations you can transfer your existing database into another state and vice versa: Those state transitions are saved in migration files, which describe how to get to the new state and how to revert the changes in order to get back to the old state.
+:::warning
+
+Neither the Sequelize CLI nor its documentation is ready for use in Sequelize 7. If you rely on the CLI, please stay on Sequelize 6 for now.
+
+Considering the main purpose of the CLI is to run migrations, you can also try using [umzug](https://github.com/sequelize/umzug), or any
+other database migration tool instead.
+
+:::
+
+Just like you use [version control](https://en.wikipedia.org/wiki/Version_control) systems such as [Git](https://en.wikipedia.org/wiki/Git) to manage changes in your source code, you can use **migrations** to keep track of changes to the database. With migrations, you can transfer your existing database into another state and vice versa: Those state transitions are saved in migration files, which describe how to get to the new state and how to revert the changes to get back to the old state.
You will need the [Sequelize Command-Line Interface (CLI)](https://github.com/sequelize/cli). The CLI ships support for migrations and project bootstrapping.
-A Migration in Sequelize is javascript file which exports two functions, `up` and `down`, that dictate how to perform the migration and undo it. You define those functions manually, but you don't call them manually; they will be called automatically by the CLI. In these functions, you should simply perform whatever queries you need, with the help of `sequelize.query` and whichever other methods Sequelize provides to you. There is no extra magic beyond that.
+A Migration in Sequelize is a JavaScript file that exports two functions, `up` and `down`, that dictate how to perform the migration and undo it. You define those functions manually, but you don't call them manually; the CLI will call them automatically. In these functions, you should perform whatever queries you need, with the help of `sequelize.query` and whichever other methods Sequelize provides to you. There is no extra magic beyond that.
## Installing the CLI
@@ -32,16 +42,16 @@ npx sequelize-cli init
yarn sequelize-cli init
```
-This will create following folders
+This will create the following folders
-- `config`, contains config file, which tells CLI how to connect with database
+- `config`, contains the config file, which tells CLI how to connect with the database
- `models`, contains all models for your project
- `migrations`, contains all migration files
- `seeders`, contains all seed files
### Configuration
-Before continuing further we will need to tell the CLI how to connect to the database. To do that let's open default config file `config/config.json`. It looks something like this:
+Before continuing further we will need to tell the CLI how to connect to the database. To do that let's open the default config file `config/config.json`. It looks something like this:
```json
{
@@ -69,17 +79,17 @@ Before continuing further we will need to tell the CLI how to connect to the dat
}
```
-Note that the Sequelize CLI assumes mysql by default. If you're using another dialect, you need to change the content of the `"dialect"` option.
+Note that the Sequelize CLI assumes MySQL by default. If you're using another dialect, you need to change the content of the `"dialect"` option.
Now edit this file and set correct database credentials and dialect. The keys of the objects (e.g. "development") are used on `model/index.js` for matching `process.env.NODE_ENV` (When undefined, "development" is a default value).
-Sequelize will use the default connection port for each dialect (for example, for postgres, it is port 5432). If you need to specify a different port, use the `"port"` field (it is not present by default in `config/config.js` but you can simply add it).
+Sequelize will use the default connection port for each dialect (for example, for Postgres, it is port 5432). If you need to specify a different port, use the `"port"` field (it is not present by default in `config/config.js` but you can simply add it).
-**Note:** _If your database doesn't exist yet, you can just call `db:create` command. With proper access it will create that database for you._
+**Note:** _If your database doesn't exist yet, you can just call `db:create` command. With proper access, it will create that database for you._
## Creating the first Model (and Migration)
-Once you have properly configured CLI config file you are ready to create your first migration. It's as simple as executing a simple command.
+Once you have properly configured the CLI config file you are ready to create your first migration. It's as simple as executing a simple command.
We will use `model:generate` command. This command requires two options:
@@ -98,9 +108,9 @@ yarn sequelize-cli model:generate --name User --attributes firstName:string,last
This will:
- Create a model file `user` in `models` folder;
-- Create a migration file with name like `XXXXXXXXXXXXXX-create-user.js` in `migrations` folder.
+- Create a migration file with a name like `XXXXXXXXXXXXXX-create-user.js` in the `migrations` folder.
-**Note:** _Sequelize will only use Model files, it's the table representation. On the other hand, the migration file is a change in that model or more specifically that table, used by CLI. Treat migrations like a commit or a log for some change in database._
+**Note:** _Sequelize will only use Model files, it's the table representation. On the other hand, the migration file is a change in that model or more specifically that table, used by CLI. Treat migrations like a commit or a log for some change in the database._
## Writing a migration
@@ -126,15 +136,17 @@ npx sequelize-cli migration:generate --name migration-example
yarn sequelize-cli migration:generate --name migration-example
```
-The passed `queryInterface` object can be used to modify the database. The `Sequelize` object stores the available data types such as `STRING` or `INTEGER`. Function `up` or `down` should return a `Promise`. Let's look at an example:
+The passed `queryInterface` object can be used to modify the database. The `Sequelize` object stores the available data types such as `STRING` or `INTEGER`. The function `up` or `down` should return a `Promise`. Let's look at an example:
```js
+const { DataTypes } = require('@sequelize/core');
+
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Person', {
- name: Sequelize.DataTypes.STRING,
+ name: DataTypes.STRING,
isBetaMember: {
- type: Sequelize.DataTypes.BOOLEAN,
+ type: DataTypes.BOOLEAN,
defaultValue: false,
allowNull: false
}
@@ -146,27 +158,30 @@ module.exports = {
};
```
-The following is an example of a migration that performs two changes in the database, using an automatically-managed transaction to ensure that all instructions are successfully executed or rolled back in case of failure:
+The following is an example of a migration that performs two changes in the database,
+using an automatically managed transaction to ensure that all instructions are successfully executed or rolled back in case of failure:
```js
+const { DataTypes } = require('@sequelize/core');
+
module.exports = {
up: (queryInterface, Sequelize) => {
- return queryInterface.sequelize.transaction(t => {
+ return queryInterface.sequelize.transaction(transaction => {
return Promise.all([
queryInterface.addColumn('Person', 'petName', {
- type: Sequelize.DataTypes.STRING
- }, { transaction: t }),
+ type: DataTypes.STRING
+ }, { transaction }),
queryInterface.addColumn('Person', 'favoriteColor', {
- type: Sequelize.DataTypes.STRING,
- }, { transaction: t })
+ type: DataTypes.STRING,
+ }, { transaction })
]);
});
},
down: (queryInterface, Sequelize) => {
- return queryInterface.sequelize.transaction(t => {
+ return queryInterface.sequelize.transaction(transaction => {
return Promise.all([
- queryInterface.removeColumn('Person', 'petName', { transaction: t }),
- queryInterface.removeColumn('Person', 'favoriteColor', { transaction: t })
+ queryInterface.removeColumn('Person', 'petName', { transaction }),
+ queryInterface.removeColumn('Person', 'favoriteColor', { transaction })
]);
});
}
@@ -176,17 +191,19 @@ module.exports = {
The next example is of a migration that has a foreign key. You can use references to specify a foreign key:
```js
+const { DataTypes } = require('@sequelize/core');
+
module.exports = {
- up: (queryInterface, Sequelize) => {
+ up: (queryInterface) => {
return queryInterface.createTable('Person', {
- name: Sequelize.DataTypes.STRING,
+ name: DataTypes.STRING,
isBetaMember: {
- type: Sequelize.DataTypes.BOOLEAN,
+ type: DataTypes.BOOLEAN,
defaultValue: false,
allowNull: false
},
userId: {
- type: Sequelize.DataTypes.INTEGER,
+ type: DataTypes.INTEGER,
references: {
model: {
tableName: 'users',
@@ -204,18 +221,20 @@ module.exports = {
}
```
-The next example is of a migration that uses async/await where you create an unique index on a new column, with a manually-managed transaction:
+The next example is of a migration that uses async/await where you create a unique index on a new column, with a manually-managed transaction:
```js
+const { DataTypes } = require('@sequelize/core');
+
module.exports = {
- async up(queryInterface, Sequelize) {
- const transaction = await queryInterface.sequelize.transaction();
+ async up(queryInterface) {
+ const transaction = await queryInterface.sequelize.startUnmanagedTransaction();
try {
await queryInterface.addColumn(
'Person',
'petName',
{
- type: Sequelize.DataTypes.STRING,
+ type: DataTypes.STRING,
},
{ transaction }
);
@@ -234,8 +253,8 @@ module.exports = {
throw err;
}
},
- async down(queryInterface, Sequelize) {
- const transaction = await queryInterface.sequelize.transaction();
+ async down(queryInterface) {
+ const transaction = await queryInterface.sequelize.startUnmanagedTransaction();
try {
await queryInterface.removeColumn('Person', 'petName', { transaction });
await transaction.commit();
@@ -247,15 +266,17 @@ module.exports = {
};
```
-The next example is of a migration that creates an unique index composed of multiple fields with a condition, which allows a relation to exist multiple times but only one can satisfy the condition:
+The next example is of a migration that creates a unique index composed of multiple fields with a condition, which allows a relation to exist multiple times but only one can satisfy the condition:
```js
+const { DataTypes } = require('@sequelize/core');
+
module.exports = {
- up: (queryInterface, Sequelize) => {
+ up: (queryInterface) => {
queryInterface.createTable('Person', {
- name: Sequelize.DataTypes.STRING,
+ name: DataTypes.STRING,
bool: {
- type: Sequelize.DataTypes.BOOLEAN,
+ type: DataTypes.BOOLEAN,
defaultValue: false
}
}).then((queryInterface, Sequelize) => {
@@ -269,7 +290,7 @@ module.exports = {
);
});
},
- down: (queryInterface, Sequelize) => {
+ down: (queryInterface) => {
return queryInterface.dropTable('Person');
}
}
@@ -288,13 +309,13 @@ yarn sequelize-cli db:migrate
This command will execute these steps:
-- Will ensure a table called `SequelizeMeta` in database. This table is used to record which migrations have run on the current database
-- Start looking for any migration files which haven't run yet. This is possible by checking `SequelizeMeta` table. In this case it will run `XXXXXXXXXXXXXX-create-user.js` migration, which we created in last step.
+- Will ensure a table called `SequelizeMeta` in the database. This table is used to record which migrations have run on the current database
+- Start looking for any migration files that haven't run yet. This is possible by checking `SequelizeMeta` table. In this case, it will run `XXXXXXXXXXXXXX-create-user.js` migration, which we created in the last step.
- Creates a table called `Users` with all columns as specified in its migration file.
## Undoing Migrations
-Now our table has been created and saved in the database. With migration you can revert to old state by just running a command.
+Now our table has been created and saved in the database. With migration, you can revert to the old state by just running a command.
You can use `db:migrate:undo`, this command will revert the most recent migration.
@@ -305,7 +326,7 @@ npx sequelize-cli db:migrate:undo
yarn sequelize-cli db:migrate:undo
```
-You can revert back to the initial state by undoing all migrations with the `db:migrate:undo:all` command. You can also revert back to a specific migration by passing its name with the `--to` option.
+You can revert to the initial state by undoing all migrations with the `db:migrate:undo:all` command. You can also revert to a specific migration by passing its name with the `--to` option.
```bash
# using npm
@@ -318,9 +339,9 @@ yarn sequelize-cli db:migrate:undo:all --to XXXXXXXXXXXXXX-create-posts.js
Suppose we want to insert some data into a few tables by default. If we follow up on the previous example we can consider creating a demo user for the `User` table.
-To manage all data migrations you can use seeders. Seed files are some change in data that can be used to populate database tables with sample or test data.
+To manage all data migrations you can use seeders. Seed files are some changes in data that can be used to populate database tables with sample or test data.
-Let's create a seed file which will add a demo user to our `User` table.
+Let's create a seed file that will add a demo user to our `User` table.
```bash
# using npm
@@ -329,9 +350,9 @@ npx sequelize-cli seed:generate --name demo-user
yarn sequelize-cli seed:generate --name demo-user
```
-This command will create a seed file in `seeders` folder. File name will look something like `XXXXXXXXXXXXXX-demo-user.js`. It follows the same `up / down` semantics as the migration files.
+This command will create a seed file in `seeders` folder. The file name will look something like `XXXXXXXXXXXXXX-demo-user.js`. It follows the same `up / down` semantics as the migration files.
-Now we should edit this file to insert demo user to `User` table.
+Now we should edit this file to insert the demo user to `User` table.
```js
module.exports = {
@@ -431,18 +452,18 @@ module.exports = {
};
```
-With this config you are telling the CLI to:
+With this config, you are telling the CLI to:
- Use `config/database.json` file for config settings;
- Use `db/models` as models folder;
- Use `db/seeders` as seeders folder;
-- Use `db/migrations` as migrations folder.
+- Use `db/migrations` as the migrations folder.
### Dynamic configuration
-The configuration file is by default a JSON file called `config.json`. But sometimes you need a dynamic configuration, for example to access environment variables or execute some other code to determine the configuration.
+The configuration file is by default a JSON file called `config.json`. But sometimes you need a dynamic configuration, for example, to access environment variables or execute some other code to determine the configuration.
-Thankfully, the Sequelize CLI can read from both `.json` and `.js` files. This can be setup with `.sequelizerc` file. You just have to provide the path to your `.js` file as the `config` option of your exported object:
+Thankfully, the Sequelize CLI can read from both `.json` and `.js` files. This can be set up with `.sequelizerc` file. You just have to provide the path to your `.js` file as the `config` option of your exported object:
```js
const path = require('path');
@@ -543,7 +564,7 @@ There are three types of storage that you can use: `sequelize`, `json`, and `non
#### Migration Storage
-By default the CLI will create a table in your database called `SequelizeMeta` containing an entry for each executed migration. To change this behavior, there are three options you can add to the configuration file. Using `migrationStorage`, you can choose the type of storage to be used for migrations. If you choose `json`, you can specify the path of the file using `migrationStoragePath` or the CLI will write to the file `sequelize-meta.json`. If you want to keep the information in the database, using `sequelize`, but want to use a different table, you can change the table name using `migrationStorageTableName`. Also you can define a different schema for the `SequelizeMeta` table by providing the `migrationStorageTableSchema` property.
+By default, the CLI will create a table in your database called `SequelizeMeta` containing an entry for each executed migration. To change this behavior, there are three options you can add to the configuration file. Using `migrationStorage`, you can choose the type of storage to be used for migrations. If you choose `json`, you can specify the path of the file using `migrationStoragePath` or the CLI will write to the file `sequelize-meta.json`. If you want to keep the information in the database, using `sequelize`, but want to use a different table, you can change the table name using `migrationStorageTableName`. Also, you can define a different schema for the `SequelizeMeta` table by providing the `migrationStorageTableSchema` property.
```json
{
@@ -573,7 +594,7 @@ By default the CLI will create a table in your database called `SequelizeMeta` c
#### Seed Storage
-By default the CLI will not save any seed that is executed. If you choose to change this behavior (!), you can use `seederStorage` in the configuration file to change the storage type. If you choose `json`, you can specify the path of the file using `seederStoragePath` or the CLI will write to the file `sequelize-data.json`. If you want to keep the information in the database, using `sequelize`, you can specify the table name using `seederStorageTableName`, or it will default to `SequelizeData`.
+By default, the CLI will not save any seed that is executed. If you choose to change this behavior (!), you can use `seederStorage` in the configuration file to change the storage type. If you choose `json`, you can specify the path of the file using `seederStoragePath` or the CLI will write to the file `sequelize-data.json`. If you want to keep the information in the database, using `sequelize`, you can specify the table name using `seederStorageTableName`, or it will default to `SequelizeData`.
```json
{
diff --git a/docs/core-concepts/assocs.md b/docs/core-concepts/assocs.md
deleted file mode 100644
index ef6a9983..00000000
--- a/docs/core-concepts/assocs.md
+++ /dev/null
@@ -1,802 +0,0 @@
----
-sidebar_position: 8
-title: Associations
----
-
-Sequelize supports the standard associations: [One-To-One](https://en.wikipedia.org/wiki/One-to-one_%28data_model%29), [One-To-Many](https://en.wikipedia.org/wiki/One-to-many_%28data_model%29) and [Many-To-Many](https://en.wikipedia.org/wiki/Many-to-many_%28data_model%29).
-
-To do this, Sequelize provides **four** types of associations that should be combined to create them:
-
-* The `HasOne` association
-* The `BelongsTo` association
-* The `HasMany` association
-* The `BelongsToMany` association
-
-The guide will start explaining how to define these four types of associations, and then will follow up to explain how to combine those to define the three standard association types ([One-To-One](https://en.wikipedia.org/wiki/One-to-one_%28data_model%29), [One-To-Many](https://en.wikipedia.org/wiki/One-to-many_%28data_model%29) and [Many-To-Many](https://en.wikipedia.org/wiki/Many-to-many_%28data_model%29)).
-
-## Defining the Sequelize associations
-
-The four association types are defined in a very similar way. Let's say we have two models, `A` and `B`. Telling Sequelize that you want an association between the two needs just a function call:
-
-```js
-const A = sequelize.define('A', /* ... */);
-const B = sequelize.define('B', /* ... */);
-
-A.hasOne(B); // A HasOne B
-A.belongsTo(B); // A BelongsTo B
-A.hasMany(B); // A HasMany B
-A.belongsToMany(B, { through: 'C' }); // A BelongsToMany B through the junction table C
-```
-
-They all accept an options object as a second parameter (optional for the first three, mandatory for `belongsToMany` containing at least the `through` property):
-
-```js
-A.hasOne(B, { /* options */ });
-A.belongsTo(B, { /* options */ });
-A.hasMany(B, { /* options */ });
-A.belongsToMany(B, { through: 'C', /* options */ });
-```
-
-The order in which the association is defined is relevant. In other words, the order matters, for the four cases. In all examples above, `A` is called the **source** model and `B` is called the **target** model. This terminology is important.
-
-The `A.hasOne(B)` association means that a One-To-One relationship exists between `A` and `B`, with the foreign key being defined in the target model (`B`).
-
-The `A.belongsTo(B)` association means that a One-To-One relationship exists between `A` and `B`, with the foreign key being defined in the source model (`A`).
-
-The `A.hasMany(B)` association means that a One-To-Many relationship exists between `A` and `B`, with the foreign key being defined in the target model (`B`).
-
-These three calls will cause Sequelize to automatically add foreign keys to the appropriate models (unless they are already present).
-
-The `A.belongsToMany(B, { through: 'C' })` association means that a Many-To-Many relationship exists between `A` and `B`, using table `C` as [junction table](https://en.wikipedia.org/wiki/Associative_entity), which will have the foreign keys (`aId` and `bId`, for example). Sequelize will automatically create this model `C` (unless it already exists) and define the appropriate foreign keys on it.
-
-*Note: In the examples above for `belongsToMany`, a string (`'C'`) was passed to the through option. In this case, Sequelize automatically generates a model with this name. However, you can also pass a model directly, if you have already defined it.*
-
-These are the main ideas involved in each type of association. However, these relationships are often used in pairs, in order to enable better usage with Sequelize. This will be seen later on.
-
-## Creating the standard relationships
-
-As mentioned, usually the Sequelize associations are defined in pairs. In summary:
-
-* To create a **One-To-One** relationship, the `hasOne` and `belongsTo` associations are used together;
-* To create a **One-To-Many** relationship, the `hasMany` and `belongsTo` associations are used together;
-* To create a **Many-To-Many** relationship, two `belongsToMany` calls are used together.
- * Note: there is also a *Super Many-To-Many* relationship, which uses six associations at once, and will be discussed in the [Advanced Many-to-Many relationships guide](../advanced-association-concepts/advanced-many-to-many.md).
-
-This will all be seen in detail next. The advantages of using these pairs instead of one single association will be discussed in the end of this chapter.
-
-## One-To-One relationships
-
-### Philosophy
-
-Before digging into the aspects of using Sequelize, it is useful to take a step back to consider what happens with a One-To-One relationship.
-
-Let's say we have two models, `Foo` and `Bar`. We want to establish a One-To-One relationship between Foo and Bar. We know that in a relational database, this will be done by establishing a foreign key in one of the tables. So in this case, a very relevant question is: in which table do we want this foreign key to be? In other words, do we want `Foo` to have a `barId` column, or should `Bar` have a `fooId` column instead?
-
-In principle, both options are a valid way to establish a One-To-One relationship between Foo and Bar. However, when we say something like *"there is a One-To-One relationship between Foo and Bar"*, it is unclear whether or not the relationship is *mandatory* or optional. In other words, can a Foo exist without a Bar? Can a Bar exist without a Foo? The answers to these questions help figuring out where we want the foreign key column to be.
-
-### Goal
-
-For the rest of this example, let's assume that we have two models, `Foo` and `Bar`. We want to setup a One-To-One relationship between them such that `Bar` gets a `fooId` column.
-
-### Implementation
-
-The main setup to achieve the goal is as follows:
-
-```js
-Foo.hasOne(Bar);
-Bar.belongsTo(Foo);
-```
-
-Since no option was passed, Sequelize will infer what to do from the names of the models. In this case, Sequelize knows that a `fooId` column must be added to `Bar`.
-
-This way, calling `Bar.sync()` after the above will yield the following SQL (on PostgreSQL, for example):
-
-```sql
-CREATE TABLE IF NOT EXISTS "foos" (
- /* ... */
-);
-CREATE TABLE IF NOT EXISTS "bars" (
- /* ... */
- "fooId" INTEGER REFERENCES "foos" ("id") ON DELETE SET NULL ON UPDATE CASCADE
- /* ... */
-);
-```
-
-### Options
-
-Various options can be passed as a second parameter of the association call.
-
-#### `onDelete` and `onUpdate`
-
-For example, to configure the `ON DELETE` and `ON UPDATE` behaviors, you can do:
-
-```js
-Foo.hasOne(Bar, {
- onDelete: 'RESTRICT',
- onUpdate: 'RESTRICT'
-});
-Bar.belongsTo(Foo);
-```
-
-The possible choices are `RESTRICT`, `CASCADE`, `NO ACTION`, `SET DEFAULT` and `SET NULL`.
-
-The defaults for the One-To-One associations is `SET NULL` for `ON DELETE` and `CASCADE` for `ON UPDATE`.
-
-#### Customizing the foreign key
-
-Both the `hasOne` and `belongsTo` calls shown above will infer that the foreign key to be created should be called `fooId`. To use a different name, such as `myFooId`:
-
-```js
-// Option 1
-Foo.hasOne(Bar, {
- foreignKey: 'myFooId'
-});
-Bar.belongsTo(Foo);
-
-// Option 2
-Foo.hasOne(Bar, {
- foreignKey: {
- name: 'myFooId'
- }
-});
-Bar.belongsTo(Foo);
-
-// Option 3
-Foo.hasOne(Bar);
-Bar.belongsTo(Foo, {
- foreignKey: 'myFooId'
-});
-
-// Option 4
-Foo.hasOne(Bar);
-Bar.belongsTo(Foo, {
- foreignKey: {
- name: 'myFooId'
- }
-});
-```
-
-As shown above, the `foreignKey` option accepts a string or an object. When receiving an object, this object will be used as the definition for the column just like it would do in a standard `sequelize.define` call. Therefore, specifying options such as `type`, `allowNull`, `defaultValue`, etc, just work.
-
-For example, to use `UUID` as the foreign key data type instead of the default (`INTEGER`), you can simply do:
-
-```js
-const { DataTypes } = require('@sequelize/core');
-
-Foo.hasOne(Bar, {
- foreignKey: {
- // name: 'myFooId'
- type: DataTypes.UUID
- }
-});
-Bar.belongsTo(Foo);
-```
-
-#### Mandatory versus optional associations
-
-By default, the association is considered optional. In other words, in our example, the `fooId` is allowed to be null, meaning that one Bar can exist without a Foo. Changing this is just a matter of specifying `allowNull: false` in the foreign key options:
-
-```js
-Foo.hasOne(Bar, {
- foreignKey: {
- allowNull: false
- }
-});
-// "fooId" INTEGER NOT NULL REFERENCES "foos" ("id") ON DELETE RESTRICT ON UPDATE RESTRICT
-```
-
-## One-To-Many relationships
-
-### Philosophy
-
-One-To-Many associations are connecting one source with multiple targets, while all these targets are connected only with this single source.
-
-This means that, unlike the One-To-One association, in which we had to choose where the foreign key would be placed, there is only one option in One-To-Many associations. For example, if one Foo has many Bars (and this way each Bar belongs to one Foo), then the only sensible implementation is to have a `fooId` column in the `Bar` table. The opposite is impossible, since one Foo has many Bars.
-
-### Goal
-
-In this example, we have the models `Team` and `Player`. We want to tell Sequelize that there is a One-To-Many relationship between them, meaning that one Team has many Players, while each Player belongs to a single Team.
-
-### Implementation
-
-The main way to do this is as follows:
-
-```js
-Team.hasMany(Player);
-Player.belongsTo(Team);
-```
-
-Again, as mentioned, the main way to do it used a pair of Sequelize associations (`hasMany` and `belongsTo`).
-
-For example, in PostgreSQL, the above setup will yield the following SQL upon `sync()`:
-
-```sql
-CREATE TABLE IF NOT EXISTS "Teams" (
- /* ... */
-);
-CREATE TABLE IF NOT EXISTS "Players" (
- /* ... */
- "TeamId" INTEGER REFERENCES "Teams" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
- /* ... */
-);
-```
-
-### Options
-
-The options to be applied in this case are the same from the One-To-One case. For example, to change the name of the foreign key and make sure that the relationship is mandatory, we can do:
-
-```js
-Team.hasMany(Player, {
- foreignKey: 'clubId'
-});
-Player.belongsTo(Team);
-```
-
-Like One-To-One relationships, `ON DELETE` defaults to `SET NULL` and `ON UPDATE` defaults to `CASCADE`.
-
-## Many-To-Many relationships
-
-### Philosophy
-
-Many-To-Many associations connect one source with multiple targets, while all these targets can in turn be connected to other sources beyond the first.
-
-This cannot be represented by adding one foreign key to one of the tables, like the other relationships did. Instead, the concept of a [Junction Model](https://en.wikipedia.org/wiki/Associative_entity) is used. This will be an extra model (and extra table in the database) which will have two foreign key columns and will keep track of the associations. The junction table is also sometimes called *join table* or *through table*.
-
-### Goal
-
-For this example, we will consider the models `Movie` and `Actor`. One actor may have participated in many movies, and one movie had many actors involved with its production. The junction table that will keep track of the associations will be called `ActorMovies`, which will contain the foreign keys `movieId` and `actorId`.
-
-### Implementation
-
-The main way to do this in Sequelize is as follows:
-
-```js
-const Movie = sequelize.define('Movie', { name: DataTypes.STRING });
-const Actor = sequelize.define('Actor', { name: DataTypes.STRING });
-Movie.belongsToMany(Actor, { through: 'ActorMovies' });
-Actor.belongsToMany(Movie, { through: 'ActorMovies' });
-```
-
-Since a string was given in the `through` option of the `belongsToMany` call, Sequelize will automatically create the `ActorMovies` model which will act as the junction model. For example, in PostgreSQL:
-
-```sql
-CREATE TABLE IF NOT EXISTS "ActorMovies" (
- "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL,
- "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
- "MovieId" INTEGER REFERENCES "Movies" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
- "ActorId" INTEGER REFERENCES "Actors" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
- PRIMARY KEY ("MovieId","ActorId")
-);
-```
-
-Instead of a string, passing a model directly is also supported, and in that case the given model will be used as the junction model (and no model will be created automatically). For example:
-
-```js
-const Movie = sequelize.define('Movie', { name: DataTypes.STRING });
-const Actor = sequelize.define('Actor', { name: DataTypes.STRING });
-const ActorMovies = sequelize.define('ActorMovies', {
- MovieId: {
- type: DataTypes.INTEGER,
- references: {
- model: Movie, // 'Movies' would also work
- key: 'id'
- }
- },
- ActorId: {
- type: DataTypes.INTEGER,
- references: {
- model: Actor, // 'Actors' would also work
- key: 'id'
- }
- }
-});
-Movie.belongsToMany(Actor, { through: ActorMovies });
-Actor.belongsToMany(Movie, { through: ActorMovies });
-```
-
-The above yields the following SQL in PostgreSQL, which is equivalent to the one shown above:
-
-```sql
-CREATE TABLE IF NOT EXISTS "ActorMovies" (
- "MovieId" INTEGER NOT NULL REFERENCES "Movies" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
- "ActorId" INTEGER NOT NULL REFERENCES "Actors" ("id") ON DELETE RESTRICT ON UPDATE CASCADE,
- "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL,
- "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
- UNIQUE ("MovieId", "ActorId"), -- Note: Sequelize generated this UNIQUE constraint but
- PRIMARY KEY ("MovieId","ActorId") -- it is irrelevant since it's also a PRIMARY KEY
-);
-```
-
-### Options
-
-Unlike One-To-One and One-To-Many relationships, the defaults for both `ON UPDATE` and `ON DELETE` are `CASCADE` for Many-To-Many relationships.
-
-Belongs-To-Many creates a unique key on through model. This unique key name can be overridden using **uniqueKey** option. To prevent creating this unique key, use the ***unique: false*** option.
-
-```js
-Project.belongsToMany(User, { through: UserProjects, uniqueKey: 'my_custom_unique' })
-```
-
-## Basics of queries involving associations
-
-With the basics of defining associations covered, we can look at queries involving associations. The most common queries on this matter are the *read* queries (i.e. SELECTs). Later on, other types of queries will be shown.
-
-In order to study this, we will consider an example in which we have Ships and Captains, and a one-to-one relationship between them. We will allow null on foreign keys (the default), meaning that a Ship can exist without a Captain and vice-versa.
-
-```js
-// This is the setup of our models for the examples below
-const Ship = sequelize.define('ship', {
- name: DataTypes.TEXT,
- crewCapacity: DataTypes.INTEGER,
- amountOfSails: DataTypes.INTEGER
-}, { timestamps: false });
-const Captain = sequelize.define('captain', {
- name: DataTypes.TEXT,
- skillLevel: {
- type: DataTypes.INTEGER,
- validate: { min: 1, max: 10 }
- }
-}, { timestamps: false });
-Captain.hasOne(Ship);
-Ship.belongsTo(Captain);
-```
-
-### Fetching associations - Eager Loading vs Lazy Loading
-
-The concepts of Eager Loading and Lazy Loading are fundamental to understand how fetching associations work in Sequelize. Lazy Loading refers to the technique of fetching the associated data only when you really want it; Eager Loading, on the other hand, refers to the technique of fetching everything at once, since the beginning, with a larger query.
-
-#### Lazy Loading example
-
-```js
-const awesomeCaptain = await Captain.findOne({
- where: {
- name: "Jack Sparrow"
- }
-});
-// Do stuff with the fetched captain
-console.log('Name:', awesomeCaptain.name);
-console.log('Skill Level:', awesomeCaptain.skillLevel);
-// Now we want information about his ship!
-const hisShip = await awesomeCaptain.getShip();
-// Do stuff with the ship
-console.log('Ship Name:', hisShip.name);
-console.log('Amount of Sails:', hisShip.amountOfSails);
-```
-
-Observe that in the example above, we made two queries, only fetching the associated ship when we wanted to use it. This can be especially useful if we may or may not need the ship, perhaps we want to fetch it conditionally, only in a few cases; this way we can save time and memory by only fetching it when necessary.
-
-Note: the `getShip()` instance method used above is one of the methods Sequelize automatically adds to `Captain` instances. There are others. You will learn more about them later in this guide.
-
-#### Eager Loading Example
-
-```js
-const awesomeCaptain = await Captain.findOne({
- where: {
- name: "Jack Sparrow"
- },
- include: Ship
-});
-// Now the ship comes with it
-console.log('Name:', awesomeCaptain.name);
-console.log('Skill Level:', awesomeCaptain.skillLevel);
-console.log('Ship Name:', awesomeCaptain.ship.name);
-console.log('Amount of Sails:', awesomeCaptain.ship.amountOfSails);
-```
-
-As shown above, Eager Loading is performed in Sequelize by using the `include` option. Observe that here only one query was performed to the database (which brings the associated data along with the instance).
-
-This was just a quick introduction to Eager Loading in Sequelize. There is a lot more to it, which you can learn at [the dedicated guide on Eager Loading](../advanced-association-concepts/eager-loading.md).
-
-### Creating, updating and deleting
-
-The above showed the basics on queries for fetching data involving associations. For creating, updating and deleting, you can either:
-
-* Use the standard model queries directly:
-
- ```js
- // Example: creating an associated model using the standard methods
- Bar.create({
- name: 'My Bar',
- fooId: 5
- });
- // This creates a Bar belonging to the Foo of ID 5 (since fooId is
- // a regular column, after all). Nothing very clever going on here.
- ```
-
-* Or use the *[special methods/mixins](#special-methodsmixins-added-to-instances)* available for associated models, which are explained later on this page.
-
-**Note:** The [`save()` instance method](pathname:///api/v7/classes/Model.html#save) is not aware of associations. In other words, if you change a value from a *child* object that was eager loaded along a *parent* object, calling `save()` on the parent will completely ignore the change that happened on the child.
-
-## Association Aliases & Custom Foreign Keys
-
-In all the above examples, Sequelize automatically defined the foreign key names. For example, in the Ship and Captain example, Sequelize automatically defined a `captainId` field on the Ship model. However, it is easy to specify a custom foreign key.
-
-Let's consider the models Ship and Captain in a simplified form, just to focus on the current topic, as shown below (less fields):
-
-```js
-const Ship = sequelize.define('ship', { name: DataTypes.TEXT }, { timestamps: false });
-const Captain = sequelize.define('captain', { name: DataTypes.TEXT }, { timestamps: false });
-```
-
-There are three ways to specify a different name for the foreign key:
-
-* By providing the foreign key name directly
-* By defining an Alias
-* By doing both things
-
-### Recap: the default setup
-
-By using simply `Ship.belongsTo(Captain)`, sequelize will generate the foreign key name automatically:
-
-```js
-Ship.belongsTo(Captain); // This creates the `captainId` foreign key in Ship.
-
-// Eager Loading is done by passing the model to `include`:
-console.log((await Ship.findAll({ include: Captain })).toJSON());
-// Or by providing the associated model name:
-console.log((await Ship.findAll({ include: 'captain' })).toJSON());
-
-// Also, instances obtain a `getCaptain()` method for Lazy Loading:
-const ship = Ship.findOne();
-console.log((await ship.getCaptain()).toJSON());
-```
-
-### Providing the foreign key name directly
-
-The foreign key name can be provided directly with an option in the association definition, as follows:
-
-```js
-Ship.belongsTo(Captain, { foreignKey: 'bossId' }); // This creates the `bossId` foreign key in Ship.
-
-// Eager Loading is done by passing the model to `include`:
-console.log((await Ship.findAll({ include: Captain })).toJSON());
-// Or by providing the associated model name:
-console.log((await Ship.findAll({ include: 'Captain' })).toJSON());
-
-// Also, instances obtain a `getCaptain()` method for Lazy Loading:
-const ship = Ship.findOne();
-console.log((await ship.getCaptain()).toJSON());
-```
-
-### Defining an Alias
-
-Defining an Alias is more powerful than simply specifying a custom name for the foreign key. This is better understood with an example:
-
-
-
-```js
-Ship.belongsTo(Captain, { as: 'leader' }); // This creates the `leaderId` foreign key in Ship.
-
-// Eager Loading no longer works by passing the model to `include`:
-console.log((await Ship.findAll({ include: Captain })).toJSON()); // Throws an error
-// Instead, you have to pass the alias:
-console.log((await Ship.findAll({ include: 'leader' })).toJSON());
-// Or you can pass an object specifying the model and alias:
-console.log((await Ship.findAll({
- include: {
- model: Captain,
- as: 'leader'
- }
-})).toJSON());
-
-// Also, instances obtain a `getLeader()` method for Lazy Loading:
-const ship = Ship.findOne();
-console.log((await ship.getLeader()).toJSON());
-```
-
-Aliases are especially useful when you need to define two different associations between the same models. For example, if we have the models `Mail` and `Person`, we may want to associate them twice, to represent the `sender` and `receiver` of the Mail. In this case we must use an alias for each association, since otherwise a call like `mail.getPerson()` would be ambiguous. With the `sender` and `receiver` aliases, we would have the two methods available and working: `mail.getSender()` and `mail.getReceiver()`, both of them returning a `Promise`.
-
-When defining an alias for a `hasOne` or `belongsTo` association, you should use the singular form of a word (such as `leader`, in the example above). On the other hand, when defining an alias for `hasMany` and `belongsToMany`, you should use the plural form. Defining aliases for Many-to-Many relationships (with `belongsToMany`) is covered in the [Advanced Many-to-Many Associations guide](../advanced-association-concepts/advanced-many-to-many.md).
-
-### Doing both things
-
-We can define and alias and also directly define the foreign key:
-
-```js
-Ship.belongsTo(Captain, { as: 'leader', foreignKey: 'bossId' }); // This creates the `bossId` foreign key in Ship.
-
-// Since an alias was defined, eager Loading doesn't work by simply passing the model to `include`:
-console.log((await Ship.findAll({ include: Captain })).toJSON()); // Throws an error
-// Instead, you have to pass the alias:
-console.log((await Ship.findAll({ include: 'leader' })).toJSON());
-// Or you can pass an object specifying the model and alias:
-console.log((await Ship.findAll({
- include: {
- model: Captain,
- as: 'leader'
- }
-})).toJSON());
-
-// Also, instances obtain a `getLeader()` method for Lazy Loading:
-const ship = Ship.findOne();
-console.log((await ship.getLeader()).toJSON());
-```
-
-## Special methods/mixins added to instances
-
-When an association is defined between two models, the instances of those models gain special methods to interact with their associated counterparts.
-
-For example, if we have two models, `Foo` and `Bar`, and they are associated, their instances will have the following methods/mixins available, depending on the association type:
-
-### `Foo.hasOne(Bar)`
-
-* `fooInstance.getBar()`
-* `fooInstance.setBar()`
-* `fooInstance.createBar()`
-
-Example:
-
-```js
-const foo = await Foo.create({ name: 'the-foo' });
-const bar1 = await Bar.create({ name: 'some-bar' });
-const bar2 = await Bar.create({ name: 'another-bar' });
-console.log(await foo.getBar()); // null
-await foo.setBar(bar1);
-console.log((await foo.getBar()).name); // 'some-bar'
-await foo.createBar({ name: 'yet-another-bar' });
-const newlyAssociatedBar = await foo.getBar();
-console.log(newlyAssociatedBar.name); // 'yet-another-bar'
-await foo.setBar(null); // Un-associate
-console.log(await foo.getBar()); // null
-```
-
-### `Foo.belongsTo(Bar)`
-
-The same ones from `Foo.hasOne(Bar)`:
-
-* `fooInstance.getBar()`
-* `fooInstance.setBar()`
-* `fooInstance.createBar()`
-
-### `Foo.hasMany(Bar)`
-
-* `fooInstance.getBars()`
-* `fooInstance.countBars()`
-* `fooInstance.hasBar()`
-* `fooInstance.hasBars()`
-* `fooInstance.setBars()`
-* `fooInstance.addBar()`
-* `fooInstance.addBars()`
-* `fooInstance.removeBar()`
-* `fooInstance.removeBars()`
-* `fooInstance.createBar()`
-
-Example:
-
-```js
-const foo = await Foo.create({ name: 'the-foo' });
-const bar1 = await Bar.create({ name: 'some-bar' });
-const bar2 = await Bar.create({ name: 'another-bar' });
-console.log(await foo.getBars()); // []
-console.log(await foo.countBars()); // 0
-console.log(await foo.hasBar(bar1)); // false
-await foo.addBars([bar1, bar2]);
-console.log(await foo.countBars()); // 2
-await foo.addBar(bar1);
-console.log(await foo.countBars()); // 2
-console.log(await foo.hasBar(bar1)); // true
-await foo.removeBar(bar2);
-console.log(await foo.countBars()); // 1
-await foo.createBar({ name: 'yet-another-bar' });
-console.log(await foo.countBars()); // 2
-await foo.setBars([]); // Un-associate all previously associated bars
-console.log(await foo.countBars()); // 0
-```
-
-The getter method accepts options just like the usual finder methods (such as `findAll`):
-
-```js
-const easyTasks = await project.getTasks({
- where: {
- difficulty: {
- [Op.lte]: 5
- }
- }
-});
-const taskTitles = (await project.getTasks({
- attributes: ['title'],
- raw: true
-})).map(task => task.title);
-```
-
-### `Foo.belongsToMany(Bar, { through: Baz })`
-
-The same ones from `Foo.hasMany(Bar)`:
-
-* `fooInstance.getBars()`
-* `fooInstance.countBars()`
-* `fooInstance.hasBar()`
-* `fooInstance.hasBars()`
-* `fooInstance.setBars()`
-* `fooInstance.addBar()`
-* `fooInstance.addBars()`
-* `fooInstance.removeBar()`
-* `fooInstance.removeBars()`
-* `fooInstance.createBar()`
-
-For belongsToMany relationships, by default `getBars()` will return all fields from the join table. Note that any `include` options will apply to the target `Bar` object, so trying to set options for the join table as you would when eager loading with `find` methods is not possible. To choose what attributes of the join table to include, `getBars()` supports a `joinTableAttributes` option that can be used similarly to setting `through.attributes` in an `include`. As an example, given Foo belongsToMany Bar, the following will both output results without join table fields:
-
-```js
-const foo = Foo.findByPk(id, {
- include: [{
- model: Bar,
- through: { attributes: [] }
- }]
-})
-console.log(foo.bars)
-
-const foo = Foo.findByPk(id)
-console.log(foo.getBars({ joinTableAttributes: [] }))
-```
-
-### Note: Method names
-
-As shown in the examples above, the names Sequelize gives to these special methods are formed by a prefix (e.g. `get`, `add`, `set`) concatenated with the model name (with the first letter in uppercase). When necessary, the plural is used, such as in `fooInstance.setBars()`. Again, irregular plurals are also handled automatically by Sequelize. For example, `Person` becomes `People` and `Hypothesis` becomes `Hypotheses`.
-
-If an alias was defined, it will be used instead of the model name to form the method names. For example:
-
-```js
-Task.hasOne(User, { as: 'Author' });
-```
-
-* `taskInstance.getAuthor()`
-* `taskInstance.setAuthor()`
-* `taskInstance.createAuthor()`
-
-## Why associations are defined in pairs?
-
-As mentioned earlier and shown in most examples above, usually associations in Sequelize are defined in pairs:
-
-* To create a **One-To-One** relationship, the `hasOne` and `belongsTo` associations are used together;
-* To create a **One-To-Many** relationship, the `hasMany` and `belongsTo` associations are used together;
-* To create a **Many-To-Many** relationship, two `belongsToMany` calls are used together.
-
-When a Sequelize association is defined between two models, only the *source* model *knows about it*. So, for example, when using `Foo.hasOne(Bar)` (so `Foo` is the source model and `Bar` is the target model), only `Foo` knows about the existence of this association. This is why in this case, as shown above, `Foo` instances gain the methods `getBar()`, `setBar()` and `createBar()`, while on the other hand `Bar` instances get nothing.
-
-Similarly, for `Foo.hasOne(Bar)`, since `Foo` knows about the relationship, we can perform eager loading as in `Foo.findOne({ include: Bar })`, but we can't do `Bar.findOne({ include: Foo })`.
-
-Therefore, to bring full power to Sequelize usage, we usually setup the relationship in pairs, so that both models get to *know about it*.
-
-Practical demonstration:
-
-* If we do not define the pair of associations, calling for example just `Foo.hasOne(Bar)`:
-
- ```js
- // This works...
- await Foo.findOne({ include: Bar });
-
- // But this throws an error:
- await Bar.findOne({ include: Foo });
- // SequelizeEagerLoadingError: foo is not associated to bar!
- ```
-
-* If we define the pair as recommended, i.e., both `Foo.hasOne(Bar)` and `Bar.belongsTo(Foo)`:
-
- ```js
- // This works!
- await Foo.findOne({ include: Bar });
-
- // This also works!
- await Bar.findOne({ include: Foo });
- ```
-
-## Multiple associations involving the same models
-
-In Sequelize, it is possible to define multiple associations between the same models. You just have to define different aliases for them:
-
-```js
-Team.hasOne(Game, { as: 'HomeTeam', foreignKey: 'homeTeamId' });
-Team.hasOne(Game, { as: 'AwayTeam', foreignKey: 'awayTeamId' });
-Game.belongsTo(Team);
-```
-
-## Creating associations referencing a field which is not the primary key
-
-In all the examples above, the associations were defined by referencing the primary keys of the involved models (in our case, their IDs). However, Sequelize allows you to define an association that uses another field, instead of the primary key field, to establish the association.
-
-This other field must have a unique constraint on it (otherwise, it wouldn't make sense).
-
-### For `belongsTo` relationships
-
-First, recall that the `A.belongsTo(B)` association places the foreign key in the *source model* (i.e., in `A`).
-
-Let's again use the example of Ships and Captains. Additionally, we will assume that Captain names are unique:
-
-```js
-const Ship = sequelize.define('ship', { name: DataTypes.TEXT }, { timestamps: false });
-const Captain = sequelize.define('captain', {
- name: { type: DataTypes.TEXT, unique: true }
-}, { timestamps: false });
-```
-
-This way, instead of keeping the `captainId` on our Ships, we could keep a `captainName` instead and use it as our association tracker. In other words, instead of referencing the `id` from the target model (Captain), our relationship will reference another column on the target model: the `name` column. To specify this, we have to define a *target key*. We will also have to specify a name for the foreign key itself:
-
-```js
-Ship.belongsTo(Captain, { targetKey: 'name', foreignKey: 'captainName' });
-// This creates a foreign key called `captainName` in the source model (Ship)
-// which references the `name` field from the target model (Captain).
-```
-
-Now we can do things like:
-
-```js
-await Captain.create({ name: "Jack Sparrow" });
-const ship = await Ship.create({ name: "Black Pearl", captainName: "Jack Sparrow" });
-console.log((await ship.getCaptain()).name); // "Jack Sparrow"
-```
-
-### For `hasOne` and `hasMany` relationships
-
-The exact same idea can be applied to the `hasOne` and `hasMany` associations, but instead of providing a `targetKey`, we provide a `sourceKey` when defining the association. This is because unlike `belongsTo`, the `hasOne` and `hasMany` associations keep the foreign key on the target model:
-
-```js
-const Foo = sequelize.define('foo', {
- name: { type: DataTypes.TEXT, unique: true }
-}, { timestamps: false });
-const Bar = sequelize.define('bar', {
- title: { type: DataTypes.TEXT, unique: true }
-}, { timestamps: false });
-const Baz = sequelize.define('baz', { summary: DataTypes.TEXT }, { timestamps: false });
-Foo.hasOne(Bar, { sourceKey: 'name', foreignKey: 'fooName' });
-Bar.hasMany(Baz, { sourceKey: 'title', foreignKey: 'barTitle' });
-// [...]
-await Bar.setFoo("Foo's Name Here");
-await Baz.addBar("Bar's Title Here");
-```
-
-### For `belongsToMany` relationships
-
-The same idea can also be applied to `belongsToMany` relationships. However, unlike the other situations, in which we have only one foreign key involved, the `belongsToMany` relationship involves two foreign keys which are kept on an extra table (the junction table).
-
-Consider the following setup:
-
-```js
-const Foo = sequelize.define('foo', {
- name: { type: DataTypes.TEXT, unique: true }
-}, { timestamps: false });
-const Bar = sequelize.define('bar', {
- title: { type: DataTypes.TEXT, unique: true }
-}, { timestamps: false });
-```
-
-There are four cases to consider:
-
-* We might want a many-to-many relationship using the default primary keys for both `Foo` and `Bar`:
-
-```js
-Foo.belongsToMany(Bar, { through: 'foo_bar' });
-// This creates a junction table `foo_bar` with fields `fooId` and `barId`
-```
-
-* We might want a many-to-many relationship using the default primary key for `Foo` but a different field for `Bar`:
-
-```js
-Foo.belongsToMany(Bar, { through: 'foo_bar', targetKey: 'title' });
-// This creates a junction table `foo_bar` with fields `fooId` and `barTitle`
-```
-
-* We might want a many-to-many relationship using the a different field for `Foo` and the default primary key for `Bar`:
-
-```js
-Foo.belongsToMany(Bar, { through: 'foo_bar', sourceKey: 'name' });
-// This creates a junction table `foo_bar` with fields `fooName` and `barId`
-```
-
-* We might want a many-to-many relationship using different fields for both `Foo` and `Bar`:
-
-```js
-Foo.belongsToMany(Bar, { through: 'foo_bar', sourceKey: 'name', targetKey: 'title' });
-// This creates a junction table `foo_bar` with fields `fooName` and `barTitle`
-```
-
-### Notes
-
-Don't forget that the field referenced in the association must have a unique constraint placed on it. Otherwise, an error will be thrown (and sometimes with a mysterious error message - such as `SequelizeDatabaseError: SQLITE_ERROR: foreign key mismatch - "ships" referencing "captains"` for SQLite).
-
-The trick to deciding between `sourceKey` and `targetKey` is just to remember where each relationship places its foreign key. As mentioned in the beginning of this guide:
-
-* `A.belongsTo(B)` keeps the foreign key in the source model (`A`), therefore the referenced key is in the target model, hence the usage of `targetKey`.
-
-* `A.hasOne(B)` and `A.hasMany(B)` keep the foreign key in the target model (`B`), therefore the referenced key is in the source model, hence the usage of `sourceKey`.
-
-* `A.belongsToMany(B)` involves an extra table (the junction table), therefore both `sourceKey` and `targetKey` are usable, with `sourceKey` corresponding to some field in `A` (the source) and `targetKey` corresponding to some field in `B` (the target).
diff --git a/docs/core-concepts/getters-setters-virtuals.md b/docs/core-concepts/getters-setters-virtuals.md
deleted file mode 100644
index 501069b0..00000000
--- a/docs/core-concepts/getters-setters-virtuals.md
+++ /dev/null
@@ -1,160 +0,0 @@
----
-sidebar_position: 5
-title: Getters, Setters & Virtuals
----
-
-Sequelize allows you to define custom getters and setters for the attributes of your models.
-
-Sequelize also allows you to specify the so-called *virtual attributes*, which are attributes on the Sequelize Model that doesn't really exist in the underlying SQL table, but instead are populated automatically by Sequelize. They are very useful to create custom attributes which also could simplify your code, for example.
-
-## Getters
-
-A getter is a `get()` function defined for one column in the model definition:
-
-```js
-const User = sequelize.define('user', {
- // Let's say we wanted to see every username in uppercase, even
- // though they are not necessarily uppercase in the database itself
- username: {
- type: DataTypes.STRING,
- get() {
- const rawValue = this.getDataValue('username');
- return rawValue ? rawValue.toUpperCase() : null;
- }
- }
-});
-```
-
-This getter, just like a standard JavaScript getter, is called automatically when the field value is read:
-
-```js
-const user = User.build({ username: 'SuperUser123' });
-console.log(user.username); // 'SUPERUSER123'
-console.log(user.getDataValue('username')); // 'SuperUser123'
-```
-
-Note that, although `SUPERUSER123` was logged above, the value truly stored in the database is still `SuperUser123`. We used `this.getDataValue('username')` to obtain this value, and converted it to uppercase.
-
-Had we tried to use `this.username` in the getter instead, we would have gotten an infinite loop! This is why Sequelize provides the `getDataValue` method.
-
-## Setters
-
-A setter is a `set()` function defined for one column in the model definition. It receives the value being set:
-
-```js
-const User = sequelize.define('user', {
- username: DataTypes.STRING,
- password: {
- type: DataTypes.STRING,
- set(value) {
- // Storing passwords in plaintext in the database is terrible.
- // Hashing the value with an appropriate cryptographic hash function is better.
- this.setDataValue('password', hash(value));
- }
- }
-});
-```
-
-```js
-const user = User.build({ username: 'someone', password: 'NotSo§tr0ngP4$SW0RD!' });
-console.log(user.password); // '7cfc84b8ea898bb72462e78b4643cfccd77e9f05678ec2ce78754147ba947acc'
-console.log(user.getDataValue('password')); // '7cfc84b8ea898bb72462e78b4643cfccd77e9f05678ec2ce78754147ba947acc'
-```
-
-Observe that Sequelize called the setter automatically, before even sending data to the database. The only data the database ever saw was the already hashed value.
-
-If we wanted to involve another field from our model instance in the computation, that is possible and very easy!
-
-```js
-const User = sequelize.define('user', {
- username: DataTypes.STRING,
- password: {
- type: DataTypes.STRING,
- set(value) {
- // Storing passwords in plaintext in the database is terrible.
- // Hashing the value with an appropriate cryptographic hash function is better.
- // Using the username as a salt is better.
- this.setDataValue('password', hash(this.username + value));
- }
- }
-});
-```
-
-**Note:** The above examples involving password handling, although much better than simply storing the password in plaintext, are far from perfect security. Handling passwords properly is hard, everything here is just for the sake of an example to show Sequelize functionality. We suggest involving a cybersecurity expert and/or reading [OWASP](https://www.owasp.org/) documents and/or visiting the [InfoSec StackExchange](https://security.stackexchange.com/).
-
-## Combining getters and setters
-
-Getters and setters can be both defined in the same field.
-
-For the sake of an example, let's say we are modeling a `Post`, whose `content` is a text of unlimited length. To improve memory usage, let's say we want to store a gzipped version of the content.
-
-*Note: modern databases should do some compression automatically in these cases. Please note that this is just for the sake of an example.*
-
-```js
-const { gzipSync, gunzipSync } = require('zlib');
-
-const Post = sequelize.define('post', {
- content: {
- type: DataTypes.TEXT,
- get() {
- const storedValue = this.getDataValue('content');
- const gzippedBuffer = Buffer.from(storedValue, 'base64');
- const unzippedBuffer = gunzipSync(gzippedBuffer);
- return unzippedBuffer.toString();
- },
- set(value) {
- const gzippedBuffer = gzipSync(value);
- this.setDataValue('content', gzippedBuffer.toString('base64'));
- }
- }
-});
-```
-
-With the above setup, whenever we try to interact with the `content` field of our `Post` model, Sequelize will automatically handle the custom getter and setter. For example:
-
-```js
-const post = await Post.create({ content: 'Hello everyone!' });
-
-console.log(post.content); // 'Hello everyone!'
-// Everything is happening under the hood, so we can even forget that the
-// content is actually being stored as a gzipped base64 string!
-
-// However, if we are really curious, we can get the 'raw' data...
-console.log(post.getDataValue('content'));
-// Output: 'H4sIAAAAAAAACvNIzcnJV0gtSy2qzM9LVQQAUuk9jQ8AAAA='
-```
-
-## Virtual fields
-
-Virtual fields are fields that Sequelize populates under the hood, but in reality they don't even exist in the database.
-
-For example, let's say we have the `firstName` and `lastName` attributes for a User.
-
-*Again, this is [only for the sake of an example](https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/).*
-
-It would be nice to have a simple way to obtain the *full name* directly! We can combine the idea of `getters` with the special data type Sequelize provides for this kind of situation: `DataTypes.VIRTUAL`:
-
-```js
-const { DataTypes } = require('@sequelize/core');
-
-const User = sequelize.define('user', {
- firstName: DataTypes.TEXT,
- lastName: DataTypes.TEXT,
- fullName: {
- type: DataTypes.VIRTUAL,
- get() {
- return `${this.firstName} ${this.lastName}`;
- },
- set(value) {
- throw new Error('Do not try to set the `fullName` value!');
- }
- }
-});
-```
-
-The `VIRTUAL` field does not cause a column in the table to exist. In other words, the model above will not have a `fullName` column. However, it will appear to have it!
-
-```js
-const user = await User.create({ firstName: 'John', lastName: 'Doe' });
-console.log(user.fullName); // 'John Doe'
-```
diff --git a/docs/core-concepts/model-basics.md b/docs/core-concepts/model-basics.md
deleted file mode 100644
index a27ec185..00000000
--- a/docs/core-concepts/model-basics.md
+++ /dev/null
@@ -1,535 +0,0 @@
----
-sidebar_position: 1
-title: Model Basics
----
-
-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 [Model](pathname:///api/v7/classes/Model.html).
-
-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.
-
-## Model Definition
-
-Models can be defined in two equivalent ways in Sequelize:
-
-* Calling [`sequelize.define(modelName, attributes, options)`](pathname:///api/v7/classes/Sequelize.html#define)
-* Extending [Model](pathname:///api/v7/classes/Model.html) and calling [`init(attributes, options)`](pathname:///api/v7/classes/Model.html#init)
-
-After a model is defined, it is available within `sequelize.models` by its model name.
-
-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.
-
-Both ways to define this model are shown below. After being defined, we can access our model with `sequelize.models.User`.
-
-### Using [`sequelize.define`](pathname:///api/v7/classes/Sequelize.html#define):
-
-```js
-const { Sequelize, DataTypes } = require('@sequelize/core');
-const sequelize = new Sequelize('sqlite::memory:');
-
-const User = sequelize.define('User', {
- // Model attributes are defined here
- firstName: {
- type: DataTypes.STRING,
- allowNull: false
- },
- lastName: {
- type: DataTypes.STRING
- // allowNull defaults to true
- }
-}, {
- // Other model options go here
-});
-
-// `sequelize.define` also returns the model
-console.log(User === sequelize.models.User); // true
-```
-
-### Extending [Model](pathname:///api/v7/classes/Model.html)
-
-```js
-const { Sequelize, DataTypes, Model } = require('@sequelize/core');
-const sequelize = new Sequelize('sqlite::memory:');
-
-class User extends Model {}
-
-User.init({
- // Model attributes are defined here
- firstName: {
- type: DataTypes.STRING,
- allowNull: false
- },
- lastName: {
- type: DataTypes.STRING
- // allowNull defaults to true
- }
-}, {
- // Other model options go here
- sequelize, // We need to pass the connection instance
- modelName: 'User' // We need to choose the model name
-});
-
-// the defined model is the class itself
-console.log(User === sequelize.models.User); // true
-```
-
-Internally, `sequelize.define` calls `Model.init`, so both approaches are essentially equivalent.
-
-#### Caveat with Public Class Fields
-
-Adding a [Public Class Field](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Public_class_fields) with the same name as one of the model's attribute is going to cause issues.
-Sequelize adds a getter & a setter for each attribute defined through `Model.init`.
-Adding a Public Class Field will shadow those getter and setters, blocking access to the model's actual data.
-
-```typescript
-// Invalid
-class User extends Model {
- id; // this field will shadow sequelize's getter & setter. It should be removed.
- otherPublicField; // this field does not shadow anything. It is fine.
-}
-
-User.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- }
-}, { sequelize });
-
-const user = new User({ id: 1 });
-user.id; // undefined
-```
-
-```typescript
-// Valid
-class User extends Model {
- otherPublicField;
-}
-
-User.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- }
-}, { sequelize });
-
-const user = new User({ id: 1 });
-user.id; // 1
-```
-
-In TypeScript, you can add typing information without adding an actual public class field by using the `declare` keyword:
-
-```typescript
-// Valid
-class User extends Model {
- declare id: number; // this is ok! The 'declare' keyword ensures this field will not be emitted by TypeScript.
-}
-
-User.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- }
-}, { sequelize });
-
-const user = new User({ id: 1 });
-user.id; // 1
-```
-
-## Table name inference
-
-Observe that, in both methods above, the table name (`Users`) was never explicitly defined. However, the model name was given (`User`).
-
-By default, when the table name is not given, Sequelize automatically pluralizes the model name and uses that as the table name. This pluralization is done under the hood by a library called [inflection](https://www.npmjs.com/package/inflection), so that irregular plurals (such as `person -> people`) are computed correctly.
-
-Of course, this behavior is easily configurable.
-
-### Enforcing the table name to be equal to the model name
-
-You can stop the auto-pluralization performed by Sequelize using the `freezeTableName: true` option. This way, Sequelize will infer the table name to be equal to the model name, without any modifications:
-
-```js
-sequelize.define('User', {
- // ... (attributes)
-}, {
- freezeTableName: true
-});
-```
-
-The example above will create a model named `User` pointing to a table also named `User`.
-
-This behavior can also be defined globally for the sequelize instance, when it is created:
-
-```js
-const sequelize = new Sequelize('sqlite::memory:', {
- define: {
- freezeTableName: true
- }
-});
-```
-
-This way, all tables will use the same name as the model name.
-
-### Providing the table name directly
-
-You can simply tell Sequelize the name of the table directly as well:
-
-```js
-sequelize.define('User', {
- // ... (attributes)
-}, {
- tableName: 'Employees'
-});
-```
-
-## Model synchronization
-
-When you define a model, you're telling Sequelize a few things about its table in the database. However, what if the table actually doesn't even exist in the database? What if it exists, but it has different columns, less columns, or any other difference?
-
-This is where model synchronization comes in. A model can be synchronized with the database by calling [`model.sync(options)`](pathname:///api/v7/classes/Model.html#sync), an asynchronous function (that returns a Promise). With this call, Sequelize will automatically perform an SQL query to the database. Note that this changes only the table in the database, not the model in the JavaScript side.
-
-* `User.sync()` - This creates the table if it doesn't exist (and does nothing if it already exists)
-* `User.sync({ force: true })` - This creates the table, dropping it first if it already existed
-* `User.sync({ alter: true })` - This checks what is the current state of the table in the database (which columns it has, what are their data types, etc), and then performs the necessary changes in the table to make it match the model.
-
-Example:
-
-```js
-await User.sync({ force: true });
-console.log("The table for the User model was just (re)created!");
-```
-
-### Synchronizing all models at once
-
-You can use [`sequelize.sync()`](pathname:///api/v7/classes/Sequelize.html#sync) to automatically synchronize all models. Example:
-
-```js
-await sequelize.sync({ force: true });
-console.log("All models were synchronized successfully.");
-```
-
-### Dropping tables
-
-To drop the table related to a model:
-
-```js
-await User.drop();
-console.log("User table dropped!");
-```
-
-To drop all tables:
-
-```js
-await sequelize.drop();
-console.log("All tables dropped!");
-```
-
-### Database safety check
-
-As shown above, the `sync` and `drop` operations are destructive. Sequelize accepts a `match` option as an additional safety check, which receives a RegExp:
-
-```js
-// This will run .sync() only if database name ends with '_test'
-sequelize.sync({ force: true, match: /_test$/ });
-```
-
-### Synchronization in production
-
-As shown above, `sync({ force: true })` and `sync({ alter: true })` can be destructive operations. Therefore, they are not recommended for production-level software. Instead, synchronization should be done with the advanced concept of [Migrations](../other-topics/migrations.md), with the help of the [Sequelize CLI](https://github.com/sequelize/cli).
-
-## Timestamps
-
-By default, Sequelize automatically adds the fields `createdAt` and `updatedAt` to every model, using the data type `DataTypes.DATE`. Those fields are automatically managed as well - whenever you use Sequelize to create or update something, those fields will be set correctly. The `createdAt` field will contain the timestamp representing the moment of creation, and the `updatedAt` will contain the timestamp of the latest update.
-
-**Note:** This is done in the Sequelize level (i.e. not done with *SQL triggers*). This means that direct SQL queries (for example queries performed without Sequelize by any other means) will not cause these fields to be updated automatically.
-
-This behavior can be disabled for a model with the `timestamps: false` option:
-
-```js
-sequelize.define('User', {
- // ... (attributes)
-}, {
- timestamps: false
-});
-```
-
-It is also possible to enable only one of `createdAt`/`updatedAt`, and to provide a custom name for these columns:
-
-```js
-class Foo extends Model {}
-Foo.init({ /* attributes */ }, {
- sequelize,
-
- // don't forget to enable timestamps!
- timestamps: true,
-
- // I don't want createdAt
- createdAt: false,
-
- // I want updatedAt to actually be called updateTimestamp
- updatedAt: 'updateTimestamp'
-});
-```
-
-## Prevent creating a default PK attribute
-
-By default, Sequelize automatically adds the primary key attribute `id` to every model when no primary key has been defined manually. To prevent this you can set the `noPrimaryKey` option to true when defining the model.
-
-```js
-const User = sequelize.define('User', {
- name: DataTypes.STRING,
-}, {
- noPrimaryKey: true,
-});
-```
-
-In case you want to prevent the addition of the default primary key for every model:
-
-```js
-const sequelize = new Sequelize({
- define: {
- noPrimaryKey: true,
- },
-});
-
-const User = sequelize.define('User', {
- name: {
- type: DataTypes.STRING
- }
-});
-```
-
-## Column declaration shorthand syntax
-
-If the only thing being specified about a column is its data type, the syntax can be shortened:
-
-```js
-// This:
-sequelize.define('User', {
- name: {
- type: DataTypes.STRING
- }
-});
-
-// Can be simplified to:
-sequelize.define('User', { name: DataTypes.STRING });
-```
-
-## Default Values
-
-By default, Sequelize assumes that the default value of a column is `NULL`. This behavior can be changed by passing a specific `defaultValue` to the column definition:
-
-```js
-sequelize.define('User', {
- name: {
- type: DataTypes.STRING,
- defaultValue: "John Doe"
- }
-});
-```
-
-It is possible to use `fn` to use a native SQL function as the default value:
-
-```js
-sequelize.define('Foo', {
- myUuid: {
- type: DataTypes.UUID,
- defaultValue: fn('uuid_generate_v4'),
- }
-});
-```
-
-Sequelize provides a series of built-in default values you can use:
-
-- [`DataTypes.NOW`](../other-topics/other-data-types.mdx#built-in-default-values-for-dates)
-- [`DataTypes.UUIDV1`, `DataTypes.UUIDV4`](../other-topics/other-data-types.mdx#built-in-default-values-for-uuid)
-
-## Data Types
-
-Every column you define in your model must have a data type. Sequelize provides [a lot of built-in data types](https://github.com/sequelize/sequelize/blob/main/src/data-types.js). To access a built-in data type, you must import `DataTypes`:
-
-```js
-const { DataTypes } = require('@sequelize/core'); // Import the built-in data types
-```
-
-### Strings
-
-```js
-DataTypes.STRING // VARCHAR(255)
-DataTypes.STRING(1234) // VARCHAR(1234)
-DataTypes.STRING.BINARY // VARCHAR BINARY
-DataTypes.TEXT // TEXT
-DataTypes.TEXT('tiny') // TINYTEXT
-DataTypes.CITEXT // CITEXT PostgreSQL and SQLite only.
-DataTypes.TSVECTOR // TSVECTOR PostgreSQL only.
-```
-
-### Boolean
-
-```js
-DataTypes.BOOLEAN // TINYINT(1)
-```
-
-### Numbers
-
-```js
-DataTypes.INTEGER // INTEGER
-DataTypes.BIGINT // BIGINT
-DataTypes.BIGINT(11) // BIGINT(11)
-
-DataTypes.FLOAT // FLOAT
-DataTypes.FLOAT(11) // FLOAT(11)
-DataTypes.FLOAT(11, 10) // FLOAT(11,10)
-
-DataTypes.REAL // REAL PostgreSQL only.
-DataTypes.REAL(11) // REAL(11) PostgreSQL only.
-DataTypes.REAL(11, 12) // REAL(11,12) PostgreSQL only.
-
-DataTypes.DOUBLE // DOUBLE
-DataTypes.DOUBLE(11) // DOUBLE(11)
-DataTypes.DOUBLE(11, 10) // DOUBLE(11,10)
-
-DataTypes.DECIMAL // DECIMAL
-DataTypes.DECIMAL(10, 2) // DECIMAL(10,2)
-```
-
-#### Unsigned & Zerofill integers - MySQL/MariaDB only
-
-In MySQL and MariaDB, the data types `INTEGER`, `BIGINT`, `FLOAT` and `DOUBLE` can be set as unsigned or zerofill (or both), as follows:
-
-```js
-DataTypes.INTEGER.UNSIGNED
-DataTypes.INTEGER.ZEROFILL
-DataTypes.INTEGER.UNSIGNED.ZEROFILL
-// You can also specify the size i.e. INTEGER(10) instead of simply INTEGER
-// Same for BIGINT, FLOAT and DOUBLE
-```
-
-### Dates
-
-```js
-DataTypes.DATE // DATETIME for mysql / sqlite, TIMESTAMP WITH TIME ZONE for postgres
-DataTypes.DATE(6) // DATETIME(6) for mysql 5.6.4+. Fractional seconds support with up to 6 digits of precision
-DataTypes.DATEONLY // DATE without time
-```
-
-### UUIDs
-
-For UUIDs, use `DataTypes.UUID`. It becomes the `UUID` data type for PostgreSQL and SQLite, and `CHAR(36)` for MySQL. Sequelize can generate UUIDs automatically for these fields, simply use `DataTypes.UUIDV1` or `DataTypes.UUIDV4` as the default value:
-
-```js
-{
- type: DataTypes.UUID,
- defaultValue: DataTypes.UUIDV4 // Or DataTypes.UUIDV1
-}
-```
-
-### Others
-
-There are other data types, covered in a [separate guide](../other-topics/other-data-types.mdx).
-
-## Column Options
-
-When defining a column, apart from specifying the `type` of the column, and the `allowNull` and `defaultValue` options mentioned above, there are a lot more options that can be used. Some examples are below.
-
-```js
-const { Model, DataTypes, Deferrable } = require('@sequelize/core');
-
-class Foo extends Model {}
-Foo.init({
- // instantiating will automatically set the flag to true if not set
- flag: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: true },
-
- // default values for dates => current time
- myDate: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
-
- // setting allowNull to false will add NOT NULL to the column, which means an error will be
- // thrown from the DB when the query is executed if the column is null. If you want to check that a value
- // is not null before querying the DB, look at the validations section below.
- title: { type: DataTypes.STRING, allowNull: false },
-
- // Creating two objects with the same value will throw an error. The unique property can be either a
- // boolean, or a string. If you provide the same string for multiple columns, they will form a
- // composite unique key.
- uniqueOne: { type: DataTypes.STRING, unique: 'compositeIndex' },
- uniqueTwo: { type: DataTypes.INTEGER, unique: 'compositeIndex' },
-
- // The unique property is simply a shorthand to create a unique constraint.
- someUnique: { type: DataTypes.STRING, unique: true },
-
- // Go on reading for further information about primary keys
- identifier: { type: DataTypes.STRING, primaryKey: true },
-
- // autoIncrement can be used to create auto_incrementing integer columns
- incrementMe: { type: DataTypes.INTEGER, autoIncrement: true },
-
- // You can specify a custom column name via the 'field' attribute:
- fieldWithUnderscores: { type: DataTypes.STRING, field: 'field_with_underscores' },
-
- // It is possible to create foreign keys:
- bar_id: {
- type: DataTypes.INTEGER,
-
- references: {
- // This is a reference to another model
- model: Bar,
-
- // This is the column name of the referenced model
- key: 'id',
-
- // With PostgreSQL, it is optionally possible to declare when to check the foreign key constraint, passing the Deferrable type.
- deferrable: Deferrable.INITIALLY_IMMEDIATE
- // Options:
- // - `Deferrable.INITIALLY_IMMEDIATE` - Immediately check the foreign key constraints
- // - `Deferrable.INITIALLY_DEFERRED` - Defer all foreign key constraint check to the end of a transaction
- // - `Deferrable.NOT` - Don't defer the checks at all (default) - This won't allow you to dynamically change the rule in a transaction
- }
- },
-
- // Comments can only be added to columns in MySQL, MariaDB, PostgreSQL and MSSQL
- commentMe: {
- type: DataTypes.INTEGER,
- comment: 'This is a column name that has a comment'
- }
-}, {
- sequelize,
- modelName: 'foo',
-
- // Using `unique: true` in an attribute above is exactly the same as creating the index in the model's options:
- indexes: [{ unique: true, fields: ['someUnique'] }]
-});
-```
-
-## Taking advantage of Models being classes
-
-The Sequelize models are [ES6 classes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes). You can very easily add custom instance or class level methods.
-
-```js
-class User extends Model {
- static classLevelMethod() {
- return 'foo';
- }
- instanceLevelMethod() {
- return 'bar';
- }
- getFullname() {
- return [this.firstname, this.lastname].join(' ');
- }
-}
-User.init({
- firstname: DataTypes.TEXT,
- lastname: DataTypes.TEXT
-}, { sequelize });
-
-console.log(User.classLevelMethod()); // 'foo'
-const user = User.build({ firstname: 'Jane', lastname: 'Doe' });
-console.log(user.instanceLevelMethod()); // 'bar'
-console.log(user.getFullname()); // 'Jane Doe'
-```
diff --git a/docs/core-concepts/model-instances.md b/docs/core-concepts/model-instances.md
deleted file mode 100644
index 1bc19061..00000000
--- a/docs/core-concepts/model-instances.md
+++ /dev/null
@@ -1,200 +0,0 @@
----
-sidebar_position: 2
-title: Model Instances
----
-
-As you already know, a model is an [ES6 class](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes). An instance of the class represents one object from that model (which maps to one row of the table in the database). This way, model instances are [DAOs](https://en.wikipedia.org/wiki/Data_access_object).
-
-For this guide, the following setup will be assumed:
-
-```js
-const { Sequelize, Model, DataTypes } = require('@sequelize/core');
-const sequelize = new Sequelize("sqlite::memory:");
-
-const User = sequelize.define("user", {
- name: DataTypes.TEXT,
- favoriteColor: {
- type: DataTypes.TEXT,
- defaultValue: 'green'
- },
- age: DataTypes.INTEGER,
- cash: DataTypes.INTEGER
-});
-
-(async () => {
- await sequelize.sync({ force: true });
- // Code here
-})();
-```
-
-## Creating an instance
-
-Although a model is a class, you should not create instances by using the `new` operator directly. Instead, the [`build`](pathname:///api/v7/classes/Model.html#build) method should be used:
-
-```js
-const jane = User.build({ name: "Jane" });
-console.log(jane instanceof User); // true
-console.log(jane.name); // "Jane"
-```
-
-However, the code above does not communicate with the database at all (note that it is not even asynchronous)! This is because the [`build`](pathname:///api/v7/classes/Model.html#build) method only creates an object that *represents* data that *can* be mapped to a database. In order to really save (i.e. persist) this instance in the database, the [`save`](pathname:///api/v7/classes/Model.html#save) method should be used:
-
-```js
-await jane.save();
-console.log('Jane was saved to the database!');
-```
-
-Note, from the usage of `await` in the snippet above, that `save` is an asynchronous method. In fact, almost every Sequelize method is asynchronous; `build` is one of the very few exceptions.
-
-### A very useful shortcut: the `create` method
-
-Sequelize provides the [`create`](pathname:///api/v7/classes/Model.html#create) method, which combines the `build` and `save` methods shown above into a single method:
-
-```js
-const jane = await User.create({ name: "Jane" });
-// Jane exists in the database now!
-console.log(jane instanceof User); // true
-console.log(jane.name); // "Jane"
-```
-
-## Note: logging instances
-
-Trying to log a model instance directly to `console.log` will produce a lot of clutter, since Sequelize instances have a lot of things attached to them. Instead, you can use the `.toJSON()` method (which, by the way, automatically guarantees the instances to be `JSON.stringify`-ed well).
-
-```js
-const jane = await User.create({ name: "Jane" });
-// console.log(jane); // Don't do this
-console.log(jane.toJSON()); // This is good!
-console.log(JSON.stringify(jane, null, 4)); // This is also good!
-```
-
-## Default values
-
-Built instances will automatically get default values:
-
-```js
-const jane = User.build({ name: "Jane" });
-console.log(jane.favoriteColor); // "green"
-```
-
-## Updating an instance
-
-If you change the value of some field of an instance, calling `save` again will update it accordingly:
-
-```js
-const jane = await User.create({ name: "Jane" });
-console.log(jane.name); // "Jane"
-jane.name = "Ada";
-// the name is still "Jane" in the database
-await jane.save();
-// Now the name was updated to "Ada" in the database!
-```
-
-You can update several fields at once with the [`set`](pathname:///api/v7/classes/Model.html#set) method:
-
-```js
-const jane = await User.create({ name: "Jane" });
-
-jane.set({
- name: "Ada",
- favoriteColor: "blue"
-});
-// As above, the database still has "Jane" and "green"
-await jane.save();
-// The database now has "Ada" and "blue" for name and favorite color
-```
-
-Note that the `save()` here will also persist any other changes that have been made on this instance, not just those in the previous `set` call. If you want to update a specific set of fields, you can use [`update`](pathname:///api/v7/classes/Model.html#update):
-
-```js
-const jane = await User.create({ name: "Jane" });
-jane.favoriteColor = "blue"
-await jane.update({ name: "Ada" })
-// The database now has "Ada" for name, but still has the default "green" for favorite color
-await jane.save()
-// Now the database has "Ada" for name and "blue" for favorite color
-```
-
-## Deleting an instance
-
-You can delete an instance by calling [`destroy`](pathname:///api/v7/classes/Model.html#destroy):
-
-```js
-const jane = await User.create({ name: "Jane" });
-console.log(jane.name); // "Jane"
-await jane.destroy();
-// Now this entry was removed from the database
-```
-
-## Reloading an instance
-
-You can reload an instance from the database by calling [`reload`](pathname:///api/v7/classes/Model.html#reload):
-
-```js
-const jane = await User.create({ name: "Jane" });
-console.log(jane.name); // "Jane"
-jane.name = "Ada";
-// the name is still "Jane" in the database
-await jane.reload();
-console.log(jane.name); // "Jane"
-```
-
-The reload call generates a `SELECT` query to get the up-to-date data from the database.
-
-## Saving only some fields
-
-It is possible to define which attributes should be saved when calling `save`, by passing an array of column names.
-
-This is useful when you set attributes based on a previously defined object, for example, when you get the values of an object via a form of a web app. Furthermore, this is used internally in the `update` implementation. This is how it looks like:
-
-```js
-const jane = await User.create({ name: "Jane" });
-console.log(jane.name); // "Jane"
-console.log(jane.favoriteColor); // "green"
-jane.name = "Jane II";
-jane.favoriteColor = "blue";
-await jane.save({ fields: ['name'] });
-console.log(jane.name); // "Jane II"
-console.log(jane.favoriteColor); // "blue"
-// The above printed blue because the local object has it set to blue, but
-// in the database it is still "green":
-await jane.reload();
-console.log(jane.name); // "Jane II"
-console.log(jane.favoriteColor); // "green"
-```
-
-## Change-awareness of save
-
-The `save` method is optimized internally to only update fields that really changed. This means that if you don't change anything and call `save`, Sequelize will know that the save is superfluous and do nothing, i.e., no query will be generated (it will still return a Promise, but it will resolve immediately).
-
-Also, if only a few attributes have changed when you call `save`, only those fields will be sent in the `UPDATE` query, to improve performance.
-
-## Incrementing and decrementing integer values
-
-In order to increment/decrement values of an instance without running into concurrency issues, Sequelize provides the [`increment`](pathname:///api/v7/classes/Model.html#increment) and [`decrement`](pathname:///api/v7/classes/Model.html#decrement) instance methods.
-
-```js
-const jane = await User.create({ name: "Jane", age: 100 });
-const incrementResult = await jane.increment('age', { by: 2 });
-// Note: to increment by 1 you can omit the `by` option and just do `user.increment('age')`
-
-// In PostgreSQL, `incrementResult` will be the updated user, unless the option
-// `{ returning: false }` was set (and then it will be undefined).
-
-// In other dialects, `incrementResult` will be undefined. If you need the updated instance, you will have to call `user.reload()`.
-```
-
-You can also increment multiple fields at once:
-
-```js
-const jane = await User.create({ name: "Jane", age: 100, cash: 5000 });
-await jane.increment({
- 'age': 2,
- 'cash': 100
-});
-
-// If the values are incremented by the same amount, you can use this other syntax as well:
-await jane.increment(['age', 'cash'], { by: 2 });
-```
-
-Decrementing works in the exact same way.
diff --git a/docs/core-concepts/model-querying-basics.md b/docs/core-concepts/model-querying-basics.md
deleted file mode 100644
index 9022265c..00000000
--- a/docs/core-concepts/model-querying-basics.md
+++ /dev/null
@@ -1,825 +0,0 @@
----
-sidebar_position: 3
-title: Model Querying - Basics
----
-
-Sequelize provides various methods to assist querying your database for data.
-
-*Important notice: to perform production-ready queries with Sequelize, make sure you have read the [Transactions guide](../other-topics/transactions.md) as well. Transactions are important to ensure data integrity and to provide other benefits.*
-
-This guide will show how to make the standard [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) queries.
-
-## Simple INSERT queries
-
-First, a simple example:
-
-```js
-// Create a new user
-const jane = await User.create({ firstName: "Jane", lastName: "Doe" });
-console.log("Jane's auto-generated ID:", jane.id);
-```
-
-The [`Model.create()`](pathname:///api/v7/classes/Model.html#create) method is a shorthand for building an unsaved instance with [`Model.build()`](pathname:///api/v7/classes/Model.html#build) and saving the instance with [`instance.save()`](pathname:///api/v7/classes/Model.html#save).
-
-It is also possible to define which attributes can be set in the `create` method. This can be especially useful if you create database entries based on a form which can be filled by a user. Using that would, for example, allow you to restrict the `User` model to set only an username but not an admin flag (i.e., `isAdmin`):
-
-```js
-const user = await User.create({
- username: 'alice123',
- isAdmin: true
-}, { fields: ['username'] });
-// let's assume the default of isAdmin is false
-console.log(user.username); // 'alice123'
-console.log(user.isAdmin); // false
-```
-
-## Simple SELECT queries
-
-You can read the whole table from the database with the [`findAll`](pathname:///api/v7/classes/Model.html#findAll) method:
-
-```js
-// Find all users
-const users = await User.findAll();
-console.log(users.every(user => user instanceof User)); // true
-console.log("All users:", JSON.stringify(users, null, 2));
-```
-
-```sql
-SELECT * FROM ...
-```
-
-## Specifying attributes for SELECT queries
-
-To select only some attributes, you can use the `attributes` option:
-
-```js
-Model.findAll({
- attributes: ['foo', 'bar']
-});
-```
-
-```sql
-SELECT foo, bar FROM ...
-```
-
-Attributes can be renamed using a nested array:
-
-```js
-Model.findAll({
- attributes: ['foo', ['bar', 'baz'], 'qux']
-});
-```
-
-```sql
-SELECT foo, bar AS baz, qux FROM ...
-```
-
-You can use [`fn`](pathname:///api/v7/index.html#fn) to do aggregations:
-
-```js
-import { fn, col } from '@sequelize/core';
-
-Model.findAll({
- attributes: [
- 'foo',
- [fn('COUNT', col('hats')), 'n_hats'],
- 'bar'
- ]
-});
-```
-
-```sql
-SELECT foo, COUNT(hats) AS n_hats, bar FROM ...
-```
-
-When using aggregation function, you must give it an alias to be able to access it from the model. In the example above you can get the number of hats with `instance.n_hats`.
-
-Sometimes it may be tiresome to list all the attributes of the model if you only want to add an aggregation:
-
-```js
-import { fn, col } from '@sequelize/core';
-
-// This is a tiresome way of getting the number of hats (along with every column)
-Model.findAll({
- attributes: [
- 'id', 'foo', 'bar', 'baz', 'qux', 'hats', // We had to list all attributes...
- [fn('COUNT', col('hats')), 'n_hats'] // To add the aggregation...
- ]
-});
-
-// This is shorter, and less error prone because it still works if you add / remove attributes from your model later
-Model.findAll({
- attributes: {
- include: [
- [fn('COUNT', col('hats')), 'n_hats']
- ]
- }
-});
-```
-
-```sql
-SELECT id, foo, bar, baz, qux, hats, COUNT(hats) AS n_hats FROM ...
-```
-
-Similarly, it's also possible to remove a selected few attributes:
-
-```js
-Model.findAll({
- attributes: { exclude: ['baz'] }
-});
-```
-
-```sql
--- Assuming all columns are 'id', 'foo', 'bar', 'baz' and 'qux'
-SELECT id, foo, bar, qux FROM ...
-```
-
-## Applying WHERE clauses
-
-[//]: # (TODO: replace link to a guide that details every operator)
-
-The `where` option is used to filter the query. There are lots of operators to use for the `where` clause, available as Symbols from [`Op`](pathname:///api/v7/index.html#Op).
-
-### The basics
-
-```js
-Post.findAll({
- where: {
- authorId: 2
- }
-});
-// SELECT * FROM post WHERE authorId = 2;
-```
-
-Observe that no operator (from `Op`) was explicitly passed, so Sequelize assumed an equality comparison by default. The above code is equivalent to:
-
-```js
-const { Op } = require('@sequelize/core');
-
-Post.findAll({
- where: {
- authorId: {
- [Op.eq]: 2
- }
- }
-});
-// SELECT * FROM post WHERE authorId = 2;
-```
-
-Multiple checks can be passed:
-
-```js
-Post.findAll({
- where: {
- authorId: 12,
- status: 'active'
- }
-});
-// SELECT * FROM post WHERE authorId = 12 AND status = 'active';
-```
-
-Just like Sequelize inferred the `Op.eq` operator in the first example, here Sequelize inferred that the caller wanted an `AND` for the two checks. The code above is equivalent to:
-
-```js
-const { Op } = require('@sequelize/core');
-
-Post.findAll({
- where: {
- [Op.and]: [
- { authorId: 12 },
- { status: 'active' }
- ]
- }
-});
-// SELECT * FROM post WHERE authorId = 12 AND status = 'active';
-```
-
-An `OR` can be easily performed in a similar way:
-
-```js
-const { Op } = require('@sequelize/core');
-
-Post.findAll({
- where: {
- [Op.or]: [
- { authorId: 12 },
- { authorId: 13 }
- ]
- }
-});
-// SELECT * FROM post WHERE authorId = 12 OR authorId = 13;
-```
-
-Since the above was an `OR` involving the same field, Sequelize allows you to use a slightly different structure which is more readable and generates the same behavior:
-
-```js
-const { Op } = require('@sequelize/core');
-
-Post.destroy({
- where: {
- authorId: {
- [Op.or]: [12, 13]
- }
- }
-});
-// DELETE FROM post WHERE authorId = 12 OR authorId = 13;
-```
-
-### Operators
-
-Sequelize provides several operators.
-
-```js
-const { Op, literal, fn } = require('@sequelize/core');
-
-Post.findAll({
- where: {
- [Op.and]: [{ a: 5 }, { b: 6 }], // (a = 5) AND (b = 6)
- [Op.or]: [{ a: 5 }, { b: 6 }], // (a = 5) OR (b = 6)
- someAttribute: {
- // Basics
- [Op.eq]: 3, // = 3
- [Op.ne]: 20, // != 20
- [Op.is]: null, // IS NULL
- [Op.not]: true, // IS NOT TRUE
- [Op.or]: [5, 6], // (someAttribute = 5) OR (someAttribute = 6)
-
- // Using dialect specific column identifiers (PG in the following example):
- [Op.col]: 'user.organization_id', // = "user"."organization_id"
-
- // Number comparisons
- [Op.gt]: 6, // > 6
- [Op.gte]: 6, // >= 6
- [Op.lt]: 10, // < 10
- [Op.lte]: 10, // <= 10
- [Op.between]: [6, 10], // BETWEEN 6 AND 10
- [Op.notBetween]: [11, 15], // NOT BETWEEN 11 AND 15
-
- // Other operators
-
- [Op.all]: literal('SELECT 1'), // > ALL (SELECT 1)
-
- [Op.in]: [1, 2], // IN [1, 2]
- [Op.notIn]: [1, 2], // NOT IN [1, 2]
-
- [Op.like]: '%hat', // LIKE '%hat'
- [Op.notLike]: '%hat', // NOT LIKE '%hat'
- [Op.startsWith]: 'hat', // LIKE 'hat%'
- [Op.endsWith]: 'hat', // LIKE '%hat'
- [Op.substring]: 'hat', // LIKE '%hat%'
- [Op.iLike]: '%hat', // ILIKE '%hat' (case insensitive) (PG only)
- [Op.notILike]: '%hat', // NOT ILIKE '%hat' (PG only)
- [Op.regexp]: '^[h|a|t]', // REGEXP/~ '^[h|a|t]' (MySQL/PG only)
- [Op.notRegexp]: '^[h|a|t]', // NOT REGEXP/!~ '^[h|a|t]' (MySQL/PG only)
- [Op.iRegexp]: '^[h|a|t]', // ~* '^[h|a|t]' (PG only)
- [Op.notIRegexp]: '^[h|a|t]', // !~* '^[h|a|t]' (PG only)
-
- [Op.any]: [2, 3], // ANY (ARRAY[2, 3]::INTEGER[]) (PG only)
- [Op.match]: fn('to_tsquery', 'fat & rat') // match text search for strings 'fat' and 'rat' (PG only)
-
- // In Postgres, Op.like/Op.iLike/Op.notLike can be combined to Op.any:
- [Op.like]: { [Op.any]: ['cat', 'hat'] } // LIKE ANY (ARRAY['cat', 'hat'])
-
- // There are more postgres-only range operators, see below
- }
- }
-});
-```
-
-#### Shorthand syntax for `Op.in`
-
-Passing an array directly to the `where` option will implicitly use the `IN` operator:
-
-```js
-Post.findAll({
- where: {
- id: [1,2,3] // Same as using `id: { [Op.in]: [1,2,3] }`
- }
-});
-// SELECT ... FROM "posts" AS "post" WHERE "post"."id" IN (1, 2, 3);
-```
-
-### Logical combinations with operators
-
-The operators `Op.and`, `Op.or` and `Op.not` can be used to create arbitrarily complex nested logical comparisons.
-
-#### Examples with `Op.and` and `Op.or`
-
-```js
-const { Op } = require('@sequelize/core');
-
-Foo.findAll({
- where: {
- rank: {
- [Op.or]: {
- [Op.lt]: 1000,
- [Op.eq]: null
- }
- },
- // rank < 1000 OR rank IS NULL
-
- {
- createdAt: {
- [Op.lt]: new Date(),
- [Op.gt]: new Date(new Date() - 24 * 60 * 60 * 1000)
- }
- },
- // createdAt < [timestamp] AND createdAt > [timestamp]
-
- {
- [Op.or]: [
- {
- title: {
- [Op.like]: 'Boat%'
- }
- },
- {
- description: {
- [Op.like]: '%boat%'
- }
- }
- ]
- }
- // title LIKE 'Boat%' OR description LIKE '%boat%'
- }
-});
-```
-
-#### Examples with `Op.not`
-
-```js
-Project.findAll({
- where: {
- name: 'Some Project',
- [Op.not]: [
- { id: [1,2,3] },
- {
- description: {
- [Op.like]: 'Hello%'
- }
- }
- ]
- }
-});
-```
-
-The above will generate:
-
-```sql
-SELECT *
-FROM `Projects`
-WHERE (
- `Projects`.`name` = 'Some Project'
- AND NOT (
- `Projects`.`id` IN (1,2,3)
- AND
- `Projects`.`description` LIKE 'Hello%'
- )
-)
-```
-
-### Advanced queries with functions (not just columns)
-
-What if you wanted to obtain something like `WHERE char_length("content") = 7`?
-
-```js
-import { where, fn, col } from '@sequelize/core';
-
-Post.findAll({
- where: where(fn('char_length', col('content')), 7)
-});
-// SELECT ... FROM "posts" AS "post" WHERE char_length("content") = 7
-```
-
-Note the usage of the [`fn`](pathname:///api/v7/index.html#fn) and [`col`](pathname:///api/v7/index.html#col) methods, which should be used to specify an SQL function call and a table column, respectively. These methods should be used instead of passing a plain string (such as `char_length(content)`) because Sequelize needs to treat this situation differently (for example, using other symbol escaping approaches).
-
-What if you need something even more complex?
-
-```js
-import { where, fn, col, Op } from '@sequelize/core';
-
-Post.findAll({
- where: {
- [Op.or]: [
- where(fn('char_length', col('content')), 7),
- {
- content: {
- [Op.like]: 'Hello%'
- }
- },
- {
- [Op.and]: [
- { status: 'draft' },
- where(fn('char_length', col('content')), {
- [Op.gt]: 10
- })
- ]
- }
- ]
- }
-});
-```
-
-The above generates the following SQL:
-
-```sql
-SELECT
- ...
-FROM "posts" AS "post"
-WHERE (
- char_length("content") = 7
- OR
- "post"."content" LIKE 'Hello%'
- OR (
- "post"."status" = 'draft'
- AND
- char_length("content") > 10
- )
-)
-```
-
-### Querying JSON
-
-JSON can be queried in three different ways:
-
-```js
-// Nested object
-await Foo.findOne({
- where: {
- meta: {
- video: {
- url: {
- [Op.ne]: null
- }
- }
- }
- }
-});
-
-// Nested key
-await Foo.findOne({
- where: {
- "meta.audio.length": {
- [Op.gt]: 20
- }
- }
-});
-
-// Containment
-await Foo.findOne({
- where: {
- meta: {
- [Op.contains]: {
- site: {
- url: 'https://google.com'
- }
- }
- }
- }
-});
-```
-
-#### MSSQL
-
-MSSQL does not have a JSON data type, however it does provide some support for JSON stored as strings through certain functions since SQL Server 2016. Using these functions, you will be able to query the JSON stored in the string, but any returned values will need to be parsed separately.
-
-```js
-import { where, fn, col } from '@sequelize/core';
-
-// ISJSON - to test if a string contains valid JSON
-await User.findAll({
- where: where(fn('ISJSON', col('userDetails')), 1)
-});
-
-// JSON_VALUE - extract a scalar value from a JSON string
-await User.findAll({
- attributes: [[ fn('JSON_VALUE', col('userDetails'), '$.address.Line1'), 'address line 1']]
-});
-
-// JSON_VALUE - query a scalar value from a JSON string
-await User.findAll({
- where: where(fn('JSON_VALUE', col('userDetails'), '$.address.Line1'), '14, Foo Street')
-});
-
-// JSON_QUERY - extract an object or array
-await User.findAll({
- attributes: [[ fn('JSON_QUERY', col('userDetails'), '$.address'), 'full address']]
-});
-```
-
-### Postgres-only Range Operators
-
-Range types can be queried with all supported operators.
-
-Keep in mind, the provided range value can [define the bound inclusion/exclusion](../other-topics/other-data-types.mdx#ranges-postgresql-only) as well.
-
-```js
-[Op.contains]: 2, // @> '2'::integer (PG range contains element operator)
-[Op.contains]: [1, 2], // @> [1, 2) (PG range contains range operator)
-[Op.contained]: [1, 2], // <@ [1, 2) (PG range is contained by operator)
-[Op.overlap]: [1, 2], // && [1, 2) (PG range overlap (have points in common) operator)
-[Op.adjacent]: [1, 2], // -|- [1, 2) (PG range is adjacent to operator)
-[Op.strictLeft]: [1, 2], // << [1, 2) (PG range strictly left of operator)
-[Op.strictRight]: [1, 2], // >> [1, 2) (PG range strictly right of operator)
-[Op.noExtendRight]: [1, 2], // &< [1, 2) (PG range does not extend to the right of operator)
-[Op.noExtendLeft]: [1, 2], // &> [1, 2) (PG range does not extend to the left of operator)
-```
-
-### Deprecated: Operator Aliases
-
-In Sequelize v4, it was possible to specify strings to refer to operators, instead of using Symbols. This is now deprecated and heavily discouraged, and will probably be removed in the next major version. If you really need it, you can pass the `operatorAliases` option in the Sequelize constructor.
-
-For example:
-
-```js
-const { Sequelize, Op } = require('@sequelize/core');
-
-const sequelize = new Sequelize('sqlite::memory:', {
- operatorsAliases: {
- $gt: Op.gt
- }
-});
-
-// Now we can use `$gt` instead of `[Op.gt]` in where clauses:
-Foo.findAll({
- where: {
- $gt: 6 // Works like using [Op.gt]
- }
-});
-```
-
-## Simple UPDATE queries
-
-Update queries also accept the `where` option, just like the read queries shown above.
-
-```js
-// Change everyone without a last name to "Doe"
-await User.update({ lastName: "Doe" }, {
- where: {
- lastName: null
- }
-});
-```
-
-## Simple DELETE queries
-
-Delete queries also accept the `where` option, just like the read queries shown above.
-
-```js
-// Delete everyone named "Jane"
-await User.destroy({
- where: {
- firstName: "Jane"
- }
-});
-```
-
-To destroy everything the `TRUNCATE` SQL can be used:
-
-```js
-// Truncate the table
-await User.destroy({
- truncate: true
-});
-```
-
-## Creating in bulk
-
-Sequelize provides the `Model.bulkCreate` method to allow creating multiple records at once, with only one query.
-
-The usage of `Model.bulkCreate` is very similar to `Model.create`, by receiving an array of objects instead of a single object.
-
-```js
-const captains = await Captain.bulkCreate([
- { name: 'Jack Sparrow' },
- { name: 'Davy Jones' }
-]);
-console.log(captains.length); // 2
-console.log(captains[0] instanceof Captain); // true
-console.log(captains[0].name); // 'Jack Sparrow'
-console.log(captains[0].id); // 1 // (or another auto-generated value)
-```
-
-However, by default, `bulkCreate` does not run validations on each object that is going to be created (which `create` does). To make `bulkCreate` run these validations as well, you must pass the `validate: true` option. This will decrease performance. Usage example:
-
-```js
-const Foo = sequelize.define('foo', {
- bar: {
- type: DataTypes.TEXT,
- validate: {
- len: [4, 6]
- }
- }
-});
-
-// This will not throw an error, both instances will be created
-await Foo.bulkCreate([
- { name: 'abc123' },
- { name: 'name too long' }
-]);
-
-// This will throw an error, nothing will be created
-await Foo.bulkCreate([
- { name: 'abc123' },
- { name: 'name too long' }
-], { validate: true });
-```
-
-If you are accepting values directly from the user, it might be beneficial to limit the columns that you want to actually insert. To support this, `bulkCreate()` accepts a `fields` option, an array defining which fields must be considered (the rest will be ignored).
-
-```js
-await User.bulkCreate([
- { username: 'foo' },
- { username: 'bar', admin: true }
-], { fields: ['username'] });
-// Neither foo nor bar are admins.
-```
-
-## Ordering and Grouping
-
-Sequelize provides the `order` and `group` options to work with `ORDER BY` and `GROUP BY`.
-
-### Ordering
-
-The `order` option takes an array of items to order the query by or a sequelize method. These *items* are themselves arrays in the form `[column, direction]`. The column will be escaped correctly and the direction will be checked in a whitelist of valid directions (such as `ASC`, `DESC`, `NULLS FIRST`, etc).
-
-```js
-import { where, fn, col, literal } from '@sequelize/core';
-
-Subtask.findAll({
- order: [
- // Will escape title and validate DESC against a list of valid direction parameters
- ['title', 'DESC'],
-
- // Will order by max(age)
- fn('max', col('age')),
-
- // Will order by max(age) DESC
- [fn('max', col('age')), 'DESC'],
-
- // Will order by otherfunction(`col1`, 12, 'lalala') DESC
- [fn('otherfunction', col('col1'), 12, 'lalala'), 'DESC'],
-
- // Will order an associated model's createdAt using the model name as the association's name.
- [Task, 'createdAt', 'DESC'],
-
- // Will order through an associated model's createdAt using the model names as the associations' names.
- [Task, Project, 'createdAt', 'DESC'],
-
- // Will order by an associated model's createdAt using the name of the association.
- ['Task', 'createdAt', 'DESC'],
-
- // Will order by a nested associated model's createdAt using the names of the associations.
- ['Task', 'Project', 'createdAt', 'DESC'],
-
- // Will order by an associated model's createdAt using an association object. (preferred method)
- [Subtask.associations.Task, 'createdAt', 'DESC'],
-
- // Will order by a nested associated model's createdAt using association objects. (preferred method)
- [Subtask.associations.Task, Task.associations.Project, 'createdAt', 'DESC'],
-
- // Will order by an associated model's createdAt using a simple association object.
- [{model: Task, as: 'Task'}, 'createdAt', 'DESC'],
-
- // Will order by a nested associated model's createdAt simple association objects.
- [{model: Task, as: 'Task'}, {model: Project, as: 'Project'}, 'createdAt', 'DESC']
- ],
-
- // Will order by max age descending
- order: literal('max(age) DESC'),
-
- // Will order by max age ascending assuming ascending is the default order when direction is omitted
- order: fn('max', col('age')),
-
- // Will order by age ascending assuming ascending is the default order when direction is omitted
- order: col('age'),
-
- // Will order randomly based on the dialect (instead of fn('RAND') or fn('RANDOM'))
- order: sequelize.random()
-});
-
-Foo.findOne({
- order: [
- // will return `name`
- ['name'],
- // will return `username` DESC
- ['username', 'DESC'],
- // will return max(`age`)
- fn('max', col('age')),
- // will return max(`age`) DESC
- [fn('max', col('age')), 'DESC'],
- // will return otherfunction(`col1`, 12, 'lalala') DESC
- [fn('otherfunction', col('col1'), 12, 'lalala'), 'DESC'],
- // will return otherfunction(awesomefunction(`col`)) DESC, This nesting is potentially infinite!
- [fn('otherfunction', fn('awesomefunction', col('col'))), 'DESC']
- ]
-});
-```
-
-To recap, the elements of the order array can be the following:
-
-* A string (which will be automatically quoted)
-* An array, whose first element will be quoted, second will be appended verbatim
-* An object with a `raw` field:
- * The content of `raw` will be added verbatim without quoting
- * Everything else is ignored, and if raw is not set, the query will fail
-* A call to `fn()` (which will generate a function call in SQL)
-* A call to `col()` (which will quote the column name)
-
-### Grouping
-
-The syntax for grouping and ordering are equal, except that grouping does not accept a direction as last argument of the array (there is no `ASC`, `DESC`, `NULLS FIRST`, etc).
-
-You can also pass a string directly to `group`, which will be included directly (verbatim) into the generated SQL. Use with caution and don't use with user generated content.
-
-```js
-Project.findAll({ group: 'name' });
-// yields 'GROUP BY name'
-```
-
-## Limits and Pagination
-
-The `limit` and `offset` options allow you to work with limiting / pagination:
-
-```js
-// Fetch 10 instances/rows
-Project.findAll({ limit: 10 });
-
-// Skip 8 instances/rows
-Project.findAll({ offset: 8 });
-
-// Skip 5 instances and fetch the 5 after that
-Project.findAll({ offset: 5, limit: 5 });
-```
-
-Usually these are used alongside the `order` option.
-
-## Utility methods
-
-Sequelize also provides a few utility methods.
-
-### `count`
-
-The `count` method simply counts the occurrences of elements in the database.
-
-```js
-console.log(`There are ${await Project.count()} projects`);
-
-const amount = await Project.count({
- where: {
- id: {
- [Op.gt]: 25
- }
- }
-});
-console.log(`There are ${amount} projects with an id greater than 25`);
-```
-
-### `max`, `min` and `sum`
-
-Sequelize also provides the `max`, `min` and `sum` convenience methods.
-
-Let's assume we have three users, whose ages are 10, 5, and 40.
-
-```js
-await User.max('age'); // 40
-await User.max('age', { where: { age: { [Op.lt]: 20 } } }); // 10
-await User.min('age'); // 5
-await User.min('age', { where: { age: { [Op.gt]: 5 } } }); // 10
-await User.sum('age'); // 55
-await User.sum('age', { where: { age: { [Op.gt]: 5 } } }); // 50
-```
-
-### `increment`, `decrement`
-
-Sequelize also provides the `increment` convenience method.
-
-Let's assume we have a user, whose age is 10.
-
-```js
-await User.increment({age: 5}, { where: { id: 1 } }) // Will increase age to 15
-await User.increment({age: -5}, { where: { id: 1 } }) // Will decrease age to 5
-```
-
-
-### Literals (raw SQL)
-
-It's not always possible for Sequelize to support every SQL feature in a clean way. It can sometimes be better to write the SQL query yourself.
-
-This can be done in two ways:
-
-- Either write a complete [raw query](./raw-queries.md) yourself,
-- or use the [`literal()`](pathname:///api/v7/index.html#literal) function provided by Sequelize to insert raw SQL almost anywhere in queries built by Sequelize.
-
-```typescript
-import { literal } from '@sequelize/core';
-
-User.findAll({
- where: literal('id = $id'),
- bind: {
- id: 5,
- },
-});
-```
-
-`literal()` supports both [replacements](./raw-queries.md#replacements) and [bind parameters](./raw-queries.md#bind-parameters) as ways to safely include user input in your query.
diff --git a/docs/core-concepts/model-querying-finders.md b/docs/core-concepts/model-querying-finders.md
deleted file mode 100644
index 2d32a339..00000000
--- a/docs/core-concepts/model-querying-finders.md
+++ /dev/null
@@ -1,91 +0,0 @@
----
-sidebar_position: 4
-title: Model Querying - Finders
----
-
-Finder methods are the ones that generate `SELECT` queries.
-
-By default, the results of all finder methods are instances of the model class (as opposed to being just plain JavaScript objects). This means that after the database returns the results, Sequelize automatically wraps everything in proper instance objects. In a few cases, when there are too many results, this wrapping can be inefficient. To disable this wrapping and receive a plain response instead, pass `{ raw: true }` as an option to the finder method.
-
-## `findAll`
-
-The `findAll` method is already known from the previous tutorial. It generates a standard `SELECT` query which will retrieve all entries from the table (unless restricted by something like a `where` clause, for example).
-
-## `findByPk`
-
-The `findByPk` method obtains only a single entry from the table, using the provided primary key.
-
-```js
-const project = await Project.findByPk(123);
-if (project === null) {
- console.log('Not found!');
-} else {
- console.log(project instanceof Project); // true
- // Its primary key is 123
-}
-```
-
-## `findOne`
-
-The `findOne` method obtains the first entry it finds (that fulfills the optional query options, if provided).
-
-```js
-const project = await Project.findOne({ where: { title: 'My Title' } });
-if (project === null) {
- console.log('Not found!');
-} else {
- console.log(project instanceof Project); // true
- console.log(project.title); // 'My Title'
-}
-```
-
-## `findOrCreate`
-
-The method `findOrCreate` will create an entry in the table unless it can find one fulfilling the query options. In both cases, it will return an instance (either the found instance or the created instance) and a boolean indicating whether that instance was created or already existed.
-
-The `where` option is considered for finding the entry, and the `defaults` option is used to define what must be created in case nothing was found. If the `defaults` do not contain values for every column, Sequelize will take the values given to `where` (if present).
-
-Let's assume we have an empty database with a `User` model which has a `username` and a `job`.
-
-```js
-const [user, created] = await User.findOrCreate({
- where: { username: 'sdepold' },
- defaults: {
- job: 'Technical Lead JavaScript'
- }
-});
-console.log(user.username); // 'sdepold'
-console.log(user.job); // This may or may not be 'Technical Lead JavaScript'
-console.log(created); // The boolean indicating whether this instance was just created
-if (created) {
- console.log(user.job); // This will certainly be 'Technical Lead JavaScript'
-}
-```
-
-## `findAndCountAll`
-
-The `findAndCountAll` method is a convenience method that combines `findAll` and `count`. This is useful when dealing with queries related to pagination where you want to retrieve data with a `limit` and `offset` but also need to know the total number of records that match the query.
-
-When `group` is not provided, the `findAndCountAll` method returns an object with two properties:
-
-* `count` - an integer - the total number records matching the query
-* `rows` - an array of objects - the obtained records
-
-When `group` is provided, the `findAndCountAll` method returns an object with two properties:
-
-* `count` - an array of objects - contains the count in each group and the projected attributes
-* `rows` - an array of objects - the obtained records
-
-```js
-const { count, rows } = await Project.findAndCountAll({
- where: {
- title: {
- [Op.like]: 'foo%'
- }
- },
- offset: 10,
- limit: 2
-});
-console.log(count);
-console.log(rows);
-```
diff --git a/docs/core-concepts/raw-queries.md b/docs/core-concepts/raw-queries.md
deleted file mode 100644
index 9d3684dd..00000000
--- a/docs/core-concepts/raw-queries.md
+++ /dev/null
@@ -1,255 +0,0 @@
----
-sidebar_position: 7
-title: Raw Queries
----
-
-As there are often use cases in which it is just easier to execute raw / already prepared SQL queries, you can use the [`sequelize.query`](pathname:///api/v7/classes/Sequelize.html#query) method.
-
-By default the function will return two arguments - a results array, and an object containing metadata (such as amount of affected rows, etc). Note that since this is a raw query, the metadata are dialect specific. Some dialects return the metadata "within" the results object (as properties on an array). However, two arguments will always be returned, but for MSSQL and MySQL it will be two references to the same object.
-
-```js
-const [results, metadata] = await sequelize.query("UPDATE users SET y = 42 WHERE x = 12");
-// Results will be an empty array and metadata will contain the number of affected rows.
-```
-
-In cases where you don't need to access the metadata you can pass in a query type to tell sequelize how to format the results. For example, for a simple select query you could do:
-
-```js
-const { QueryTypes } = require('@sequelize/core');
-const users = await sequelize.query("SELECT * FROM `users`", { type: QueryTypes.SELECT });
-// We didn't need to destructure the result here - the results were returned directly
-```
-
-Several other query types are available. [Peek into the source for details](https://github.com/sequelize/sequelize/blob/main/packages/core/src/query-types.ts).
-
-A second option is the model. If you pass a model the returned data will be instances of that model.
-
-```js
-// Callee is the model definition. This allows you to easily map a query to a predefined model
-const projects = await sequelize.query('SELECT * FROM projects', {
- model: Projects,
- mapToModel: true // pass true here if you have any mapped fields
-});
-// Each element of `projects` is now an instance of Project
-```
-
-See more options in the [query API reference](pathname:///api/v7/classes/Sequelize.html#query). Some examples:
-
-```js
-const { QueryTypes } = require('@sequelize/core');
-await sequelize.query('SELECT 1', {
- // A function (or false) for logging your queries
- // Will get called for every SQL query that gets sent
- // to the server.
- logging: console.log,
-
- // If plain is true, then sequelize will only return the first
- // record of the result set. In case of false it will return all records.
- plain: false,
-
- // Set this to true if you don't have a model definition for your query.
- raw: false,
-
- // The type of query you are executing. The query type affects how results are formatted before they are passed back.
- type: QueryTypes.SELECT
-});
-
-// Note the second argument being null!
-// Even if we declared a callee here, the raw: true would
-// supersede and return a raw object.
-console.log(await sequelize.query('SELECT * FROM projects', { raw: true }));
-```
-
-## "Dotted" attributes and the `nest` option
-
-If an attribute name of the table contains dots, the resulting objects can become nested objects by setting the `nest: true` option. This is achieved with [dottie.js](https://github.com/mickhansen/dottie.js/) under the hood. See below:
-
-* Without `nest: true`:
-
- ```js
- const { QueryTypes } = require('@sequelize/core');
- const records = await sequelize.query('select 1 as `foo.bar.baz`', {
- type: QueryTypes.SELECT
- });
- console.log(JSON.stringify(records[0], null, 2));
- ```
-
- ```json
- {
- "foo.bar.baz": 1
- }
- ```
-
-* With `nest: true`:
-
- ```js
- const { QueryTypes } = require('@sequelize/core');
- const records = await sequelize.query('select 1 as `foo.bar.baz`', {
- nest: true,
- type: QueryTypes.SELECT
- });
- console.log(JSON.stringify(records[0], null, 2));
- ```
-
- ```json
- {
- "foo": {
- "bar": {
- "baz": 1
- }
- }
- }
- ```
-
-## Replacements
-
-Replacements are a way to pass variables in your Query. They are an alternative to [Bind Parameters](#bind-parameters).
-
-The difference between replacements and bind parameters is that replacements are escaped and inserted into the query by Sequelize before the query is sent to the database,
-whereas bind parameters are sent to the database separately from the SQL query text, and 'escaped' by the Database itself.
-
-Replacements can be written in two different ways:
-
-- Either using numeric identifiers (represented by a `?`). The `replacements` option must be an array. The values will be replaced in the order in which they appear in the array and query.
-- Or by using alphanumeric identifiers (e.g. `:firstName`, `:status`, etc…). These identifiers follow common identifier rules (alphanumeric & underscore only, cannot start with a number). The `replacements` option must be a plain object which includes each parameter (without the `:` prefix).
-
-The `replacements` option must contain all bound values, or Sequelize will throw an error.
-
-Examples:
-
-```js
-const { QueryTypes } = require('@sequelize/core');
-
-await sequelize.query(
- 'SELECT * FROM projects WHERE status = ?',
- {
- replacements: ['active'],
- type: QueryTypes.SELECT,
- },
-);
-
-await sequelize.query(
- 'SELECT * FROM projects WHERE status = :status',
- {
- replacements: { status: 'active' },
- type: QueryTypes.SELECT,
- },
-);
-```
-
-When using operators like `LIKE`, keep in mind that special characters in your replacement do keep their special meaning.
-e.g. the following query matches users with names that start with 'ben':
-
-```js
-const { QueryTypes } = require('@sequelize/core');
-
-await sequelize.query(
- 'SELECT * FROM users WHERE name LIKE :searchName',
- {
- replacements: { searchName: 'ben%' },
- type: QueryTypes.SELECT
- }
-);
-```
-
-:::caution
-
-Sequelize does not currently support a way to [specify the DataType of a replacement](https://github.com/sequelize/sequelize/issues/14410),
-and will try to guess its type prior to serializing it.
-
-For instance, Arrays will not be serialized as the SQL `ARRAY` type. Instead, the following query:
-
-```js
-const { QueryTypes } = require('@sequelize/core');
-
-await sequelize.query(
- 'SELECT * FROM projects WHERE status IN (:status)',
- {
- replacements: { status: ['active', 'inactive'] },
- type: QueryTypes.SELECT
- }
-);
-```
-
-Will result in this SQL:
-
-```sql
-SELECT * FROM projects WHERE status IN ('active', 'inactive')
-```
-
-Until such a feature is implemented, you can use a [bind parameter](#bind-parameters) and cast it instead.
-
-:::
-
-## Bind Parameters
-
-Bind parameters are a way to pass variables in your Query. They are an alternative to [Replacements](#replacements).
-
-The difference between replacements and bind parameters is that replacements are escaped and inserted into the query by Sequelize before the query is sent to the database,
-whereas bind parameters are sent to the database separately from the SQL query text, and 'escaped' by the Database itself.
-
-A query can have both bind parameters and replacements.
-
-Each database uses a different syntax for bind parameters, but Sequelize provides its own unification layer.
-
-Inconsequentially to which database you use, in Sequelize bind parameters are written following a postgres-like syntax. You can either:
-
-- Use numeric identifiers (e.g. `$1`, `$2`, etc…). Note that these identifiers start at 1, not 0. The `bind` option must be an array which contains a value for each identifier used in the query (`$1` is bound to the 1st element in the array (`bind[0]`), etc…).
-- Use alphanumeric identifiers (e.g. `$firstName`, `$status`, etc…). These identifiers follow common identifier rules (alphanumeric & underscore only, cannot start with a number). The `bind` option must be a plain object which includes each bind parameter (without the `$` prefix).
-
-The `bind` option must contain all bound values, or Sequelize will throw an error.
-
-:::info
-
-Bind Parameters can only be used for data values. Bind Parameters cannot be used to dynamically change the name of a table, a column, or other non-data values parts of the query.
-
-Your database may have further restrictions with bind parameters.
-
-:::
-
-Examples:
-
-```js
-const { QueryTypes } = require('@sequelize/core');
-
-await sequelize.query(
- 'SELECT * FROM projects WHERE status = $1',
- {
- bind: ['active'],
- type: QueryTypes.SELECT,
- },
-);
-
-await sequelize.query(
- 'SELECT * FROM projects WHERE status = $status',
- {
- bind: { status: 'active' },
- type: QueryTypes.SELECT,
- },
-);
-```
-
-Sequelize does not currently support a way to [specify the DataType of a bind parameter](https://github.com/sequelize/sequelize/issues/14410).
-Until such a feature is implemented, you can cast your bind parameters if you need to change their DataType:
-
-```js
-const { QueryTypes } = require('@sequelize/core');
-
-await sequelize.query(
- 'SELECT * FROM projects WHERE id = CAST($1 AS int)',
- {
- bind: [5],
- type: QueryTypes.SELECT,
- },
-);
-```
-
-:::note Did you know?
-
-Some dialects, such as PostgreSQL and IBM Db2, support a terser cast syntax that you can use if you prefer:
-
-```typescript
-await sequelize.query('SELECT * FROM projects WHERE id = $1::int');
-```
-
-:::
diff --git a/docs/core-concepts/validations-and-constraints.md b/docs/core-concepts/validations-and-constraints.md
deleted file mode 100644
index ec81235c..00000000
--- a/docs/core-concepts/validations-and-constraints.md
+++ /dev/null
@@ -1,275 +0,0 @@
----
-sidebar_position: 6
-title: Validations & Constraints
----
-
-In this tutorial you will learn how to setup validations and constraints for your models in Sequelize.
-
-For this tutorial, the following setup will be assumed:
-
-```js
-const { Sequelize, Op, Model, DataTypes } = require('@sequelize/core');
-const sequelize = new Sequelize("sqlite::memory:");
-
-const User = sequelize.define("user", {
- username: {
- type: DataTypes.TEXT,
- allowNull: false,
- unique: true
- },
- hashedPassword: {
- type: DataTypes.STRING(64),
- validate: {
- is: /^[0-9a-f]{64}$/i
- }
- }
-});
-
-(async () => {
- await sequelize.sync({ force: true });
- // Code here
-})();
-```
-
-## Difference between Validations and Constraints
-
-Validations are checks performed in the Sequelize level, in pure JavaScript. They can be arbitrarily complex if you provide a custom validator function, or can be one of the built-in validators offered by Sequelize. If a validation fails, no SQL query will be sent to the database at all.
-
-On the other hand, constraints are rules defined at SQL level. The most basic example of constraint is an Unique Constraint. If a constraint check fails, an error will be thrown by the database and Sequelize will forward this error to JavaScript (in this example, throwing a `SequelizeUniqueConstraintError`). Note that in this case, the SQL query was performed, unlike the case for validations.
-
-## Unique Constraint
-
-Our code example above defines a unique constraint on the `username` field:
-
-```js
-/* ... */ {
- username: {
- type: DataTypes.TEXT,
- allowNull: false,
- unique: true
- },
-} /* ... */
-```
-
-When this model is synchronized (by calling `sequelize.sync` for example), the `username` field will be created in the table as `` `username` TEXT UNIQUE``, and an attempt to insert an username that already exists there will throw a `SequelizeUniqueConstraintError`.
-
-## Allowing/disallowing null values
-
-By default, `null` is an allowed value for every column of a model. This can be disabled setting the `allowNull: false` option for a column, as it was done in the `username` field from our code example:
-
-```js
-/* ... */ {
- username: {
- type: DataTypes.TEXT,
- allowNull: false,
- unique: true
- },
-} /* ... */
-```
-
-Without `allowNull: false`, the call `User.create({})` would work.
-
-### Note about `allowNull` implementation
-
-The `allowNull` check is the only check in Sequelize that is a mix of a *validation* and a *constraint* in the senses described at the beginning of this tutorial. This is because:
-
-* If an attempt is made to set `null` to a field that does not allow null, a `ValidationError` will be thrown *without any SQL query being performed*.
-* In addition, after `sequelize.sync`, the column that has `allowNull: false` will be defined with a `NOT NULL` SQL constraint. This way, direct SQL queries that attempt to set the value to `null` will also fail.
-
-## Validators
-
-Model validators allow you to specify format/content/inheritance validations for each attribute of the model. Validations are automatically run on `create`, `update` and `save`. You can also call `validate()` to manually validate an instance.
-
-### Per-attribute validations
-
-You can define your custom validators or use several built-in validators, implemented by [validator.js (10.11.0)](https://github.com/chriso/validator.js), as shown below.
-
-```js
-sequelize.define('foo', {
- bar: {
- type: DataTypes.STRING,
- validate: {
- is: /^[a-z]+$/i, // matches this RegExp
- is: ["^[a-z]+$",'i'], // same as above, but constructing the RegExp from a string
- not: /^[a-z]+$/i, // does not match this RegExp
- not: ["^[a-z]+$",'i'], // same as above, but constructing the RegExp from a string
- isEmail: true, // checks for email format (foo@bar.com)
- isUrl: true, // checks for url format (https://foo.com)
- isIP: true, // checks for IPv4 (129.89.23.1) or IPv6 format
- isIPv4: true, // checks for IPv4 (129.89.23.1)
- isIPv6: true, // checks for IPv6 format
- isAlpha: true, // will only allow letters
- isAlphanumeric: true, // will only allow alphanumeric characters, so "_abc" will fail
- isNumeric: true, // will only allow numbers
- isInt: true, // checks for valid integers
- isFloat: true, // checks for valid floating point numbers
- isDecimal: true, // checks for any numbers
- isLowercase: true, // checks for lowercase
- isUppercase: true, // checks for uppercase
- notNull: true, // won't allow null
- isNull: true, // only allows null
- notEmpty: true, // don't allow empty strings
- equals: 'specific value', // only allow a specific value
- contains: 'foo', // force specific substrings
- notIn: [['foo', 'bar']], // check the value is not one of these
- isIn: [['foo', 'bar']], // check the value is one of these
- notContains: 'bar', // don't allow specific substrings
- len: [2,10], // only allow values with length between 2 and 10
- isUUID: 4, // only allow uuids
- isDate: true, // only allow date strings
- isAfter: "2011-11-05", // only allow date strings after a specific date
- isBefore: "2011-11-05", // only allow date strings before a specific date
- max: 23, // only allow values <= 23
- min: 23, // only allow values >= 23
- isCreditCard: true, // check for valid credit card numbers
-
- // Examples of custom validators:
- isEven(value) {
- if (parseInt(value) % 2 !== 0) {
- throw new Error('Only even values are allowed!');
- }
- }
- isGreaterThanOtherField(value) {
- if (parseInt(value) <= parseInt(this.otherField)) {
- throw new Error('Bar must be greater than otherField.');
- }
- }
- }
- }
-});
-```
-
-Note that where multiple arguments need to be passed to the built-in validation functions, the arguments to be passed must be in an array. But if a single array argument is to be passed, for instance an array of acceptable strings for `isIn`, this will be interpreted as multiple string arguments instead of one array argument. To work around this pass a single-length array of arguments, such as `[['foo', 'bar']]` as shown above.
-
-To use a custom error message instead of that provided by [validator.js](https://github.com/chriso/validator.js), use an object instead of the plain value or array of arguments, for example a validator which needs no argument can be given a custom message with
-
-```js
-isInt: {
- msg: "Must be an integer number of pennies"
-}
-```
-
-or if arguments need to also be passed add an `args` property:
-
-```js
-isIn: {
- args: [['en', 'zh']],
- msg: "Must be English or Chinese"
-}
-```
-
-When using custom validator functions the error message will be whatever message the thrown `Error` object holds.
-
-See [the validator.js project](https://github.com/chriso/validator.js) for more details on the built in validation methods.
-
-**Hint:** You can also define a custom function for the logging part. Just pass a function. The first parameter will be the string that is logged.
-
-### `allowNull` interaction with other validators
-
-If a particular field of a model is set to not allow null (with `allowNull: false`) and that value has been set to `null`, all validators will be skipped and a `ValidationError` will be thrown.
-
-On the other hand, if it is set to allow null (with `allowNull: true`) and that value has been set to `null`, only the built-in validators will be skipped, while the custom validators will still run.
-
-This means you can, for instance, have a string field which validates its length to be between 5 and 10 characters, but which also allows `null` (since the length validator will be skipped automatically when the value is `null`):
-
-```js
-class User extends Model {}
-User.init({
- username: {
- type: DataTypes.STRING,
- allowNull: true,
- validate: {
- len: [5, 10]
- }
- }
-}, { sequelize });
-```
-
-You also can conditionally allow `null` values, with a custom validator, since it won't be skipped:
-
-```js
-class User extends Model {}
-User.init({
- age: DataTypes.INTEGER,
- name: {
- type: DataTypes.STRING,
- allowNull: true,
- validate: {
- customValidator(value) {
- if (value === null && this.age !== 10) {
- throw new Error("name can't be null unless age is 10");
- }
- })
- }
- }
-}, { sequelize });
-```
-
-You can customize `allowNull` error message by setting the `notNull` validator:
-
-```js
-class User extends Model {}
-User.init({
- name: {
- type: DataTypes.STRING,
- allowNull: false,
- validate: {
- notNull: {
- msg: 'Please enter your name'
- }
- }
- }
-}, { sequelize });
-```
-
-### Model-wide validations
-
-Validations can also be defined to check the model after the field-specific validators. Using this you could, for example, ensure either neither of `latitude` and `longitude` are set or both, and fail if one but not the other is set.
-
-Model validator methods are called with the model object's context and are deemed to fail if they throw an error, otherwise pass. This is just the same as with custom field-specific validators.
-
-Any error messages collected are put in the validation result object alongside the field validation errors, with keys named after the failed validation method's key in the `validate` option object. Even though there can only be one error message for each model validation method at any one time, it is presented as a single string error in an array, to maximize consistency with the field errors.
-
-An example:
-
-```js
-class Place extends Model {}
-Place.init({
- name: DataTypes.STRING,
- address: DataTypes.STRING,
- latitude: {
- type: DataTypes.INTEGER,
- validate: {
- min: -90,
- max: 90
- }
- },
- longitude: {
- type: DataTypes.INTEGER,
- validate: {
- min: -180,
- max: 180
- }
- },
-}, {
- sequelize,
- validate: {
- bothCoordsOrNone() {
- if ((this.latitude === null) !== (this.longitude === null)) {
- throw new Error('Either both latitude and longitude, or neither!');
- }
- }
- }
-})
-```
-
-In this simple case an object fails validation if either latitude or longitude is given, but not both. If we try to build one with an out-of-range latitude and no longitude, `somePlace.validate()` might return:
-
-```js
-{
- 'latitude': ['Invalid number: latitude'],
- 'bothCoordsOrNone': ['Either both latitude and longitude, or neither!']
-}
-```
-
-Such validation could have also been done with a custom validator defined on a single attribute (such as the `latitude` attribute, by checking `(value === null) !== (this.longitude === null)`), but the model-wide validation approach is cleaner.
diff --git a/docs/databases/_category_.json b/docs/databases/_category_.json
new file mode 100644
index 00000000..33923e20
--- /dev/null
+++ b/docs/databases/_category_.json
@@ -0,0 +1,9 @@
+{
+ "position": 3,
+ "label": "Databases",
+ "collapsible": true,
+ "collapsed": true,
+ "link": {
+ "type": "generated-index"
+ }
+}
diff --git a/docs/databases/_connection-options.md b/docs/databases/_connection-options.md
new file mode 100644
index 00000000..79cb1736
--- /dev/null
+++ b/docs/databases/_connection-options.md
@@ -0,0 +1,5 @@
+Connection Options are used to configure a connection to the database.
+
+The simplest way to use them is at the root of the configuration object. These options can also be
+used in the [`replication`](../other-topics/read-replication.md) option to customize the connection for each replica,
+and can be modified by the [`beforeConnect`](../other-topics/hooks.mdx) hook on a connection-by-connection basis.
diff --git a/docs/databases/db2.mdx b/docs/databases/db2.mdx
new file mode 100644
index 00000000..2e1afe04
--- /dev/null
+++ b/docs/databases/db2.mdx
@@ -0,0 +1,54 @@
+---
+title: DB2 for LUW
+sidebar_position: 1
+---
+
+# Sequelize for DB2 for Linux, Unix, and Windows
+
+:::info Version Compatibility
+
+See [Releases](/releases#db2-for-luw-support-table) to see which versions of DB2 for LUW are supported.
+
+:::
+
+To use Sequelize with DB2 for LUW, you need to install the `@sequelize/db2` dialect package:
+
+```bash npm2yarn
+npm i @sequelize/db2
+```
+
+Then use the `Db2Dialect` class as the dialect option in the Sequelize constructor:
+
+```ts
+import { Sequelize } from '@sequelize/core';
+import { Db2Dialect } from '@sequelize/db2';
+
+const sequelize = new Sequelize({
+ dialect: Db2Dialect,
+ database: 'mydb',
+ user: 'myuser',
+ password: 'mypass',
+ hostname: 'localhost',
+ port: 50000,
+ ssl: true,
+});
+```
+
+## Connection Options
+
+import ConnectionOptions from './_connection-options.md';
+
+
+
+The following options are accepted by the DB2 for LUW dialect:
+
+| Option | Description |
+|------------------------|-------------------------------------------------|
+| `database` | ODBC "DATABASE" parameter |
+| `username` | ODBC "UID" parameter |
+| `password` | ODBC "PWD" parameter |
+| `hostname` | ODBC "HOSTNAME" parameter |
+| `port` | ODBC "PORT" parameter |
+| `ssl` | Sets ODBC "Security" parameter to SSL when true |
+| `sslServerCertificate` | ODBC "SSLServerCertificate" parameter |
+| `odbcOptions` | Additional ODBC parameters. |
diff --git a/docs/databases/ibmi.mdx b/docs/databases/ibmi.mdx
new file mode 100644
index 00000000..37422a47
--- /dev/null
+++ b/docs/databases/ibmi.mdx
@@ -0,0 +1,58 @@
+---
+title: DB2 for IBM i
+sidebar_position: 1
+---
+
+# Sequelize for DB2 for IBM i
+
+:::danger
+
+Our implementation of DB2 for IBM i is not integration tested against an actual database.
+As such, we cannot guarantee that it will work as expected, nor its stability.
+
+We rely on the community to help us improve this dialect.
+
+:::
+
+:::info Version Compatibility
+
+See [Releases](/releases#db2-for-ibm-i-support-table) to see which versions of DB2 for IBM i are supported.
+
+:::
+
+To use Sequelize with DB2 for IBM i, you need to install the `@sequelize/db2-ibmi` dialect package:
+
+```bash npm2yarn
+npm i @sequelize/db2-ibmi
+```
+
+Then use the `IbmiDialect` class as the dialect option in the Sequelize constructor:
+
+```ts
+import { Sequelize } from '@sequelize/core';
+import { IbmiDialect } from '@sequelize/db2-ibmi';
+
+const sequelize = new Sequelize({
+ dialect: IbmiDialect,
+ odbcConnectionString: 'DSN=MYDSN;UID=myuser;PWD=mypassword',
+ connectionTimeout: 60,
+});
+```
+
+## Connection Options
+
+import ConnectionOptions from './_connection-options.md';
+
+
+
+The following options are accepted by the DB2 for IBM i dialect:
+
+| Option | Description |
+|------------------------|---------------------------------------------------------------------------------------------------------------|
+| `connectionTimeout` | The number of seconds to wait for a request on the connection to complete before returning to the application |
+| `loginTimeout` | The number of seconds to wait for a login request to complete before returning to the application |
+| `odbcConnectionString` | The connection string to connect to the database. If provided, the options below are not necessary. |
+| `dataSourceName` | The ODBC "DSN" part of the connection string. |
+| `username` | The ODBC "UID" part of the connection string. |
+| `system` | The ODBC "SYSTEM" part of the connection string. |
+| `password` | The ODBC "PWD" part of the connection string. |
diff --git a/docs/databases/mariadb.mdx b/docs/databases/mariadb.mdx
new file mode 100644
index 00000000..41ef1d11
--- /dev/null
+++ b/docs/databases/mariadb.mdx
@@ -0,0 +1,101 @@
+---
+title: MariaDB
+sidebar_position: 1
+---
+
+# Sequelize for MariaDB
+
+:::info Version Compatibility
+
+See [Releases](/releases#mariadb-support-table) to see which versions of MariaDB are supported.
+
+:::
+
+To use Sequelize with MariaDB, you need to install the `@sequelize/mariadb` dialect package:
+
+```bash npm2yarn
+npm i @sequelize/mariadb
+```
+
+Then use the `MariaDBDialect` class as the dialect option in the Sequelize constructor:
+
+```ts
+import { Sequelize } from '@sequelize/core';
+import { MariaDBDialect } from '@sequelize/mariadb';
+
+const sequelize = new Sequelize({
+ dialect: MariaDBDialect,
+ database: 'mydb',
+ user: 'myuser',
+ password: 'mypass',
+ host: 'localhost',
+ port: 3306,
+ showWarnings: true,
+ connectTimeout: 1000,
+});
+```
+
+## Connection Options
+
+import ConnectionOptions from './_connection-options.md';
+
+
+
+The following options are passed as-is to the `mariadb` package that Sequelize uses to connect to MariaDB.
+Please refer to the [MariaDB documentation](https://github.com/mariadb-corporation/mariadb-connector-nodejs/blob/b65aca10b77f5ede83f16a8edd0537b2ef12a16f/documentation/connection-options.md) for more information about what each of these options do.
+
+For convenience, here is an edited copy of the documentation that only includes the options that are accepted by Sequelize:
+
+| Option | Description | Type | Default |
+|:--------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------:|:------------------:|
+| `user` | User to access database | string | |
+| `password` | User password | string | |
+| `host` | IP address or DNS of database server. *Not used when using the `socketPath` option* | string | "localhost" |
+| `port` | Database server port number | integer | 3306 |
+| `database` | Default database to use when establishing the connection | string | |
+| `socketPath` | Permit connecting to the database via Unix domain socket or named pipe, if the server allows it | string | |
+| `compress` | Compress exchanges with database using gzip. This can give you better performance when accessing a database in a different location. | boolean | false |
+| `connectTimeout` | Connection timeout in milliseconds | integer | 1000 |
+| `socketTimeout` | Socket timeout in milliseconds after the connection is established | integer | 0 |
+| `maxAllowedPacket` | permit to indicate server global variable [max_allowed_packet](https://mariadb.com/kb/en/library/server-system-variables/#max_allowed_packet) value to ensure efficient batching. default is 4Mb. see [batch documentation] | integer | 4196304 |
+| `prepareCacheLength` | Define prepare LRU cache length. 0 means no cache | int | 256 |
+| `ssl` | SSL options. See [SSL options in the MariaDB documentation] | boolean \| object | false |
+| `charset` | Protocol character set used with the server. Connection collation will be the [default collation] associated with charset. It's mainly used for micro-optimizations. The default is often sufficient. | string | UTF8MB4 |
+| `collation` | (used in replacement of charset) Permit to defined collation used for connection. This will defined the charset encoding used for exchanges with database and defines the order used when comparing strings. It's mainly used for micro-optimizations | string | UTF8MB4_UNICODE_CI |
+| `debug` | Logs all exchanges with the server. Displays in hexa. | boolean | false |
+| `debugLen` | String length of logged message / error or trace | integer | 256 |
+| `logParam` | indicate if parameters must be logged by query logger. | boolean | false |
+| `foundRows` | When enabled, the update number corresponds to update rows. When disabled, it indicates the real rows changed. | boolean | true |
+| `multipleStatements` | Allows you to issue several SQL statements in a single `query()` call. (That is, `INSERT INTO a VALUES('b'); INSERT INTO c VALUES('d');`). This may be a **security risk** as it allows for SQL Injection attacks. | boolean | false |
+| `permitLocalInfile` | Allows the use of `LOAD DATA INFILE` statements. Loading data from a file from the client may be a security issue, as a man-in-the-middle proxy server can change the actual file the server loads. Being able to execute a query on the client gives you access to files on the client. | boolean | false |
+| `pipelining` | Sends queries one by one without waiting on the results of the previous entry. For more information, see [Pipelining]( | boolean | true |
+| `trace` | Adds the stack trace at the time of query creation to the error stack trace, making it easier to identify the part of the code that issued the query. Note: This feature is disabled by default due to the performance cost of stack creation. Only turn it on when you need to debug issues. | boolean | false |
+| `connectAttributes` | Sends information, (client name, version, operating system, Node.js version, and so on) to the [Performance Schema](https://mariadb.com/kb/en/library/performance-schema-session_connect_attrs-table/). When enabled, the Connector sends JSON attributes in addition to the defaults. | boolean \| object | false |
+| `sessionVariables` | Permit to set session variables when connecting. Example: `sessionVariables: { idle_transaction_timeout: 10000 }` | object |
+| `initSql` | When a connection is established, permit to execute commands before using connection | string | array |
+| `bulk` | disabled bulk command in batch | boolean |
+| `forceVersionCheck` | Force server version check by explicitly using SELECT VERSION(), not relying on server initial packet. | boolean | false |
+| `checkDuplicate` | Indicate to throw an exception if result-set will not contain some data due to having duplicate identifier. JSON cannot have multiple identical key, so query like `SELECT 1 as i, 2 as i` cannot result in `{ i:1, i:2 }`, 'i:1' would be skipped. When `checkDuplicate` is enable (default) driver will throw an error if some data are skipped. | boolean | true |
+| `keepAliveDelay` | permit to enable socket keep alive, setting delay. 0 means not enabled. Keep in mind that this don't reset server [`@@wait_timeout`](https://mariadb.com/kb/en/library/server-system-variables/#wait_timeout) (use pool option idleTimeout for that). in ms | integer | |
+| `rsaPublicKey` | Indicate path/content to MySQL server RSA public key. | string | |
+| `cachingRsaPublicKey` | Indicate path/content to MySQL server caching RSA public key. | string | |
+| `allowPublicKeyRetrieval` | Indicate that if `rsaPublicKey` or `cachingRsaPublicKey` public key are not provided, if client can ask server to send public key. | boolean | false |
+| `stream` | permits to set a function with parameter to set stream | function | |
+| `metaEnumerable` | make resultset meta property enumerable | boolean | false |
+| `infileStreamFactory` | When LOAD LOCAL command executed, permit to set a callback function of type `(filepath?: string) => stream.Readable`. Connector will then not send file from LOAD LOCAL, but Readable content. This can permit to set extra validation of file path for example. | function | |
+| `logPackets` | Debug option : permit to save last exchanged packet. Error messages will display those last exchanged packet. | boolean | false |
+| `debugCompress` | This will print all incoming and outgoing compressed packets on stdout. | boolean | false |
+| `timeout` | Command execution timeout | number | |
+
+## Other MariaDB Options
+
+The following options are also available for MariaDB:
+
+| Option | Description |
+|----------------|--------------------------------------------------------------------------------------------------------------------------|
+| `showWarnings` | If `true`, warnings produced during the execution of a query will be sent to the `logging` callback. Default is `false`. |
+
+[batch documentation]: https://github.com/mariadb-corporation/mariadb-connector-nodejs/blob/b65aca10b77f5ede83f16a8edd0537b2ef12a16f/documentation/batch.md
+[SSL options in the MariaDB documentation]: https://github.com/mariadb-corporation/mariadb-connector-nodejs/blob/b65aca10b77f5ede83f16a8edd0537b2ef12a16f/documentation/connection-options.md#configuration
+[default collation]: https://github.com/mariadb-corporation/mariadb-connector-nodejs/blob/master/lib/const/collations.js#L372
+[Pipelining]: https://github.com/mariadb-corporation/mariadb-connector-nodejs/blob/b65aca10b77f5ede83f16a8edd0537b2ef12a16f/documentation/pipelining.md
diff --git a/docs/databases/mssql.mdx b/docs/databases/mssql.mdx
new file mode 100644
index 00000000..a9ab1175
--- /dev/null
+++ b/docs/databases/mssql.mdx
@@ -0,0 +1,109 @@
+---
+title: MS SQL Server
+sidebar_position: 1
+---
+
+# Sequelize for Microsoft SQL Server
+
+:::info Version Compatibility
+
+See [Releases](/releases#microsoft-sql-server-mssql-support-table) to see which versions of SQL Server are supported.
+
+:::
+
+To use Sequelize with Microsoft SQL Server, you need to install the `@sequelize/mssql` dialect package:
+
+```bash npm2yarn
+npm i @sequelize/mssql
+```
+
+Then use the `MsSqlDialect` class as the dialect option in your Sequelize instance:
+
+```ts
+import { Sequelize } from '@sequelize/core';
+import { MsSqlDialect } from '@sequelize/mssql';
+
+const sequelize = new Sequelize({
+ dialect: MsSqlDialect,
+ server: 'localhost',
+ port: 1433,
+ database: 'database',
+ authentication: {
+ type: 'default',
+ options: {
+ userName: 'username',
+ password: 'password',
+ },
+ },
+});
+```
+
+## Connection Options
+
+import ConnectionOptions from './_connection-options.md';
+
+
+
+The following options are passed as-is to the `tedious` package that Sequelize uses to connect to SQL Server.
+Please refer to the [Tedious documentation](https://tediousjs.github.io/tedious/api-connection.html#function_newConnection) for more information about what each of these options do.
+
+For convenience, here is an edited copy of the documentation that only includes the options that are accepted by Sequelize:
+
+| Option | Description |
+|:------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `server` | Hostname to connect to. |
+| `localAddress` | Network interface (ip address) to use when connecting to SQL Server. |
+| `database` | Database to connect to. |
+| `port` | Port to connect to (default: 1433). Mutually exclusive with `instanceName`. |
+| `instanceName` | The instance name to connect to. The SQL Server Browser service must be running on the database server, and UDP port 1434 on the database server must be reachable. Mutually exclusive with `port`. |
+| `authentication` | The authentication options. Please see which sub-options can be used on [the Tedious documentation] |
+| `abortTransactionOnError` | A boolean determining whether to rollback a transaction automatically if any error is encountered during the given transaction's execution. This sets the value for SET XACT_ABORT during the initial SQL phase of a connection ([documentation](https://msdn.microsoft.com/en-us/library/ms188792.aspx)). |
+| `appName` | Application name used for identifying a specific application in profiling, logging or tracing tools of SQL Server. (default: Tedious) |
+| `cancelTimeout` | The number of milliseconds before the cancel (abort) of a request is considered failed (default: 5000). |
+| `connectionRetryInterval` | Number of milliseconds before retrying to establish connection, in case of transient failure. (default: 500) |
+| `connectTimeout` | The number of milliseconds before the attempt to connect is considered failed (default: 15000). |
+| `connectionIsolationLevel` | The default isolation level for new connections. All out-of-transaction queries are executed with this setting. The isolation levels are available from the `TEDIOUS_ISOLATION_LEVEL` export. (default: `READ_COMMITED`). |
+| `cryptoCredentialsDetails` | When `encrypt` is set to true, an object may be supplied that will be used as the secureContext field when creating a [`TLSSocket`](https://nodejs.org/docs/latest/api/tls.html#class-tlstlssocket). The available options are listed under [`tls.createSecureContext`](https://nodejs.org/docs/latest/api/tls.html#tlscreatesecurecontextoptions). |
+| `datefirst` | An integer representing the first day of the week. (default: 7) |
+| `dateFormat` | A string representing the date format. (default: `mdy`) |
+| `debug` | See `options.debug` in [the Tedious documentation] |
+| `enableAnsiNull` | Controls the way null values should be used during comparison operation. (default: true) |
+| `enableAnsiPadding` | Controls if padding should be applied for values shorter than the size of defined column. (default: true) |
+| `enableAnsiWarnings` | If true, SQL Server will follow ISO standard behavior during various error conditions. For details, see [documentation](https://docs.microsoft.com/en-us/sql/t-sql/statements/set-ansi-warnings-transact-sql). (default: true) |
+| `enableArithAbort` | Ends a query when an overflow or divide-by-zero error occurs during query execution. See [documentation](https://docs.microsoft.com/en-us/sql/t-sql/statements/set-arithabort-transact-sql?view=sql-server-2017) for more details. (default: true) |
+| `enableConcatNullYieldsNull` | If true, concatenating a null value with a string results in a NULL value. (default: true) |
+| `enableCursorCloseOnCommit` | If true, cursors will be closed when a transaction is committed or rolled back. (default: null) |
+| `enableImplicitTransactions` | Sets the connection to either implicit or autocommit transaction mode. (default: false) |
+| `enableNumericRoundabort` | If false, error is not generated during loss of precession. (default: false) |
+| `encrypt` | A string value set to `'strict'` enables the TDS 8.0 protocol. Otherwise, encrypt can be set to a boolean value which determines whether or not the connection will be encrypted under the TDS 7.x protocol. (default: true) |
+| `fallbackToDefaultDb` | By default, if the database requested by options.database cannot be accessed, the connection will fail with an error. However, if this is set to true, then the user's default database will be used instead (Default: false). |
+| `language` | Specifies the language environment for the session. The session language determines the datetime formats and system messages. (default: us_english). |
+| `maxRetriesOnTransientErrors` | The maximum number of connection retries for transient errors. (default: 3). |
+| `multiSubnetFailover` | Sets the `MultiSubnetFailover = True` parameter, which can help minimize the client recovery latency when failovers occur. (default: false). |
+| `packetSize` | The size of TDS packets (subject to negotiation with the server). Should be a power of 2. (default: 4096). |
+| `readOnlyIntent` | A boolean, determining whether the connection will request read only access from a SQL Server Availability Group. For more information, see here. (default: false). |
+| `requestTimeout` | The number of milliseconds before a request is considered failed, or 0 for no timeout (default: 15000). |
+| `tdsVersion` | The version of TDS to use. If server doesn't support specified version, negotiated version is used instead. The versions are available from the `TDS_VERSION` export. (default: 7_4). |
+| `textsize` | Specifies the size of varchar(max), nvarchar(max), varbinary(max), text, ntext, and image data returned by a SELECT statement. (default: 2147483647) (Textsize is set by a numeric value.) |
+| `trustServerCertificate` | If "true", the SQL Server SSL certificate is automatically trusted when the communication layer is encrypted using SSL. If "false", the SQL Server validates the server SSL certificate. If the server certificate validation fails, the driver raises an error and terminates the connection. Make sure the value passed to serverName exactly matches the Common Name (CN) or DNS name in the Subject Alternate Name in the server certificate for an SSL connection to succeed. (default: true). |
+
+[the Tedious documentation]: https://tediousjs.github.io/tedious/api-connection.html#function_newConnection
+
+### Domain Account
+
+In order to connect with a domain account, use the following format.
+
+```ts
+const sequelize = new Sequelize({
+ dialect: MsSqlDialect,
+ instanceName: 'SQLEXPRESS',
+ authentication: {
+ type: 'ntlm',
+ options: {
+ domain: 'yourDomain',
+ userName: 'username',
+ password: 'password',
+ },
+ },
+});
+```
diff --git a/docs/databases/mysql.mdx b/docs/databases/mysql.mdx
new file mode 100644
index 00000000..5606422f
--- /dev/null
+++ b/docs/databases/mysql.mdx
@@ -0,0 +1,91 @@
+---
+title: MySQL
+sidebar_position: 1
+---
+
+# Sequelize for MySQL
+
+:::info Version Compatibility
+
+See [Releases](/releases#mysql-support-table) to see which versions of MySQL are supported.
+
+:::
+
+To use Sequelize with MySQL, you need to install the `@sequelize/mysql` dialect package:
+
+```bash npm2yarn
+npm i @sequelize/mysql
+```
+
+Then use the `MySqlDialect` class as the dialect option in the Sequelize constructor:
+
+```ts
+import { Sequelize } from '@sequelize/core';
+import { MySqlDialect } from '@sequelize/mysql';
+
+const sequelize = new Sequelize({
+ dialect: MySqlDialect,
+ database: 'mydb',
+ user: 'myuser',
+ password: 'mypass',
+ host: 'localhost',
+ port: 3306,
+});
+```
+
+## Connection Options
+
+import ConnectionOptions from './_connection-options.md';
+
+
+
+The following options are passed as-is to the `mysql2` package that Sequelize uses to connect to MySQL.
+Please refer to the [mysql2 documentation](https://sidorares.github.io/node-mysql2/docs) for more information about what each of these options do.
+
+For convenience, here is an edited copy of the documentation that only includes the options that are accepted by Sequelize:
+
+| Option | Description |
+|-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `database` | Name of the database to use for this connection |
+| `user` | The MySQL user to authenticate as |
+| `port` | The port number to connect to. (Default: 3306) |
+| `host` | The hostname of the database you are connecting to. (Default: localhost) |
+| `localAddress` | The source IP address to use for TCP connection |
+| `password` | The password of that MySQL user |
+| `password1` | Alias for the MySQL user password. Makes a bit more sense in a multifactor authentication setup (see "password2" and "password3") |
+| `password2` | 2nd factor authentication password. Mandatory when the authentication policy for the MySQL user account requires an additional authentication method that needs a password. https://dev.mysql.com/doc/refman/8.0/en/multifactor-authentication.html |
+| `password3` | 3rd factor authentication password. Mandatory when the authentication policy for the MySQL user account requires two additional authentication methods and the last one needs a password. https://dev.mysql.com/doc/refman/8.0/en/multifactor-authentication.html |
+| `passwordSha1` | _no documentation available_ |
+| `socketPath` | The path to a unix domain socket to connect to. When used host and port are ignored. |
+| `ssl` | object with ssl parameters or a string containing name of ssl profile |
+| `charset` | The charset for the connection. This is called 'collation' in the SQL-level of MySQL (like utf8_general_ci). If a SQL-level charset is specified (like utf8mb4) then the default collation for that charset is used. (Default: 'UTF8_GENERAL_CI') |
+| `compress` | _no documentation available_ |
+| `trace` | Generates stack traces on Error to include call site of library entrance ('long stack traces'). Slight performance penalty for most calls. (Default: true) |
+| `enableKeepAlive` | Enable keep-alive on the socket. (Default: true) |
+| `isServer` | _no documentation available_ |
+| `insecureAuth` | Allow connecting to MySQL instances that ask for the old (insecure) authentication method. (Default: false) |
+| `multipleStatements` | Allow multiple mysql statements per query. Be careful with this, it exposes you to SQL injection attacks. (Default: false) |
+| `waitForConnections` | _no documentation available_ |
+| `connectionLimit` | _no documentation available_ |
+| `connectTimeout` | The milliseconds before a timeout occurs during the initial connection to the MySQL server. (Default: 10 seconds) |
+| `charsetNumber` | _no documentation available_ |
+| `maxIdle` | _no documentation available_ |
+| `queueLimit` | _no documentation available_ |
+| `idleTimeout` | _no documentation available_ |
+| `maxPreparedStatements` | _no documentation available_ |
+| `keepAliveInitialDelay` | If keep-alive is enabled users can supply an initial delay. (Default: 0) |
+| `infileStreamFactory` | By specifying a function that returns a readable stream, an arbitrary stream can be sent when sending a local fs file. |
+| `flags` | List of connection flags to use other than the default ones. It is also possible to denylist default ones |
+| `authSwitchHandler` | _no documentation available_ |
+| `connectAttributes` | _no documentation available_ |
+| `authPlugins` | _no documentation available_ |
+| `debug` | This will print all incoming and outgoing packets on stdout. You can also restrict debugging to packet types by passing an array of types (strings) to debug; |
+| `stream` | _no documentation available_ |
+
+## Other MySQL Options
+
+The following options are also available for MySQL:
+
+| Option | Description |
+|----------------|--------------------------------------------------------------------------------------------------------------------------|
+| `showWarnings` | If `true`, warnings produced during the execution of a query will be sent to the `logging` callback. Default is `false`. |
diff --git a/docs/databases/new.md b/docs/databases/new.md
new file mode 100644
index 00000000..83241e8d
--- /dev/null
+++ b/docs/databases/new.md
@@ -0,0 +1,65 @@
+---
+sidebar_position: 2
+---
+
+# Other databases
+
+If you are using a database that is not currently supported by Sequelize, there are a few options available to you:
+
+- [Request support for the new dialect](#requesting-support-for-a-new-dialect)
+- [Contribute a new dialect](#contributing-a-new-dialect)
+- [Create a third-party dialect](#creating-a-third-party-dialect)
+
+## Requesting support for a new dialect
+
+You can request support for a new dialect by creating a feature request on the [Sequelize repository](https://github.com/sequelize/sequelize).
+Make sure to verify that there isn't already an open issue for the dialect you are requesting. [View the list of requests for new dialects here](https://github.com/sequelize/sequelize/issues?q=is%3Aopen+is%3Aissue+label%3A%22dialect%3A+new%22).
+
+We have conditions for accepting new dialects:
+
+- The database must support SQL queries.
+- The database must have an existing Node.js library we can use to connect to it.
+- We must be able to run a database instance in GitHub Codespaces, as a way to ensure we can run a database for local development.
+- We must be able to run a database instance in GitHub Actions, to integrate it in our integration tests.
+- Our integration tests must be able to run against the database in 15 minutes or less.
+
+It is also possible to sponsor the development of a new dialect. If you are interested in this option, [please contact us via email](https://github.com/sequelize/sequelize/blob/main/CONTACT.md).
+Please keep in mind that the above conditions still apply to sponsored dialects, and that implementing a new dialect can be a significant investment.
+
+If the dialect matches the requirements, we will consider implementing a new dialect once it is requested by enough users,
+or if it is sponsored.
+Individual contributors may also [contribute a new dialect](#contributing-a-new-dialect) if they wish.
+
+## Contributing a new dialect
+
+:::important
+
+We do not accept new dialects for Sequelize 6.
+
+:::
+
+If you wish to open a pull request to add a new dialect, please follow the steps below:
+
+- Make sure the dialect would match [the requirements for inclusion](#requesting-support-for-a-new-dialect) in Sequelize.
+- [Open a feature request](#requesting-support-for-a-new-dialect) for the new dialect, if one does not already exist.
+- Indicate that you are working on implementing the dialect in the feature request (even if it's not your own request).
+- Explore the source code of an existing dialect package to understand how it works. We unfortunately do not have a guide for creating a new dialect at this time.
+
+## Creating a third-party dialect
+
+:::important
+
+While we are slowly working towards providing a stable public API to create third-party dialects, many necessary APIs are
+still internal and subject to change.
+
+If you do implement a third-party dialect, please be aware that it may break in future non-major versions of Sequelize.
+
+:::
+
+If your dialect does not match the requirements for inclusion in Sequelize, you can create a third-party dialect.
+
+Each dialect is a separate package that extends Sequelize with the necessary functionality to connect to the database.
+Third-party dialects can be published on npm and used in your project like any other package.
+
+Consider exploring the source code of an existing dialect package to understand how it works.
+We unfortunately do not have a guide for creating a new dialect at this time.
diff --git a/docs/databases/postgres.mdx b/docs/databases/postgres.mdx
new file mode 100644
index 00000000..75f90e5e
--- /dev/null
+++ b/docs/databases/postgres.mdx
@@ -0,0 +1,113 @@
+---
+title: PostgreSQL
+sidebar_position: 1
+---
+
+# Sequelize for PostgreSQL
+
+:::info Version Compatibility
+
+See [Releases](/releases#postgresql-support-table) to see which versions of PostgreSQL are supported.
+
+:::
+
+To use Sequelize with PostgreSQL, you need to install the `@sequelize/postgres` dialect package:
+
+```bash npm2yarn
+npm i @sequelize/postgres
+```
+
+Then use the `PostgresDialect` class as the dialect option in the Sequelize constructor:
+
+```ts
+import { Sequelize } from '@sequelize/core';
+import { PostgresDialect } from '@sequelize/postgres';
+
+const sequelize = new Sequelize({
+ dialect: PostgresDialect,
+ database: 'mydb',
+ user: 'myuser',
+ password: 'mypass',
+ host: 'localhost',
+ port: 5432,
+ ssl: true,
+ clientMinMessages: 'notice',
+});
+```
+
+## Connection Options
+
+import ConnectionOptions from './_connection-options.md';
+
+
+
+The following options are accepted by the PostgreSQL dialect:
+
+| Option | Description |
+|---------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `database` | The name of the database to connect to. |
+| `user` | The user to authenticate as. |
+| `password` | The user's password. |
+| `host` | The host to connect to (either an IP, a domain name, or a path to a Unix socket). |
+| `port` | The port to connect to. Default is `5432`. |
+| `ssl` | The SSL configuration to use when connecting to the server. Passed directly to [`TLSSocket`](https://nodejs.org/docs/latest/api/tls.html#class-tlstlssocket), supports all [`tls.connect`](https://nodejs.org/docs/latest/api/tls.html#tlsconnectoptions-callback) options |
+| `query_timeout` | The number of milliseconds before a query call will timeout, default is no timeout. |
+| `connectionTimeoutMillis` | The number of milliseconds to wait for a connection, default is no timeout. |
+| `application_name` | Configures [the `application_name` PostgreSQL option](https://www.postgresql.org/docs/current/runtime-config-logging.html#GUC-APPLICATION-NAME) for the connection. |
+| `statement_timeout` | Configures [the `statement_timeout` PostgreSQL option](https://www.postgresql.org/docs/current/runtime-config-client.html#GUC-STATEMENT-TIMEOUT) for the connection. |
+| `idle_in_transaction_session_timeout` | Configures [the `idle_in_transaction_session_timeout` PostgreSQL option](https://www.postgresql.org/docs/current/runtime-config-client.html#GUC-IDLE-IN-TRANSACTION-SESSION-TIMEOUT) for the connection. |
+| `client_encoding` | Configures [the `client_encoding` PostgreSQL option](https://www.postgresql.org/docs/current/runtime-config-client.html#GUC-CLIENT-ENCODING) for the connection. Default is `utf8`. |
+| `lock_timeout` | Configures [the `lock_timeout` PostgreSQL option](https://www.postgresql.org/docs/current/runtime-config-client.html#GUC-LOCK-TIMEOUT) for the connection. |
+| `options` | Configures [the `options` libpq option](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-OPTIONS) for the connection. |
+| `keepAlive` | Configures [the libpq `keepalives` option](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-KEEPALIVES). Must be a boolean. |
+| `keepAliveInitialDelayMillis` | Configures [the libpq `keepalives_idle` option](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-KEEPALIVES-IDLE), but in milliseconds (will be rounded down to the nearest second). |
+
+:::info
+
+Sequelize uses the `pg` package to connect to PostgreSQL.
+Most of the above options are provided as-is to the `pg` package,
+and you can find more information about them in the [pg documentation](https://node-postgres.com/apis/client#new-client).
+
+:::
+
+### Connection via Unix Socket
+
+To connect to PostgreSQL using a Unix socket, you can use the `host` option with the absolute path to the socket file:
+
+```ts
+const sequelize = new Sequelize({
+ dialect: PostgresDialect,
+ host: '/var/run/postgresql',
+});
+```
+
+## Other PostgreSQL Options
+
+The following options are also available for PostgreSQL:
+
+| Option | Description |
+|-----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `clientMinMessages` | Configures [the `client_min_messages` PostgreSQL option](https://www.postgresql.org/docs/current/runtime-config-client.html#GUC-CLIENT-MIN-MESSAGES) for all connections. Defaults to "warning". |
+| `standardConformingStrings` | Configures [the `standard_conforming_strings` PostgreSQL option](https://www.postgresql.org/docs/current/runtime-config-compatible.html#GUC-STANDARD-CONFORMING-STRINGS) for all connections. If your PostgreSQL server is configured with `standard_conforming_strings = off`, it is extremely important to set this option to `false` to avoid SQL injection vulnerabilities. |
+| `native` | If true, Sequelize will use the `pg-native` package instead of the `pg` package. `pg-native` must be installed separately. |
+
+## Amazon Redshift
+
+:::caution
+
+While Redshift is based on PostgreSQL, it does not support the same set of features as PostgreSQL.
+
+As we do not have access to a Redshift instance, we cannot guarantee that Sequelize will work correctly with Redshift,
+and we rely on the help of the community to keep this documentation up to date.
+
+:::
+
+Redshift doesn't support `client_min_messages`, you must set it to `'ignore'`:
+
+```ts
+new Sequelize({
+ dialect: PostgresDialect,
+ // Your pg options here
+ clientMinMessages: 'ignore',
+});
+```
diff --git a/docs/databases/snowflake.mdx b/docs/databases/snowflake.mdx
new file mode 100644
index 00000000..b69d0704
--- /dev/null
+++ b/docs/databases/snowflake.mdx
@@ -0,0 +1,83 @@
+---
+title: Snowflake
+sidebar_position: 1
+---
+
+# Sequelize for Snowflake
+
+:::danger
+
+Our implementation of Snowflake is not integration tested against an actual database.
+As such, we cannot guarantee that it will work as expected, nor its stability.
+
+We rely on the community to help us improve this dialect.
+
+:::
+
+:::info Version Compatibility
+
+See [Releases](/releases#snowflake-support-table) to see which versions of Snowflake are supported.
+
+:::
+
+To use Sequelize with Snowflake, you need to install the `@sequelize/snowflake` dialect package:
+
+```bash npm2yarn
+npm i @sequelize/snowflake
+```
+
+Then use the `SnowflakeDialect` class as the dialect option in the Sequelize constructor:
+
+```ts
+import { Sequelize } from '@sequelize/core';
+import { SnowflakeDialect } from '@sequelize/snowflake';
+
+const sequelize = new Sequelize({
+ dialect: SnowflakeDialect,
+ accessUrl: 'https://myaccount.us-east-1.snowflakecomputing.com',
+ role: 'myRole',
+ warehouse: 'myWarehouse',
+ username: 'myUserName',
+ password: 'myPassword',
+ database: 'myDatabaseName',
+});
+```
+
+
+## Connection Options
+
+import ConnectionOptions from './_connection-options.md';
+
+
+
+The following options are passed as-is to the `snowflake-sdk` package that Sequelize uses to connect to Snowflake.
+Please refer to the [Snowflake documentation](https://docs.snowflake.com/en/developer-guide/node-js/nodejs-driver-options) for more information about what each of these options do.
+
+For convenience, here is an edited copy of the documentation that only includes the options that are accepted by Sequelize:
+
+| Option | Description |
+|--------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `accessUrl` | Specifies a fully-qualified endpoint for connecting to Snowflake. |
+| `account` | Snowflake Account Identifier |
+| `application` | Specifies the name of the client application connecting to Snowflake. |
+| `authenticator` | Specifies the authenticator to use for verifying user login credentials. See the [Snowflake documentation](https://docs.snowflake.com/en/developer-guide/node-js/nodejs-driver-options#authentication-options) for details |
+| `clientSessionKeepAlive` | By default, client connections typically time out approximately 3-4 hours after the most recent query was executed. If the `clientSessionKeepAlive` option is set to `true`, the client’s connection to the server will be kept alive indefinitely, even if no queries are executed. Note that [the Sequelize Pool](../other-topics/connection-pool.md) also disconnects connections if they are idle after a specified time. |
+| `clientSessionKeepAliveHeartbeatFrequency` | Sets the frequency (interval in seconds) between heartbeat messages. |
+| `database` | The default database to use for the session after connecting. |
+| `password` | Password for the user for when `authenticator` is set to `SNOWFLAKE`. |
+| `privateKey` | Specifies the private key (in PEM format) for key pair authentication. |
+| `privateKeyPass` | Specifies the passcode to decrypt the private key file, if the file is encrypted. |
+| `privateKeyPath` | Specifies the local path to the private key file (e.g. `rsa_key.p8`). |
+| `role` | The default security role to use for the session after connecting. |
+| `timeout` | Number of milliseconds to keep the connection alive with no response. Default: 60000 (1 minute). |
+| `token` | Specifies the OAuth token to use for authentication for when `authenticator` is set to `OAUTH`. |
+| `username` | The login name for your Snowflake user or your Identity Provider. |
+| `warehouse` | The default virtual warehouse to use for the session after connecting. Used for performing queries, loading data, etc. |
+
+## Other Snowflake Options
+
+The following options are also available for Snowflake:
+
+| Option | Description |
+|----------------|--------------------------------------------------------------------------------------------------------------------------|
+| `showWarnings` | If `true`, warnings produced during the execution of a query will be sent to the `logging` callback. Default is `false`. |
diff --git a/docs/databases/sqlite.mdx b/docs/databases/sqlite.mdx
new file mode 100644
index 00000000..c7fe0d48
--- /dev/null
+++ b/docs/databases/sqlite.mdx
@@ -0,0 +1,64 @@
+---
+title: SQLite
+sidebar_position: 1
+---
+
+# Sequelize for SQLite
+
+To use Sequelize with SQLite, you need to install the `@sequelize/sqlite` dialect package:
+
+```bash npm2yarn
+npm i @sequelize/sqlite3
+```
+
+Then use the `SqliteDialect` class as the dialect option in the Sequelize constructor:
+
+```ts
+import { Sequelize } from '@sequelize/core';
+import { SqliteDialect } from '@sequelize/sqlite3';
+
+const sequelize = new Sequelize({
+ dialect: SqliteDialect,
+ storage: 'sequelize.sqlite',
+});
+```
+
+## Connection Options
+
+import ConnectionOptions from './_connection-options.md';
+
+
+
+The following options are accepted by the SQLite dialect:
+
+| Option | Description |
+|------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `storage` | Path to the SQLite database file. |
+| `mode` | An integer bit flag that represents the mode to open the database connection with. The mode can be a union (using `\|`) of `OPEN_READONLY`, `OPEN_READWRITE`, `OPEN_CREATE`, `OPEN_FULLMUTEX`, `OPEN_URI`, `OPEN_SHAREDCACHE`, `OPEN_PRIVATECACHE`, each of which are exposed by `@sequelize/sqlite`. Refer to the [SQLite documentation](https://www.sqlite.org/c3ref/open.html) to learn what each of these flags do. |
+| `password` | The "PRAGMA KEY" password to use for the connection, if using plugins like `sqlcipher`. |
+
+### Temporary Storages
+
+SQLite supports two types of temporary storages:
+
+- Set `storage` to an empty string to use a disk-based temporary storage.
+- Set `storage` to `':memory:'` to use a memory-based temporary storage.
+
+In both cases, the database will be destroyed when the connection is closed. As such, using temporary storage
+requires configuring [the Connection Pool](../other-topics/connection-pool.md) to keep a single connection open, using the following configuration:
+
+```ts
+const sequelize = new Sequelize({
+ dialect: SqliteDialect,
+ storage: ':memory:', // or ''
+ pool: { max: 1, idle: Infinity, maxUses: Infinity },
+});
+```
+
+## Other SQLite Options
+
+The following options are also available for SQLite:
+
+| Option | Description |
+|---------------|--------------------------------------------------------|
+| `foreignKeys` | If set to false, SQLite will not enforce foreign keys. |
diff --git a/docs/getting-started.mdx b/docs/getting-started.mdx
index 2e7ea3f4..a34c7505 100644
--- a/docs/getting-started.mdx
+++ b/docs/getting-started.mdx
@@ -1,111 +1,70 @@
---
sidebar_position: 2
-title: Getting Started
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
+# Getting Started
+
In this tutorial you will learn to make a simple setup of Sequelize.
## Installing
Sequelize is available via [npm](https://www.npmjs.com/package/@sequelize/core) (or [yarn](https://yarnpkg.com/package/@sequelize/core)).
+:::info
+
The following commands will install Sequelize v7.
If you're looking for Sequelize v6 (published as `sequelize` instead of `@sequelize/core`),
[visit the v6 documentation](pathname:///docs/v6/getting-started/)
-
-
-
-```bash
-# This will install v6, the latest stable release of Sequelize
-npm i sequelize
-
-# This will install v7, the latest alpha release of Sequelize
-npm i @sequelize/core
-```
-
-
-
-
-```bash
-# This will install v6, the latest stable release of Sequelize
-yarn add sequelize
+:::
-# This will install v7, the latest alpha release of Sequelize
-yarn add @sequelize/core
+```bash npm2yarn
+# This will install Sequelize 7, the latest alpha release of Sequelize
+npm i @sequelize/core@alpha
```
-
-
+## Connecting to a database
-Next you'll need to manually install the driver for your database of choice:
+To connect to the database, you must create a Sequelize instance, and pass it the database configuration
+information, such as the dialect, the host, and the username and password.
-
-
+Each dialect supports a different set of options. Follow the links below to see how to connect to your database:
-```bash
-# using npm
-npm i pg pg-hstore # PostgreSQL
-npm i mysql2 # MySQL
-npm i mariadb # MariaDB
-npm i sqlite3 # SQLite
-npm i tedious # Microsoft SQL Server
-npm i ibm_db # DB2
-npm i odbc # IBM i
-```
+- [PostgreSQL](./databases/postgres.mdx)
+- [MySQL](./databases/mysql.mdx)
+- [MariaDB](./databases/mariadb.mdx)
+- [SQLite](./databases/sqlite.mdx)
+- [Microsoft SQL Server](./databases/mssql.mdx)
+- [DB2 for LUW](./databases/db2.mdx)
+- [DB2 for IBM i](./databases/ibmi.mdx)
+- [Snowflake](./databases/snowflake.mdx)
-
-
-
-```bash
-yarn add pg pg-hstore # PostgreSQL
-yarn add mysql2 # MySQL
-yarn add mariadb # MariaDB
-yarn add sqlite3 # SQLite
-yarn add tedious # Microsoft SQL Server
-yarn add ibm_db # DB2
-yarn add odbc # IBM i
-```
+If your database is not listed above, Sequelize does not support it out of the box.
+However, Sequelize is extensible, and you can create your own dialect, or find a community-maintained one.
+For more information, see our documentation about [supporting other databases](./databases/new.md).
-
-
-
-## Connecting to a database
-
-To connect to the database, you must create a Sequelize instance.
-This can be done by either passing the connection parameters separately to the Sequelize constructor or by passing a single connection URI:
+Here is a short example of how to connect to a SQLite database:
```javascript
-const { Sequelize } = require('@sequelize/core');
-
-// Option 1: Passing a connection URI
-const sequelize = new Sequelize('sqlite::memory:') // Example for sqlite
-const sequelize = new Sequelize('postgres://user:pass@example.com:5432/dbname') // Example for postgres
+import { Sequelize } from '@sequelize/core';
+import { SqliteDialect } from '@sequelize/sqlite3';
-// Option 2: Passing parameters separately (sqlite)
const sequelize = new Sequelize({
- dialect: 'sqlite',
- storage: 'path/to/database.sqlite'
-});
-
-// Option 3: Passing parameters separately (other dialects)
-const sequelize = new Sequelize('database', 'username', 'password', {
- host: 'localhost',
- // one of our supported dialects:
- // 'mysql', 'mariadb', 'postgres', 'mssql', 'sqlite', 'snowflake', 'db2' or 'ibmi'
- dialect: 'postgres'
+ dialect: SqliteDialect,
});
```
-The Sequelize constructor accepts a lot of options.
-They are documented in the [API Reference](pathname:///api/v7/classes/Sequelize.html#constructor).
+The Sequelize constructor accepts many options.
+They are documented in the [API Reference](pathname:///api/v7/classes/_sequelize_core.index.Sequelize.html#constructor).
### Testing the connection
-You can use the `.authenticate()` function to test if the connection is OK:
+You can use the `.authenticate()` function to test if the connection is OK.
+Note that this is completely optional, but recommended as Sequelize fetches your Database version on the first connection.
+That version is then used to determine which SQL features are available.
```js
try {
@@ -118,10 +77,10 @@ try {
### Closing the connection
-Sequelize will keep the connection open by default, and use the same connection for all queries.
-If you need to close the connection, call `sequelize.close()` (which is asynchronous and returns a Promise).
+Sequelize uses a connection pool to manage connections to the database. This means some connection can remain open even after you're done with them.
+If you wish to gracefully shut down your application, you can use [`sequelize.close()`](pathname:///api/v7/classes/_sequelize_core.index.Sequelize.html#close) to close all active connections.
-:::note
+:::caution
Once `sequelize.close()` has been called, it's impossible to open a new connection. You will need to create a new Sequelize instance to access your database again.
@@ -129,50 +88,62 @@ Once `sequelize.close()` has been called, it's impossible to open a new connecti
## Terminology convention
-Observe that, in the examples above, `Sequelize` refers to the library itself while `sequelize` refers to an instance of Sequelize,
-which represents a connection to one database. This is the recommended convention and it will be followed throughout the documentation.
+Observe that, in the examples above, `Sequelize` refers to the library itself while the lowercase `sequelize` refers to an instance of Sequelize.
+This is the recommended convention, and it will be followed throughout the documentation.
-## Tip for reading the docs
+## TypeScript
-You are encouraged to run code examples locally while reading the Sequelize docs. This will help you learn faster.
-The easiest way to do this is using the SQLite dialect:
+Sequelize provides built-in TypeScript support.
-```js
-const { Sequelize, Op, Model, DataTypes } = require('@sequelize/core');
-const sequelize = new Sequelize("sqlite::memory:");
+Head to the [Version Policy page](/releases) to know which versions of TypeScript are supported,
+and make sure that the [`@types/node`](https://www.npmjs.com/package/@types/node) package corresponding to your Node.js version is installed
+in your project.
-// Code here! It works!
-```
+## CommonJS or ESM?
-To experiment with the other dialects, which are harder to setup locally,
-you can use the [Sequelize SSCCE](https://github.com/sequelize/sequelize-sscce) GitHub repository,
-which allows you to run code on all supported dialects!
+Our documentation makes heavy use of ECMAScript Modules (ESM), but CommonJS is fully supported by Sequelize.
+To use Sequelize in a CommonJS project, simply use `require` instead of `import`:
-## New databases versus existing databases
+```js
+// how you would import Sequelize in ESM
+import { Sequelize, Op, Model, DataTypes } from '@sequelize/core';
-If you are starting a project from scratch, and your database is still empty, Sequelize can be used since the beginning in order to automate the creation of every table in your database.
+// how you would import Sequelize in CommonJS
+const { Sequelize, Op, Model, DataTypes } = require('@sequelize/core');
+```
-Also, if you want to use Sequelize to connect to a database that is already filled with tables and data, that works as well! Sequelize has got you covered in both cases.
+Most of the methods provided by Sequelize are asynchronous and therefore return [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
+we highly recommend using **ECMAScript Modules**, as they give you access to [Top-Level Await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await), which we use extensively in our examples.
+Head to [Node.js' documentation](https://nodejs.org/api/esm.html) to learn how to use them in Node.js.
## Logging
-By default, Sequelize will log to console every SQL query it performs. The `options.logging` option can be used to customize this behavior, by defining the function that gets executed every time Sequelize would log something. The default value is `console.log` and when using that only the first log parameter of log function call is displayed. For example, for query logging the first parameter is the raw query and the second (hidden by default) is the Sequelize object.
+To ease debugging, you can enable logging in Sequelize. This is done by setting the `logging` option to a function that gets executed every time Sequelize needs to log something.
-Common useful values for `options.logging`:
+Examples:
```js
-const sequelize = new Sequelize('sqlite::memory:', {
- // Choose one of the logging options
- logging: console.log, // Default, displays the first parameter of the log function call
- logging: (...msg) => console.log(msg), // Displays all log function call parameters
- logging: false, // Disables logging
- logging: msg => logger.debug(msg), // Use custom logger (e.g. Winston or Bunyan), displays the first parameter
- logging: logger.debug.bind(logger) // Alternative way to use custom logger, displays all messages
+const sequelize = new Sequelize({
+ dialect: SqliteDialect,
+
+ // Disables logging (default)
+ logging: false,
+
+ // Sends the logging output to the console
+ logging: console.log,
+
+ // You can also use an arbitrary function, which can be used to send logs to a logging tool
+ logging: (...msg) => console.log(msg),
});
```
-## Promises and async/await
+:::tip
+
+If you need to log only specific queries, you can use the `logging` option in all model methods that execute queries,
+as well as all `queryInterface` methods.
+
+:::
-Most of the methods provided by Sequelize are asynchronous and therefore return Promises. They are all [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) , so you can use the Promise API (for example, using `then`, `catch`, `finally`) out of the box.
+## Next Step
-Of course, using `async` and `await` works normally as well.
+Now that you have a Sequelize instance, the next step to get started is to [define your first models](./models/defining-models.mdx).
diff --git a/docs/index.mdx b/docs/index.mdx
index 1e7774d3..0534a808 100644
--- a/docs/index.mdx
+++ b/docs/index.mdx
@@ -6,81 +6,92 @@ sidebar_label: Introduction
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
+import DecoratorInfo from './_fragments/_decorator-info.mdx';
-[](https://www.npmjs.com/package/sequelize)
-[](https://github.com/sequelize/sequelize/actions?query=workflow%3ACI)
-[](https://www.npmjs.com/package/sequelize)
+[](https://www.npmjs.com/package/@sequelize/core)
+[](https://www.npmjs.com/package/@sequelize/core)
[](https://opencollective.com/sequelize)
[](https://github.com/sequelize/sequelize)
[](https://github.com/sequelize/sequelize)
[](https://github.com/sequelize/sequelize)
-[](https://sequelize.org/slack)
-[](https://www.npmjs.com/package/sequelize)
+[](https://www.npmjs.com/package/@sequelize/core)
[](https://github.com/sequelize/sequelize/blob/main/LICENSE)
-[](https://github.com/semantic-release/semantic-release)
Sequelize is a promise-based [Node.js](https://nodejs.org/en/about/) [ORM tool](https://en.wikipedia.org/wiki/Object-relational_mapping) for [Postgres](https://en.wikipedia.org/wiki/PostgreSQL), [MySQL](https://en.wikipedia.org/wiki/MySQL), [MariaDB](https://en.wikipedia.org/wiki/MariaDB), [SQLite](https://en.wikipedia.org/wiki/SQLite), [Microsoft SQL Server](https://en.wikipedia.org/wiki/Microsoft_SQL_Server), [Amazon Redshift](https://docs.aws.amazon.com/redshift/index.html) and [Snowflake’s Data Cloud](https://docs.snowflake.com/en/user-guide/intro-key-concepts.html). It features solid transaction support, relations, eager and lazy loading, read replication and more.
-Sequelize follows [Semantic Versioning](https://semver.org) and the [official Node.js LTS schedule](https://nodejs.org/en/about/releases/). Version 7 of Sequelize officially supports the Node.js versions `^14.17,0`, `^16.0.0`. Other version might be working as well.
+Sequelize follows [Semantic Versioning](https://semver.org) and the [official Node.js LTS schedule](https://nodejs.org/en/about/releases/). Version 7 of Sequelize officially supports the Node.js versions `>=18.0.0`.
You are currently looking at the **Tutorials and Guides** for Sequelize. You might also be interested in the [API Reference](pathname:///api/v7).
## Quick example
-
+
-```javascript
-import { Sequelize, Model, DataTypes, InferAttributes, InferCreationAttributes } from 'sequelize';
-
-const sequelize = new Sequelize('sqlite::memory:');
+```typescript
+import { Sequelize, Model, DataTypes, InferAttributes, InferCreationAttributes } from '@sequelize/core';
+import { Attribute } from '@sequelize/core/decorators-legacy';
+import { SqliteDialect } from '@sequelize/sqlite3';
class User extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.STRING)
declare username: string | null;
+
+ @Attribute(DataTypes.DATE)
declare birthday: Date | null;
}
-User.init({
- username: DataTypes.STRING,
- birthday: DataTypes.DATE
-}, { sequelize, modelName: 'user' });
-
-(async () => {
- await sequelize.sync();
- const jane = await User.create({
- username: 'janedoe',
- birthday: new Date(1980, 6, 20),
- });
- console.log(jane.toJSON());
-})();
+const sequelize = new Sequelize({
+ dialect: SqliteDialect,
+ models: [User],
+});
+
+await sequelize.sync();
+
+const jane = await User.create({
+ username: 'janedoe',
+ birthday: new Date(1980, 6, 20),
+});
+
+console.log(jane.toJSON());
```
-
-
-```javascript
-const { Sequelize, Model, DataTypes } = require('sequelize');
-const sequelize = new Sequelize('sqlite::memory:');
-
-class User extends Model {}
-User.init({
- username: DataTypes.STRING,
- birthday: DataTypes.DATE
-}, { sequelize, modelName: 'user' });
-
-(async () => {
- await sequelize.sync();
- const jane = await User.create({
- username: 'janedoe',
- birthday: new Date(1980, 6, 20),
- });
- console.log(jane.toJSON());
-})();
+
+
+```typescript
+import { Sequelize, Model, DataTypes } from '@sequelize/core';
+import { Attribute } from '@sequelize/core/decorators-legacy';
+import { SqliteDialect } from '@sequelize/sqlite3';
+
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ username;
+
+ @Attribute(DataTypes.DATE)
+ birthday;
+}
+
+const sequelize = new Sequelize({
+ dialect: SqliteDialect,
+ models: [User],
+});
+
+await sequelize.sync();
+
+const jane = await User.create({
+ username: 'janedoe',
+ birthday: new Date(1980, 6, 20),
+});
+
+console.log(jane.toJSON());
```
+
+
To learn more about how to use Sequelize, read the tutorials available in the left menu. Begin with [Getting Started](./getting-started.mdx).
## Supporting the project
diff --git a/docs/advanced-association-concepts/_category_.json b/docs/models/_category_.json
similarity index 71%
rename from docs/advanced-association-concepts/_category_.json
rename to docs/models/_category_.json
index 3c655fb7..5121b4f2 100644
--- a/docs/advanced-association-concepts/_category_.json
+++ b/docs/models/_category_.json
@@ -1,6 +1,6 @@
{
"position": 4,
- "label": "Advanced Association Concepts",
+ "label": "Models & Tables",
"collapsible": true,
"collapsed": false,
"link": {
diff --git a/docs/models/advanced.mdx b/docs/models/advanced.mdx
new file mode 100644
index 00000000..2ecf68af
--- /dev/null
+++ b/docs/models/advanced.mdx
@@ -0,0 +1,112 @@
+---
+sidebar_position: 9
+---
+
+# Advanced model definitions
+
+## Prevent creating a default PK attribute
+
+By default, Sequelize automatically adds the primary key attribute `id` to every model when no primary key has been defined manually.
+To prevent this you can set the [`noPrimaryKey`](pathname:///api/v7/interfaces/_sequelize_core.index.ModelOptions.html#noPrimaryKey) option to true when defining the model.
+
+```typescript
+@Table({ noPrimaryKey: true })
+export class User extends Model {}
+```
+
+If you want to prevent the addition of the default primary key for every model, you can also configure this option globally:
+
+```js
+class User extends Model {}
+
+const sequelize = new Sequelize({
+ dialect: SqliteDialect,
+ define: {
+ noPrimaryKey: true,
+ },
+ models: [User],
+});
+```
+
+Instances without primary keys can still be retrieved using `Model.findOne` and `Model.findAll`.
+
+:::caution
+
+Some model methods require a primary key to be defined. For instance, `Model.findByPk` will throw an error if no primary key is defined.
+
+If your model has no primary keys, you need to use the static equivalent of the following instance methods, and provide your own `where` parameter:
+
+- `instance.save`: `Model.update`
+- `instance.update`: `Model.update`
+- `instance.reload`: `Model.findOne`
+- `instance.destroy`: `Model.destroy`
+- `instance.restore`: `Model.restore`
+- `instance.decrement`: `Model.decrement`
+- `instance.increment`: `Model.increment`
+
+:::
+
+## Caveat with minification
+
+When defining a model, the name of the model will, by default, be the name of your class.
+
+If you minify your code, the class name — and therefore its model name — may be changed by your minifier.
+This can be an issue, as many systems use your modelName, from [sequelize.models](pathname:///api/v7/classes/_sequelize_core.index.Sequelize.html#models)
+to [the name of the table associated to the model](./naming-strategies.mdx).
+
+The solution to prevent this issue is to explicitly set the `modelName` option:
+
+```typescript
+// by specifying 'modelName' explicitely, the name of this model can be safely minified.
+@Table({ modelName: 'User' })
+export class User extends Model {}
+```
+
+## Engines
+
+:::info
+
+This feature is only available in MySQL & MariaDB
+
+:::
+
+The default engine for a model is InnoDB.
+
+You can change the engine for a model with the `engine` option (e.g., to MyISAM):
+
+```js
+const Person = sequelize.define('person', { /* attributes */ }, {
+ engine: 'MYISAM',
+});
+```
+
+Like every option for the definition of a model, this setting can also be changed globally with the `define` option of the Sequelize constructor:
+
+```js
+const sequelize = new Sequelize({
+ /* options */
+ define: { engine: 'MYISAM' },
+});
+```
+
+## Table comments
+
+:::info
+
+This feature is only available in MySQL, MariaDB and PostgreSQL
+
+:::
+
+
+You can use the `comment` model option to set a comment on the table, which will be added to the table definition in SQL.
+
+```js
+import { Model } from '@sequelize/core';
+import { Table } from '@sequelize/core/decorators-legacy';
+
+@Table({
+ // highlight-next-line
+ comment: 'This is the User model'
+})
+export class User extends Model {}
+```
diff --git a/docs/models/auto-timestamps.mdx b/docs/models/auto-timestamps.mdx
new file mode 100644
index 00000000..2fbc220a
--- /dev/null
+++ b/docs/models/auto-timestamps.mdx
@@ -0,0 +1,211 @@
+---
+sidebar_position: 5
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+# Auto-generated Timestamps
+
+By default, Sequelize automatically adds the attributes `createdAt` and `updatedAt` to every model, using the data type `DataTypes.DATE`.
+Those attributes are automatically managed as well - whenever you use Sequelize to create or update something, those attributes will be set correctly.
+
+The `createdAt` attribute will contain the timestamp representing the moment of creation, and the `updatedAt` will contain the timestamp of the latest update.
+
+**Note:** The value of these attributes are updated by Sequelize in JavaScript (i.e. not done with *SQL triggers*).
+This means that direct SQL queries (for example queries performed without Sequelize by any other means) will not cause these attributes to be updated automatically.
+
+:::info
+
+A third Auto-generated Timestamp called `deletedAt` exists. It is used by [Paranoid Models](./paranoid.mdx) to mark a row as deleted.
+
+:::
+
+## TypeScript
+
+If you're using TypeScript, you'll want to type these attributes. You can do so by simply adding them to your model as follows:
+
+```typescript
+import { InferCreationAttributes, InferAttributes, Model, CreationOptional } from '@sequelize/core';
+
+class User extends Model, InferCreationAttributes> {
+ declare createdAt: CreationOptional;
+ declare updatedAt: CreationOptional;
+}
+```
+
+You don't need to decorate them, as they're automatically configured by Sequelize.
+
+## Disabling Auto-Timestamp Attributes
+
+All auto-generated timestamp attributes can be removed from your model by setting the [`timestamps`](pathname:///api/v7/interfaces/_sequelize_core.index.ModelOptions.html#timestamps) option to `false`:
+
+```ts
+// highlight-next-line
+@Table({ timestamps: false })
+class User extends Model {}
+```
+
+The `timestamps` option can also be disabled globally, on the `Sequelize` instance:
+
+```js
+class User extends Model {}
+
+const sequelize = new Sequelize({
+ dialect: SqliteDialect,
+ define: {
+ // highlight-next-line
+ timestamps: false,
+ },
+ models: [User],
+});
+```
+
+It is also possible to disable only some auto-generated timestamp attributes
+by setting the [`createdAt`](pathname:///api/v7/interfaces/_sequelize_core.index.ModelOptions.html#createdAt) or [`updatedAt`](pathname:///api/v7/interfaces/_sequelize_core.index.ModelOptions.html#updatedAt) option to false:
+
+```js
+@Table({
+ // don't generate a "createdAt" attribute
+ // highlight-next-line
+ createdAt: false,
+ // don't generate an "updatedAt" attribute
+ // highlight-next-line
+ updatedAt: false,
+})
+class User extends Model {}
+```
+
+## Renaming Auto-Timestamp Attributes
+
+You may also be interested in renaming the attributes. There are two parts to this:
+
+- Renaming the JavaScript name (the *attribute name*)
+- Renaming the Database name (the *column name*)
+
+### Changing the Attribute Name
+
+Changing the [*attribute name*](../terminology.md#attributes--columns)
+is done by using the `@CreatedAt`, `@UpdatedAt`, or [`@DeletedAt`](./paranoid.mdx) decorators on the attribute of your choice.
+
+
+
+
+```typescript
+import { InferCreationAttributes, InferAttributes, Model, CreationOptional } from '@sequelize/core';
+import { CreatedAt, UpdatedAt, DeletedAt } from '@sequelize/core/decorators-legacy';
+
+class User extends Model, InferCreationAttributes> {
+ // highlight-next-line
+ @CreatedAt
+ declare creationDate: CreationOptional;
+
+ // highlight-next-line
+ @UpdatedAt
+ declare lastUpdateDate: CreationOptional;
+
+ // highlight-next-line
+ @DeletedAt
+ declare deletionDate: Date | null;
+}
+```
+
+
+
+
+```javascript
+import { Model } from '@sequelize/core';
+import { CreatedAt, UpdatedAt, DeletedAt } from '@sequelize/core/decorators-legacy';
+
+class User extends Model {
+ // highlight-next-line
+ @CreatedAt
+ creationDate;
+
+ // highlight-next-line
+ @UpdatedAt
+ lastUpdateDate;
+
+ // highlight-next-line
+ @DeletedAt
+ deletionDate;
+}
+```
+
+
+
+
+:::info Paranoid Mode
+
+Using `@DeletedAt` on your model will automatically enable [Paranoid Mode](./paranoid.mdx) for that model.
+
+:::
+
+### Changing the Column Name
+
+Changing the Column Name is done by using the `@ColumnName` decorator on the timestamp attribute.
+If `@ColumnName` is not used, the Column Name will be automatically inferred from the Attribute Name.
+
+Read more on this in [the Naming Strategies API](./naming-strategies.mdx) documentation.
+
+:::note
+
+Notice how in the example below, we do not configure the data type or the nullability of these attributes. These properties will
+be configured automatically by Sequelize.
+
+:::
+
+
+
+
+```typescript
+import { Model, InferCreationAttributes, InferAttributes, CreationOptional } from '@sequelize/core';
+import { ColumnName } from '@sequelize/core/decorators-legacy';
+
+class User extends Model, InferCreationAttributes> {
+ // sets the Database name to 'created_at'
+ // highlight-next-line
+ @ColumnName('created_at')
+ declare createdAt: CreationOptional;
+
+ // sets the Database name to 'updated_at'
+ // highlight-next-line
+ @ColumnName('updated_at')
+ declare updatedAt: CreationOptional;
+
+ // for Paranoid Models
+ // sets the Database name to 'deleted_at'
+ // highlight-next-line
+ @ColumnName('deleted_at')
+ declare deletedAt: Date | null;
+}
+```
+
+
+
+
+```javascript
+import { Model } from '@sequelize/core';
+import { ColumnName } from '@sequelize/core/decorators-legacy';
+
+class User extends Model {
+ // sets the Database name to 'created_at'
+ // highlight-next-line
+ @ColumnName('created_at')
+ createdAt;
+
+ // sets the Database name to 'updated_at'
+ // highlight-next-line
+ @ColumnName('updated_at')
+ updatedAt;
+
+ // for Paranoid Models
+ // sets the Database name to 'deleted_at'
+ // highlight-next-line
+ @ColumnName('deleted_at')
+ deletedAt;
+}
+```
+
+
+
diff --git a/docs/models/concrete-table-inheritance.svg b/docs/models/concrete-table-inheritance.svg
new file mode 100644
index 00000000..7a053113
--- /dev/null
+++ b/docs/models/concrete-table-inheritance.svg
@@ -0,0 +1,200 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Player
+ name
+
+
+
+
+
+
+
+
+
+ Footballer
+ club
+
+
+
+
+
+
+
+
+
+ Cricketer
+ battingAverage
+
+
+
+
+
+
+
+
+
+ Bowler
+ bowlingAverage
+
+
+
+
+
+
+
+
+
+ «table»
+ footballers
+ name
+ club
+
+
+
+
+
+
+
+
+
+ «table»
+ cricketers
+ name
+ batting_average
+
+
+
+
+
+
+
+
+
+ «table»
+ bowlers
+ name
+ batting_average
+ bowling_average
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/other-topics/other-data-types.mdx b/docs/models/data-types.mdx
similarity index 81%
rename from docs/other-topics/other-data-types.mdx
rename to docs/models/data-types.mdx
index a634d265..1e93e901 100644
--- a/docs/other-topics/other-data-types.mdx
+++ b/docs/models/data-types.mdx
@@ -1,12 +1,14 @@
---
-sidebar_position: 2
+sidebar_position: 3
title: Data Types
---
import { DialectTableFilter } from '@site/src/components/dialect-table-filter.tsx';
import { Deprecated } from '@site/src/components/deprecated.tsx';
+import JsDefaultCaution from '../_fragments/_js-default-caution.mdx';
+import UuidSupportTable from '../_fragments/_uuid-support-table.mdx';
-Sequelize provides [a lot of built-in data types](pathname:///api/v7/modules/datatypes). To access a built-in data type, you must import `DataTypes`:
+Sequelize provides [a lot of built-in data types](pathname:///api/v7/modules/_sequelize_core.index.DataTypes.html). To access a built-in data type, you must import `DataTypes`:
```js
// Import the built-in data types
@@ -29,15 +31,15 @@ A ❌ means the dialect does not support that DataType.
| Sequelize DataType | PostgreSQL | MariaDB | MySQL | MSSQL | [SQLite][sqlite-datatypes] | [Snowflake](https://docs.snowflake.com/en/sql-reference/data-types-text.html) | db2 | ibmi |
|------------------------------------------------------------------------------------|-----------------------------------------------------------------|------------------------------------|------------------------------|--------------------------------|----------------------------|-------------------------------------------------------------------------------|-----------------------------|-----------------------------|
-| [`STRING`](pathname:///api/v7/interfaces/DataTypes.StringDataTypeConstructor.html) | [`VARCHAR(255)`][postgres-char] | [`VARCHAR(255)`][mariadb-varchar] | [`VARCHAR(255)`][mysql-char] | [`NVARCHAR(255)`][mssql-nchar] | `TEXT` | `VARCHAR(255)` | `VARCHAR(255)` | `VARCHAR(255)` |
+| [`STRING`](pathname:///api/v7/classes/_sequelize_core.index.unknown.STRING.html) | [`VARCHAR(255)`][postgres-char] | [`VARCHAR(255)`][mariadb-varchar] | [`VARCHAR(255)`][mysql-char] | [`NVARCHAR(255)`][mssql-nchar] | `TEXT` | `VARCHAR(255)` | `VARCHAR(255)` | `VARCHAR(255)` |
| `STRING(100)` | `VARCHAR(100)` | `VARCHAR(100)` | `VARCHAR(100)` | `NVARCHAR(100)` | `TEXT` | `VARCHAR(100)` | `VARCHAR(100)` | `VARCHAR(100)` |
| `STRING.BINARY` | ❌ | `VARCHAR(255) BINARY` | `VARCHAR(255) BINARY` | ❌ | `TEXT COLLATE BINARY` | `VARCHAR(255) BINARY` | `VARCHAR(255) FOR BIT DATA` | `VARCHAR(255) FOR BIT DATA` |
| `STRING(100).BINARY` | ❌ | `VARCHAR(100) BINARY` | `VARCHAR(100) BINARY` | ❌ | `TEXT COLLATE BINARY` | `VARCHAR(100) BINARY` | `VARCHAR(100) FOR BIT DATA` | `VARCHAR(100) FOR BIT DATA` |
-| [`TEXT`](pathname:///api/v7/interfaces/DataTypes.TextDataTypeConstructor.html) | [`TEXT`][postgres-char] | [`TEXT`][mariadb-text] | [`TEXT`][mysql-text] | `NVARCHAR(MAX)` | `TEXT` | `TEXT` | `CLOB(2147483647)` | `CLOB(2147483647)` |
+| [`TEXT`](pathname:///api/v7/classes/_sequelize_core.index.unknown.TEXT.html) | [`TEXT`][postgres-char] | [`TEXT`][mariadb-text] | [`TEXT`][mysql-text] | `NVARCHAR(MAX)` | `TEXT` | `TEXT` | `CLOB(2147483647)` | `CLOB(2147483647)` |
| `TEXT('tiny')` | `TEXT` | [`TINYTEXT`][mariadb-tinytext] | [`TINYTEXT`][mysql-text] | `NVARCHAR(256)` | `TEXT` | `TEXT` | `VARCHAR(256)` | `VARCHAR(256)` |
| `TEXT('medium')` | `TEXT` | [`MEDIUMTEXT`][mariadb-mediumtext] | [`MEDIUMTEXT`][mysql-text] | `NVARCHAR(MAX)` | `TEXT` | `TEXT` | `VARCHAR(16777216)` | `VARCHAR(16777216)` |
| `TEXT('long')` | `TEXT` | [`LONGTEXT`][mariadb-longtext] | [`LONGTEXT`][mysql-text] | `NVARCHAR(MAX)` | `TEXT` | `TEXT` | `CLOB(2147483647)` | `CLOB(2147483647)` |
-| [`CHAR`](pathname:///api/v7/interfaces/DataTypes.CharDataTypeConstructor.html) | [`CHAR(255)`][postgres-char] | [`CHAR(255)`][mariadb-char] | [`CHAR(255)`][mysql-char] | [`CHAR(255)`][mssql-char] | ❌ | `CHAR(255)` | `CHAR(255)` | `CHAR(255)` |
+| [`CHAR`](pathname:///api/v7/classes/_sequelize_core.index.unknown.CHAR.html) | [`CHAR(255)`][postgres-char] | [`CHAR(255)`][mariadb-char] | [`CHAR(255)`][mysql-char] | [`CHAR(255)`][mssql-char] | ❌ | `CHAR(255)` | `CHAR(255)` | `CHAR(255)` |
| `CHAR(100)` | `CHAR(100)` | `CHAR(100)` | `CHAR(100)` | `CHAR(100)` | ❌ | `CHAR(100)` | `CHAR(100)` | `CHAR(100)` |
| `CHAR.BINARY` | ❌ | `CHAR(255) BINARY` | `CHAR(255) BINARY` | ❌ | ❌ | `CHAR(255) BINARY` | `CHAR(255) FOR BIT DATA` | `CHAR(255) FOR BIT DATA` |
| `CHAR(100).BINARY` | ❌ | `CHAR(100) BINARY` | `CHAR(100) BINARY` | ❌ | ❌ | `CHAR(100) BINARY` | `CHAR(255) FOR BIT DATA` | `CHAR(255) FOR BIT DATA` |
@@ -62,25 +64,25 @@ A ❌ means the dialect does not support that DataType.
| Sequelize DataType | [PostgreSQL][postgres-numeric] | [MariaDB][mariadb-numeric] | [MySQL][mysql-numeric] | [MSSQL][mssql-ints] | [SQLite][sqlite-datatypes] | [Snowflake][snowflake-numeric] | db2 | ibmi |
|----------------------------------------------------------------------------------------------|--------------------------------|-----------------------------------------------------|------------------------|----------------------|----------------------------|--------------------------------|---------------------|---------------------|
-| [`TINYINT`](pathname:///api/v7/interfaces/DataTypes.TinyIntegerDataTypeConstructor.html) | `SMALLINT`[^ints-1] | [`TINYINT`][mariadb-tinyint] | `TINYINT` | `SMALLINT`[^mssql-1] | `INTEGER` | `INTEGER` | `SMALLINT`[^ints-1] | `SMALLINT`[^ints-1] |
+| [`TINYINT`](pathname:///api/v7/classes/_sequelize_core.index.unknown.TINYINT.html) | `SMALLINT`[^ints-1] | [`TINYINT`][mariadb-tinyint] | `TINYINT` | `SMALLINT`[^mssql-1] | `INTEGER` | `INTEGER` | `SMALLINT`[^ints-1] | `SMALLINT`[^ints-1] |
| `TINYINT(1)` | ❌ | `TINYINT(1)` | `TINYINT(1)` | ❌ | ❌ | `❌ | ❌ | ❌ |
| `TINYINT.UNSIGNED` | `SMALLINT` | `TINYINT UNSIGNED` | `TINYINT UNSIGNED` | `TINYINT`[^mssql-1] | `INTEGER` | `INTEGER` | `SMALLINT` | `SMALLINT` |
| `TINYINT.ZEROFILL` | ❌ | `TINYINT ZEROFILL` | `TINYINT ZEROFILL` | ❌ | ❌ | ❌ | ❌ | ❌ |
-| [`SMALLINT`](pathname:///api/v7/interfaces/DataTypes.SmallIntegerDataTypeConstructor.html) | `SMALLINT` | [`SMALLINT`](https://mariadb.com/kb/en/smallint/) | `SMALLINT` | `SMALLINT` | `INTEGER` | `INTEGER` | `SMALLINT` | `SMALLINT` |
+| [`SMALLINT`](pathname:///api/v7/classes/_sequelize_core.index.unknown.SMALLINT.html) | `SMALLINT` | [`SMALLINT`](https://mariadb.com/kb/en/smallint/) | `SMALLINT` | `SMALLINT` | `INTEGER` | `INTEGER` | `SMALLINT` | `SMALLINT` |
| `SMALLINT(1)` | ❌ | `SMALLINT(1)` | `SMALLINT(1)` | ❌ | ❌ | ❌ | ❌ | ❌ |
| `SMALLINT.UNSIGNED` | `INTEGER`[^ints-2] | `SMALLINT UNSIGNED` | `SMALLINT UNSIGNED` | `INTEGER`[^ints-2] | `INTEGER` | `INTEGER` | `INTEGER`[^ints-2] | `INTEGER`[^ints-2] |
| `SMALLINT.ZEROFILL` | ❌ | `SMALLINT ZEROFILL` | `SMALLINT ZEROFILL` | ❌ | ❌ | ❌ | ❌ | ❌ |
-| [`MEDIUMINT`](pathname:///api/v7/interfaces/DataTypes.MediumIntegerDataTypeConstructor.html) | `INTEGER` | [`MEDIUMINT`](https://mariadb.com/kb/en/mediumint/) | `MEDIUMINT` | `INTEGER`[^ints-1] | `INTEGER` | `INTEGER` | `INTEGER` | `INTEGER` |
+| [`MEDIUMINT`](pathname:///api/v7/classes/_sequelize_core.index.unknown.MEDIUMINT.html) | `INTEGER` | [`MEDIUMINT`](https://mariadb.com/kb/en/mediumint/) | `MEDIUMINT` | `INTEGER`[^ints-1] | `INTEGER` | `INTEGER` | `INTEGER` | `INTEGER` |
| `MEDIUMINT(1)` | ❌ | `MEDIUMINT(1)` | `MEDIUMINT(1)` | ❌ | ❌ | ❌ | ❌ | ❌ |
| `MEDIUMINT.UNSIGNED` | `INTEGER` | `MEDIUMINT UNSIGNED` | `MEDIUMINT UNSIGNED` | `INTEGER` | `INTEGER` | `INTEGER` | `INTEGER` | `INTEGER` |
| `MEDIUMINT.ZEROFILL` | ❌ | `MEDIUMINT ZEROFILL` | `MEDIUMINT ZEROFILL` | ❌ | ❌ | ❌ | ❌ | ❌ |
-| [`INTEGER`](pathname:///api/v7/interfaces/IntegerDataTypeConstructor.html) | `INTEGER` | [`INTEGER`](https://mariadb.com/kb/en/integer/) | `INTEGER` | `INTEGER` | `INTEGER` | `INTEGER` | `INTEGER` | `INTEGER` |
+| [`INTEGER`](pathname:///api/v7/classes/_sequelize_core.index.unknown.INTEGER.html) | `INTEGER` | [`INTEGER`](https://mariadb.com/kb/en/integer/) | `INTEGER` | `INTEGER` | `INTEGER` | `INTEGER` | `INTEGER` | `INTEGER` |
| `INTEGER(1)` | ❌ | `INTEGER(1)` | `INTEGER(1)` | ❌ | ❌ | ❌ | ❌ | ❌ |
| `INTEGER.UNSIGNED` | `BIGINT` | `INTEGER UNSIGNED` | `INTEGER UNSIGNED` | `BIGINT` | `INTEGER` | `INTEGER` | `BIGINT` | `BIGINT` |
| `INTEGER.ZEROFILL` | ❌ | `INTEGER ZEROFILL` | `INTEGER ZEROFILL` | ❌ | ❌ | ❌ | ❌ | ❌ |
-| [`BIGINT`](pathname:///api/v7/interfaces/DataTypes.BigIntDataTypeConstructor.html) | `BIGINT` | [`BIGINT`](https://mariadb.com/kb/en/bigint/) | `BIGINT` | `BIGINT` | `INTEGER` | `INTEGER` | `BIGINT` | `BIGINT` |
+| [`BIGINT`](pathname:///api/v7/classes/_sequelize_core.index.unknown.BIGINT.html) | `BIGINT` | [`BIGINT`](https://mariadb.com/kb/en/bigint/) | `BIGINT` | `BIGINT` | ❌ | `INTEGER` | `BIGINT` | `BIGINT` |
| `BIGINT(1)` | ❌ | `BIGINT(1)` | `BIGINT(1)` | ❌ | ❌ | ❌ | ❌ | ❌ |
-| `BIGINT.UNSIGNED` | ❌ | `BIGINT UNSIGNED` | `BIGINT UNSIGNED` | ❌ | `INTEGER` | `INTEGER` | ❌ | ❌ |
+| `BIGINT.UNSIGNED` | ❌ | `BIGINT UNSIGNED` | `BIGINT UNSIGNED` | ❌ | ❌ | `INTEGER` | ❌ | ❌ |
| `BIGINT.ZEROFILL` | ❌ | `BIGINT ZEROFILL` | `BIGINT ZEROFILL` | ❌ | ❌ | ❌ | ❌ | ❌ |
@@ -89,9 +91,9 @@ A ❌ means the dialect does not support that DataType.
[^ints-2]: When an unsigned int type is not available, Sequelize uses a bigger int type to ensure the type covers all possible unsigned integer values of the smaller int type.
[^mssql-1]: `TINYINT` in SQL Server is unsigned. `DataTypes.TINYINT.UNSIGNED` therefore maps to `TINYINT`, and `DataTypes.TINYINT` maps to `SMALLINT`.
-:::caution
+:::caution
-The JavaScript [`number`][mdn-number] type can represent ints ranging from [`-9007199254740991`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_SAFE_INTEGER)
+The JavaScript [`number`][mdn-number] type can represent ints ranging from [`-9007199254740991`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_SAFE_INTEGER)
to [`9007199254740991`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER).
If your SQL type supports integer values outside this range, we recommend using [`bigint`][mdn-bigint] or [`string`][mdn-string] to represent your integers.
@@ -116,11 +118,11 @@ The types in the following table are typically represented as an [IEEE 754 float
| Sequelize DataType | [PostgreSQL][postgres-numeric] | [MariaDB][mariadb-numeric] | [MySQL][mysql-numeric] | [MSSQL][mssql-inexact-decimals] | [SQLite][sqlite-datatypes] | [Snowflake][snowflake-numeric] | db2 | ibmi |
|------------------------------------------------------------------------------------|--------------------------------|---------------------------------------------------------|-----------------------------|---------------------------------|----------------------------|--------------------------------|----------|----------|
-| [`FLOAT`](pathname:///api/v7/interfaces/DataTypes.FloatDataTypeConstructor.html) | `REAL` | [`FLOAT`](https://mariadb.com/kb/en/float/) | `FLOAT` | `REAL` | `REAL`[^sqlite-3] | `FLOAT`[^snowflake-1] | `REAL` | `REAL` |
+| [`FLOAT`](pathname:///api/v7/classes/_sequelize_core.index.unknown.FLOAT.html) | `REAL` | [`FLOAT`](https://mariadb.com/kb/en/float/) | `FLOAT` | `REAL` | `REAL`[^sqlite-3] | `FLOAT`[^snowflake-1] | `REAL` | `REAL` |
| `FLOAT(11, 10)` | ❌ | `FLOAT(11,10)` | `FLOAT(11,10)` | ❌ | ❌ | ❌ | ❌ | ❌ |
| `FLOAT.UNSIGNED` | `REAL` | `FLOAT UNSIGNED` | `FLOAT UNSIGNED` | `REAL` | `REAL` | `FLOAT` | `REAL` | `REAL` |
| `FLOAT.ZEROFILL` | ❌ | `FLOAT ZEROFILL` | `FLOAT ZEROFILL` | ❌ | ❌ | ❌ | ❌ | ❌ |
-| [`DOUBLE`](pathname:///api/v7/interfaces/DataTypes.DoubleDataTypeConstructor.html) | `DOUBLE PRECISION` | [`DOUBLE PRECISION`](https://mariadb.com/kb/en/double/) | `DOUBLE PRECISION` | `DOUBLE PRECISION` | `REAL` | `FLOAT` | `DOUBLE` | `DOUBLE` |
+| [`DOUBLE`](pathname:///api/v7/classes/_sequelize_core.index.unknown.DOUBLE.html) | `DOUBLE PRECISION` | [`DOUBLE PRECISION`](https://mariadb.com/kb/en/double/) | `DOUBLE PRECISION` | `DOUBLE PRECISION` | `REAL` | `FLOAT` | `DOUBLE` | `DOUBLE` |
| `DOUBLE(11, 10)` | ❌ | `DOUBLE PRECISION(11, 10)` | `DOUBLE PRECISION(11, 10)` | ❌ | ❌ | ❌ | ❌ | ❌ |
| `DOUBLE.UNSIGNED` | `DOUBLE PRECISION` | `DOUBLE PRECISION UNSIGNED` | `DOUBLE PRECISION UNSIGNED` | `DOUBLE PRECISION` | `REAL` | `FLOAT` | `DOUBLE` | `DOUBLE` |
| `DOUBLE.ZEROFILL` | ❌ | `DOUBLE PRECISION ZEROFILL` | `DOUBLE PRECISION ZEROFILL` | ❌ | ❌ | ❌ | ❌ | ❌ |
@@ -146,7 +148,7 @@ Numeric options can be combined:
| Sequelize DataType | [PostgreSQL][postgres-numeric] | [MariaDB][mariadb-numeric] | [MySQL][mysql-numeric] | [MSSQL][mssql-exact-decimals] | [SQLite][sqlite-datatypes] | [Snowflake][snowflake-numeric] | db2 | ibmi |
|--------------------------------------------------------------------------------------|--------------------------------|--------------------------------------------------------|--------------------------|-------------------------------|----------------------------|--------------------------------|------------------|------------------|
-| [`DECIMAL`](pathname:///api/v7/interfaces/DataTypes.DecimalDataTypeConstructor.html) | `DECIMAL` | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
+| [`DECIMAL`](pathname:///api/v7/classes/_sequelize_core.index.unknown.DECIMAL.html) | `DECIMAL` | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| `DECIMAL(11, 10)` | `DECIMAL(11, 10)` | [`DECIMAL(11,10)`](https://mariadb.com/kb/en/decimal/) | `DECIMAL(11,10)` | `DECIMAL(11,10)` | ❌ | `DECIMAL(11,10)` | `DECIMAL(11,10)` | `DECIMAL(11,10)` |
| `DECIMAL(p, s).UNSIGNED` | `DECIMAL(p, s)` | `DECIMAL(p, s) UNSIGNED` | `DECIMAL(p, s) UNSIGNED` | `DECIMAL(p, s)` | ❌ | `DECIMAL(p, s)` | `DECIMAL(p, s)` | `DECIMAL(p, s)` |
| `DECIMAL(p, s).ZEROFILL` | ❌ | `DECIMAL(p, s) ZEROFILL` | `DECIMAL(p, s) ZEROFILL` | ❌ | ❌ | ❌ | ❌ | ❌ |
@@ -156,7 +158,7 @@ Numeric options can be combined:
:::caution
Exact Decimal Numbers are not representable in JavaScript [yet](https://github.com/tc39/proposal-decimal).
-The JavaScript [`number`][mdn-number] type is a double-precision 64-bit binary format [IEEE 754][ieee-754] value, better represented by [Inexact Decimal types](#inexact-decimal-numbers).
+The JavaScript [`number`][mdn-number] type is a double-precision 64-bit binary format [IEEE 754][ieee-754] value, better represented by [Approximate Decimal types](#approximate-decimal-numbers).
To avoid any loss of precision, we recommend using [`string`][mdn-string] to represent Exact Decimal Numbers in JavaScript.
@@ -175,7 +177,7 @@ Numeric options can be combined:
| Sequelize DataType | [PostgreSQL][postgres-temporal] | [MariaDB][mariadb-temporal] | [MySQL][mysql-temporal] | MSSQL | SQLite | [Snowflake][snowflake-temporal] | db2 | ibmi |
|--------------------------------------------------------------------------------|---------------------------------|---------------------------------------------------|---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------|--------|---------------------------------|----------------|----------------|
-| [`DATE`](pathname:///api/v7/interfaces/DataTypes.DateDataTypeConstructor.html) | `TIMESTAMP WITH TIME ZONE` | [`DATETIME`](https://mariadb.com/kb/en/datetime/) | [`DATETIME`](https://dev.mysql.com/doc/refman/8.0/en/datetime.html) | [`DATETIMEOFFSET`](https://docs.microsoft.com/en-us/sql/t-sql/data-types/datetimeoffset-transact-sql) | `TEXT` | `TIMESTAMP` | `TIMESTAMP` | `TIMESTAMP` |
+| [`DATE`](pathname:///api/v7/classes/_sequelize_core.index.unknown.DATE.html) | `TIMESTAMP WITH TIME ZONE` | [`DATETIME`](https://mariadb.com/kb/en/datetime/) | [`DATETIME`](https://dev.mysql.com/doc/refman/8.0/en/datetime.html) | [`DATETIMEOFFSET`](https://docs.microsoft.com/en-us/sql/t-sql/data-types/datetimeoffset-transact-sql) | `TEXT` | `TIMESTAMP` | `TIMESTAMP` | `TIMESTAMP` |
| `DATE(6)` | `TIMESTAMP(6) WITH TIME ZONE` | `DATETIME(6)` | `DATETIME(6)` | `DATETIMEOFFSET(6)` | `TEXT` | `TIMESTAMP(6)` | `TIMESTAMP(6)` | `TIMESTAMP(6)` |
| `DATEONLY` | `DATE` | [`DATE`](https://mariadb.com/kb/en/date/) | [`DATE`](https://dev.mysql.com/doc/refman/8.0/en/datetime.html) | [`DATE`](https://docs.microsoft.com/en-us/sql/t-sql/data-types/date-transact-sql) | `TEXT` | `DATE` | `DATE` | `DATE` |
| `TIME` | `TIME` | [`TIME`](https://mariadb.com/kb/en/time/) | [`TIME`](https://dev.mysql.com/doc/refman/8.0/en/time.html) | [`TIME`](https://docs.microsoft.com/en-us/sql/t-sql/data-types/time-transact-sql) | `TEXT` | `TIME` | `TIME` | `TIME` |
@@ -185,7 +187,7 @@ Numeric options can be combined:
### Built-in Default Values for Dates
-Along with regular [default values](../core-concepts/model-basics.md#default-values), Sequelize provides `DataTypes.NOW`
+Along with regular [default values](./defining-models.mdx#default-values), Sequelize provides `DataTypes.NOW`
which will use the appropriate native SQL function based on your dialect.
@@ -205,10 +207,15 @@ MyModel.init({
});
```
+
+
## UUIDs
For UUIDs, use `DataTypes.UUID`. It becomes the `UUID` data type for PostgreSQL and SQLite, and `CHAR(36)` for MySQL.
+You can also use `DataTypes.UUID.V4` and `DataTypes.UUID.V1`
+to limit which version of UUID is accepted by the attribute to v4 or v1 respectively.
+
| Sequelize DataType | PostgreSQL | MariaDB | MySQL | MSSQL | SQLite | Snowflake | db2 | ibmi |
@@ -219,40 +226,37 @@ For UUIDs, use `DataTypes.UUID`. It becomes the `UUID` data type for PostgreSQL
### Built-in Default Values for UUID
-Sequelize can generate UUIDs automatically for these fields, simply use `DataTypes.UUIDV1` or `DataTypes.UUIDV4` as the default value:
+Sequelize can generate UUIDs automatically for these attributes, simply use [`sql.uuidV1` or `sql.uuidV4`](../querying/raw-queries.mdx#sqluuidv4--sqluuidv1) as the default value:
```javascript
MyModel.init({
myUuid: {
- type: DataTypes.UUID,
- defaultValue: DataTypes.UUIDV4, // Or DataTypes.UUIDV1
+ type: DataTypes.UUID.V4,
+ defaultValue: sql.uuidV4, // Or sql.uuidV1
},
});
```
-:::caution
+In supported dialects, Sequelize will set the default value to the appropriate function, as shown in the table below.
+These dialects can also use these two functions in migrations.
-The generation of values for `DataTypes.UUIDV1` and `DataTypes.UUIDV4` is done by the Sequelize layer, in JavaScript.
-For this reason, it is only used when interacting with Models. It cannot be used in [migrations](./migrations.md).
+In all other dialects, Sequelize will generate the UUID value itself, in JavaScript.
+This means that they cannot use these two functions in migrations.
-If your dialect provides a built-in SQL function to generate UUIDs, you can use `fn` to set a default value on the SQL layer.
-Making it usable with raw queries, and migrations.
+
-```javascript
-import { fn } from '@sequelize/core';
+The postgres dialect requires the `uuid-ossp` extension to be enabled to be able to generate v1 UUIDs.
+If this extension is not available, you can force Sequelize to generate the UUIDs itself by using `sql.uuidV1.asJavaScript` instead.
+```javascript
MyModel.init({
myUuid: {
- type: DataTypes.UUID,
- // 'uuid_generate_v4' is only available in postgres + uuid-ossp
- // other dialects may support this function under different names.
- defaultValue: fn('uuid_generate_v4'),
+ type: DataTypes.UUID.V1,
+ defaultValue: sql.uuidV1.asJavaScript,
},
});
```
-:::
-
## BLOBs
The blob datatype allows you to insert data both as strings and buffers. However, when a blob is retrieved from database with Sequelize, it will always be retrieved as a [Node Buffer](https://nodejs.org/api/buffer.html).
@@ -261,7 +265,7 @@ The blob datatype allows you to insert data both as strings and buffers. However
| Sequelize DataType | PostgreSQL | MariaDB | MySQL | MSSQL | [SQLite][sqlite-datatypes] | Snowflake | db2 | ibmi |
|--------------------------------------------------------------------------------|------------|--------------|--------------|------------------|----------------------------|--------------|-------------|-------------|
-| [`BLOB`](pathname:///api/v7/interfaces/DataTypes.BlobDataTypeConstructor.html) | `BYTEA` | `BLOB` | `BLOB` | `VARBINARY(MAX)` | `BLOB` | `BLOB` | `BLOB(1M)` | `BLOB(1M)` |
+| [`BLOB`](pathname:///api/v7/classes/_sequelize_core.index.unknown.BLOB.html) | `BYTEA` | `BLOB` | `BLOB` | `VARBINARY(MAX)` | `BLOB` | `BLOB` | `BLOB(1M)` | `BLOB(1M)` |
| `BLOB('tiny')` | `BYTEA` | `TINYBLOB` | `TINYBLOB` | `VARBINARY(256)` | `BLOB` | `TINYBLOB` | `BLOB(255)` | `BLOB(255)` |
| `BLOB('medium')` | `BYTEA` | `MEDIUMBLOB` | `MEDIUMBLOB` | `VARBINARY(MAX)` | `BLOB` | `MEDIUMBLOB` | `BLOB(16M)` | `BLOB(16M)` |
| `BLOB('long')` | `BYTEA` | `LONGBLOB` | `LONGBLOB` | `VARBINARY(MAX)` | `BLOB` | `LONGBLOB` | `BLOB(2G)` | `BLOB(2G)` |
@@ -282,7 +286,7 @@ The ENUM is a data type that accepts only a few values, specified as a list.
DataTypes.ENUM('foo', 'bar') // An ENUM with allowed values 'foo' and 'bar'
```
-See [the API Reference for DataTypes.ENUM](pathname:///api/v7/interfaces/DataTypes.EnumDataTypeConstructor.html) for more information about the options this DataType accepts.
+See [the API Reference for DataTypes.ENUM](pathname:///api/v7/classes/_sequelize_core.index.unknown.ENUM.html) for more information about the options this DataType accepts.
## JSON & JSONB
@@ -303,7 +307,16 @@ If you simply want to store and retrieve a JSON representation, using JSON will
:::info Querying JSON
-Sequelize provides a special syntax to query the contents of a JSON object. [Read more about querying JSON](../core-concepts/model-querying-basics.md#querying-json).
+Sequelize provides a special syntax to query the contents of a JSON object. [Read more about querying JSON](../querying/json.mdx).
+
+:::
+
+:::warning SQL `NULL` vs JSON `'null'`
+
+If your column is nullable, be aware that inserting `null` will insert the JSON `'null'` value by default instead of the
+SQL `NULL` value.
+
+[Read more about how `null` is handled in JSON columns](../querying/json.mdx#json-null-vs-sql-null)
:::
@@ -313,12 +326,12 @@ Sequelize provides a special syntax to query the contents of a JSON object. [Rea
| Sequelize DataType | PostgreSQL | [MariaDB](https://mariadb.com/kb/en/geometry-types/) | [MySQL](https://dev.mysql.com/doc/refman/8.0/en/spatial-type-overview.html) | MSSQL | SQLite | Snowflake | db2 | ibmi |
|------------------------------------------------------------------------------------------|---------------------------------------------------------------------------|------------------------------------------------------|-----------------------------------------------------------------------------|-------|--------|-----------|-----|------|
-| [`GEOMETRY`](pathname:///api/v7/interfaces/DataTypes.GeometryDataTypeConstructor.html) | [`GEOMETRY`](https://postgis.net/workshops/postgis-intro/geometries.html) | `GEOMETRY` | `GEOMETRY` | ❌ | ❌ | ❌ | ❌ | ❌ |
+| [`GEOMETRY`](pathname:///api/v7/classes/_sequelize_core.index.unknown.GEOMETRY.html) | [`GEOMETRY`](https://postgis.net/workshops/postgis-intro/geometries.html) | `GEOMETRY` | `GEOMETRY` | ❌ | ❌ | ❌ | ❌ | ❌ |
| `GEOMETRY('POINT')` | `GEOMETRY(POINT)` | `POINT` | `POINT` | ❌ | ❌ | ❌ | ❌ | ❌ |
| `GEOMETRY('POINT', 4326)` | `GEOMETRY(POINT,4326)` | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| `GEOMETRY('POLYGON')` | `GEOMETRY(POLYGON)` | `POLYGON` | `POLYGON` | ❌ | ❌ | ❌ | ❌ | ❌ |
| `GEOMETRY('LINESTRING')` | `GEOMETRY(LINESTRING)` | `LINESTRING` | `LINESTRING` | ❌ | ❌ | ❌ | ❌ | ❌ |
-| [`GEOGRAPHY`](pathname:///api/v7/interfaces/DataTypes.GeographyDataTypeConstructor.html) | [`GEOGRAPHY`](https://postgis.net/workshops/postgis-intro/geography.html) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
+| [`GEOGRAPHY`](pathname:///api/v7/classes/_sequelize_core.index.unknown.GEOGRAPHY.html) | [`GEOGRAPHY`](https://postgis.net/workshops/postgis-intro/geography.html) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| `HSTORE` | [`HSTORE`](https://www.postgresql.org/docs/9.1/hstore.html) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
@@ -327,8 +340,6 @@ Sequelize provides a special syntax to query the contents of a JSON object. [Rea
In Postgres, the GEOMETRY and GEOGRAPHY types are implemented by [the PostGIS extension](https://postgis.net/workshops/postgis-intro/geometries.html).
-In Postgres, You must install the [pg-hstore](https://www.npmjs.com/package/pg-hstore) package if you use `DataTypes.HSTORE`
-
:::
## DataTypes exclusive to PostgreSQL
@@ -368,7 +379,7 @@ DataTypes.RANGE(DataTypes.DATEONLY) // daterange
DataTypes.RANGE(DataTypes.DECIMAL) // numrange
```
-Since range types have extra information for their bound inclusion/exclusion it's not very straightforward to just use a tuple to represent them in javascript.
+Since range types have extra information for their bound inclusion/exclusion it's not very straightforward to just use a tuple to represent them in JavaScript.
When supplying ranges as values you can choose from the following APIs:
@@ -453,15 +464,16 @@ User.init({
| Sequelize DataType | PostgreSQL | MariaDB | MySQL | MSSQL | SQLite | Snowflake | db2 | ibmi |
|--------------------|--------------------------------------------------------------------------|---------|-------|-------|--------|-----------|-----|------|
-| `CIDR` | [`CIDR`](https://www.postgresql.org/docs/9.1/datatype-net-types.html) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
-| `INET` | [`INET`](https://www.postgresql.org/docs/9.1/datatype-net-types.html) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
-| `MACADDR` | [`MACADDR`](https://www.postgresql.org/docs/9.1/datatype-net-types.html) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
+| `CIDR` | [`CIDR`](https://www.postgresql.org/docs/11/datatype-net-types.html) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
+| `INET` | [`INET`](https://www.postgresql.org/docs/11/datatype-net-types.html) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
+| `MACADDR` | [`MACADDR`](https://www.postgresql.org/docs/11/datatype-net-types.html) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
+| `MACADDR8` | [`MACADDR8`](https://www.postgresql.org/docs/11/datatype-net-types.html) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
## Virtual
-`DataTypes.VIRTUAL` is a special DataType used to declare [virtual attributes](../core-concepts/getters-setters-virtuals.md#virtual-fields).
+`DataTypes.VIRTUAL` is a special DataType used to declare [virtual attributes](./getters-setters-virtuals.md#virtual-attributes).
It does not create an actual column.
:::caution
@@ -473,10 +485,10 @@ See [the issue about generated columns](https://github.com/sequelize/sequelize/i
## Custom Data Types
-Databases support more Data Types that are not covered by the ones built-in in Sequelize.
-If you need to use such a Data Types, you can [create your own DataType](./extending-data-types.md).
+Databases support more Data Types that are not covered by the ones built-in in Sequelize.
+If you need to use such a Data Types, you can [create your own DataType](../other-topics/extending-data-types.md).
-It is also possible to use a raw SQL string as the type of your attribute.
+It is also possible to use a raw SQL string as the type of your attribute.
This string will be used as-is as the type of your column when creating the table.
```typescript
@@ -489,7 +501,7 @@ User = sequelize.define('user', {
**Caution:** Sequelize will not do any extra type transformation or validation on an attribute declared like this. Use wisely!
-And, of course, you can open a feature request in [the Sequelize repository](https://github.com/sequelize/sequelize)
+And, of course, you can open a feature request in [the Sequelize repository](https://github.com/sequelize/sequelize)
to request the addition of a new built-in DataType.
[mdn-number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number
diff --git a/docs/models/defining-models.mdx b/docs/models/defining-models.mdx
new file mode 100644
index 00000000..2f065323
--- /dev/null
+++ b/docs/models/defining-models.mdx
@@ -0,0 +1,614 @@
+---
+sidebar_position: 1
+sidebar_label: Defining a Model
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+import DecoratorInfo from '../_fragments/_decorator-info.mdx';
+import JsDefaultCaution from '../_fragments/_js-default-caution.mdx';
+
+# 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](https://en.wikipedia.org/wiki/Data_access_object).
+
+## 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](#without-decorators), but you should read this chapter first.
+
+
+
+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.
+
+
+
+
+```ts
+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, InferCreationAttributes> {
+ @Attribute(DataTypes.INTEGER)
+ @PrimaryKey
+ @AutoIncrement
+ declare id: CreationOptional;
+
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ declare firstName: string;
+
+ @Attribute(DataTypes.STRING)
+ declare lastName: string | null;
+}
+```
+
+
+
+
+```js
+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](pathname:///api/v7/modules/_sequelize_core.decorators_legacy.html).
+You can also [define associations between models](../associations/basics.md), 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:
+
+```ts
+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):
+
+```ts
+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`](https://github.com/mrmlnc/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](#defining-a-model-1).
+
+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](../other-topics/legacy-model-definitions.mdx) 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](./data-types.mdx).
+
+## Nullability
+
+Just like in SQL, attributes are nullable by default in Sequelize
+
+You can change this behavior by using the [`@NotNull`] decorator:
+
+
+
+
+```ts
+import { DataTypes, Model, InferAttributes, InferCreationAttributes } from '@sequelize/core';
+import { Attribute, NotNull } from '@sequelize/core/decorators-legacy';
+
+class User extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.STRING)
+ // highlight-next-line
+ @NotNull
+ declare firstName: string;
+
+ @Attribute(DataTypes.STRING)
+ declare lastName: string | null;
+}
+```
+
+
+
+
+```js
+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)
+ // highlight-next-line
+ @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:
+
+
+
+
+```ts
+import { DataTypes, Model, InferAttributes, InferCreationAttributes, CreationOptional } from '@sequelize/core';
+import { Attribute, NotNull, Default } from '@sequelize/core/decorators-legacy';
+
+class User extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ // highlight-next-line
+ @Default('John')
+ declare firstName: CreationOptional;
+}
+```
+
+:::info `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.
+
+:::
+
+
+
+
+```js
+import { DataTypes, Model } from '@sequelize/core';
+import { Attribute, NotNull, Default } from '@sequelize/core/decorators-legacy';
+
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ // highlight-next-line
+ @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`](./data-types.mdx#built-in-default-values-for-dates) for the current timestamp,
+but a custom function can be used as well:
+
+
+
+
+```ts
+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, InferCreationAttributes> {
+ @PrimaryKey
+ @Attribute(DataTypes.STRING(18))
+ // Generate a new ID every time a new instance is created
+ // highlight-next-line
+ @Default(() => uniqid())
+ declare id: CreationOptional;
+
+ @Attribute(DataTypes.DATE)
+ @NotNull
+ // The current date/time will be used to populate this column at the moment of insertion
+ // highlight-next-line
+ @Default(DataTypes.NOW)
+ declare registeredAt: CreationOptional;
+}
+```
+
+
+
+
+```js
+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
+ // highlight-next-line
+ @Default(() => uniqid())
+ id;
+
+ @Attribute(DataTypes.DATE)
+ @NotNull
+ // The current date/time will be used to populate this column at the moment of insertion
+ // highlight-next-line
+ @Default(DataTypes.NOW)
+ registeredAt;
+}
+```
+
+
+
+
+
+
+### Dynamic SQL default values
+
+You can also use a SQL function as a default value using [raw SQL](../querying/raw-queries.mdx).
+This has the advantage over [dynamic JS default values](#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`](../querying/raw-queries.mdx#sqlfn) function to make the database generate a random number for you:
+
+
+
+
+```ts
+import { DataTypes, Model, sql, InferAttributes, InferCreationAttributes, CreationOptional } from '@sequelize/core';
+import { Attribute, Default } from '@sequelize/core/decorators-legacy';
+
+class User extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.FLOAT)
+ @Default(sql.fn('random'))
+ declare randomNumber: CreationOptional;
+}
+```
+
+
+
+
+```js
+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`](./data-types.mdx#built-in-default-values-for-uuid), 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:
+
+```ts
+import { DataTypes, Model, InferAttributes, InferCreationAttributes, CreationOptional } from '@sequelize/core';
+
+class User extends Model, InferCreationAttributes> {
+ // highlight-next-line
+ declare id: CreationOptional;
+}
+```
+
+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:
+
+
+
+
+```ts
+import { DataTypes, Model, InferAttributes, InferCreationAttributes, CreationOptional } from '@sequelize/core';
+import { Attribute, PrimaryKey, AutoIncrement } from '@sequelize/core/decorators-legacy';
+
+class User extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.INTEGER)
+ // highlight-next-line
+ @PrimaryKey
+ // highlight-next-line
+ @AutoIncrement
+ declare internalId: CreationOptional;
+}
+```
+
+:::info `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.
+
+:::
+
+
+
+
+```ts
+import { DataTypes, Model } from '@sequelize/core';
+import { Attribute, PrimaryKey, AutoIncrement } from '@sequelize/core/decorators-legacy';
+
+class User extends Model {
+ @Attribute(DataTypes.INTEGER)
+ // highlight-next-line
+ @PrimaryKey
+ // highlight-next-line
+ @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](./advanced.mdx#prevent-creating-a-default-pk-attribute).
+
+### Composite Primary Keys
+
+Composite primary keys are supported by using the [`@PrimaryKey`] decorator on multiple attributes:
+
+
+
+
+```ts
+import { DataTypes, Model, InferAttributes, InferCreationAttributes } from '@sequelize/core';
+import { Attribute, PrimaryKey } from '@sequelize/core/decorators-legacy';
+
+class UserRole extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.INTEGER)
+ // highlight-next-line
+ @PrimaryKey
+ declare userId: number;
+
+ @Attribute(DataTypes.INTEGER)
+ // highlight-next-line
+ @PrimaryKey
+ declare roleId: number;
+}
+```
+
+
+
+
+```js
+import { DataTypes, Model } from '@sequelize/core';
+import { Attribute, PrimaryKey } from '@sequelize/core/decorators-legacy';
+
+class UserRole extends Model {
+ @Attribute(DataTypes.INTEGER)
+ // highlight-next-line
+ @PrimaryKey
+ userId;
+
+ @Attribute(DataTypes.INTEGER)
+ // highlight-next-line
+ @PrimaryKey
+ roleId;
+}
+```
+
+
+
+
+## Table & Column names
+
+Head to the [Naming Strategies API](./naming-strategies.mdx) 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:
+
+```typescript
+import { Model } from '@sequelize/core';
+import { Table } from '@sequelize/core/decorators-legacy';
+
+// This model's table will be created in the "public" schema
+// highlight-next-line
+@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:
+
+```js
+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
+ // highlight-next-line
+ schema: 'public',
+ models: [User],
+});
+```
+
+## Model Methods
+
+Sequelize models are [classes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes).
+You can very easily add custom instance or class level methods.
+
+:::info
+
+[`Model`] provides a suite of built-in static & instance methods that can be used to interact with the database.
+
+Take a look at the API Reference for [`Model`] for more information
+
+:::
+
+
+
+
+```typescript
+import { Model, InferCreationAttributes, InferAttributes } from '@sequelize/core';
+import { SqliteDialect } from '@sequelize/sqlite3';
+import { Attribute, NotNull } from '@sequelize/core/decorators-legacy';
+
+class User extends Model, InferCreationAttributes> {
+ @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'
+```
+
+
+
+
+```javascript
+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](./model-synchronization.md), after which you'll be able to [create, read, update and delete](../querying/insert.md) your models.
+
+Alternatively, find out how to [define associations](../associations/basics.md) between models, learn about [indexes](./indexes.md), [constraints](./validations-and-constraints.md), 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.
+
+[`Model`]: pathname:///api/v7/classes/_sequelize_core.index.Model.html
+[`@Table`]: pathname:///api/v7/functions/_sequelize_core.decorators_legacy.Table.html
+[`@Attribute`]: pathname:///api/v7/functions/_sequelize_core.decorators_legacy.Attribute.html
+[`@NotNull`]: pathname:///api/v7/functions/_sequelize_core.decorators_legacy.NotNull.html
+[`@Default`]: pathname:///api/v7/functions/_sequelize_core.decorators_legacy.Default.html
+[`@PrimaryKey`]: pathname:///api/v7/functions/_sequelize_core.decorators_legacy.PrimaryKey.html
+[`@AutoIncrement`]: pathname:///api/v7/functions/_sequelize_core.decorators_legacy.AutoIncrement.html
+[`importModels`]: pathname:///api/v7/functions/_sequelize_core.index.importModels.html
+[`import`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import
+[`require`]: https://nodejs.org/api/modules.html#requireid
+[`CreationOptional`]: pathname:///api/v7/types/_sequelize_core.index.CreationOptional.html
+[`fn`]: pathname:///api/v7/variables/_sequelize_core.index.sql.fn.html
+[`literal`]: pathname:///api/v7/variables/_sequelize_core.index.sql.literal.html
+[`Sequelize`]: pathname:///api/v7/classes/_sequelize_core.index.Sequelize.html
+[`ValidationError`]: pathname:///api/v7/classes/_sequelize_core.index.ValidationError.html
+[`QueryInterface#addConstraint`]: pathname:///api/v7/classes/_sequelize_core.index.AbstractQueryInterface.html#addConstraint
+[`Model#validate`]: pathname:///api/v7/classes/index.Model.html#validate
diff --git a/docs/models/getters-setters-virtuals.md b/docs/models/getters-setters-virtuals.md
new file mode 100644
index 00000000..43df075f
--- /dev/null
+++ b/docs/models/getters-setters-virtuals.md
@@ -0,0 +1,211 @@
+---
+sidebar_position: 5
+title: Getters, Setters & Virtuals
+---
+
+Sequelize allows you to define custom getters and setters for the attributes of your models.
+
+Sequelize also allows you to specify the so-called [*virtual attributes*](#virtual-attributes),
+which are attributes on the Sequelize Model that doesn't really exist in the underlying SQL table, but instead are populated automatically by Sequelize.
+They are very useful to create custom attributes which also could simplify your code.
+
+## Attribute Getters & Setters
+
+Attribute Getters & Setters are like any other JavaScript getter and setters, but cause the creation of an attribute in the model definition.
+The main advantage is that Sequelize will call these getters and setters automatically when the attribute is read or set.
+
+You must decorate your getter or setter with [attribute decorators](defining-models.mdx), just like you would with any other attribute.
+
+Unlike the standard JavaScript getters & setters, __you do not need to define both a getter and a setter for the same attribute__.
+Sequelize will automatically create a setter for you if you only define a getter, and vice versa. You can of course define
+both if you need to.
+
+### Getters
+
+Getters will be called automatically when the attribute is read, be it through `model.get('attribute')`, or `model.attribute`.
+The only exception is `model.getDataValue('attribute')`, which will return the raw value of the attribute, without calling the getter.
+
+```ts
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ get username(): string {
+ return this.getDataValue('username').toUpperCase();
+ }
+}
+
+const user = User.build({ username: 'SuperUser123' });
+
+// This will call the getter
+console.log(user.username); // 'SUPERUSER123'
+
+// This will not call the getter
+console.log(user.getDataValue('username')); // 'SuperUser123'
+```
+
+:::caution Accessing the attribute value
+
+Inside your getter or setter, you should use `this.getDataValue('attributeName')` to access the value of the attribute, and
+`this.setDataValue('attributeName', value)` to set the value of the attribute.
+
+If you try to access the attribute directly, you will get an infinite loop.
+
+```ts
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ get username(): string {
+ // This will call the getter again
+ // error-next-line
+ return this.username.toUpperCase();
+ }
+}
+```
+
+:::
+
+### Setters
+
+Setters will be called automatically when the attribute is set, be it through `model.set('attribute', value)`, or `model.attribute = value`.
+The only exception is `model.setDataValue('attribute', value)`, which will set the raw value of the attribute, without calling the setter.
+
+```ts
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ set username(value: string) {
+ this.setDataValue('username', value.toUpperCase());
+ }
+}
+```
+
+:::caution Static Methods
+
+Static model methods do not interact with the instance of the model, and therefore will ignore any getters or setters defined for the model.
+
+```ts
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ set username(value: string) {
+ this.setDataValue('username', value.toUpperCase());
+ }
+}
+
+// This will insert the value as-is, without calling the setter,
+// so it will not be converted to uppercase
+await User.update({
+ username: 'ephys',
+}, {
+ where: { id: 1 },
+});
+```
+
+:::
+
+:::caution Setter dependencies
+
+While it is possible for a setter to use the value of another attribute,
+be aware that the setter will not be called again if the other attribute changes.
+
+The setter is only called when the value of the attribute is set, and is called immediately. Accessing the value
+of another attribute inside the setter can lead to unexpected results depending on the order of operations.
+
+```ts
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ declare username: string;
+
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ set password(value: string) {
+ // Accessing the value of another attribute inside the setter can lead to unexpected results
+ // error-next-line
+ this.setDataValue('password', hash(this.username + value));
+ }
+}
+```
+
+:::
+
+## Virtual attributes
+
+Virtual attributes are attributes that Sequelize populates under the hood, but in reality they don't even exist in the database.
+
+For example, let's say we have a User model with `firstName` and `lastName`[^1] attributes.
+We could decide to add a model getter to obtain the *full name* directly:
+
+```ts
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ declare firstName: string;
+
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ declare lastName: string;
+
+ // highlight-start
+ get fullName(): NonAttribute {
+ return `${this.firstName} ${this.lastName}`;
+ }
+ // highlight-end
+}
+```
+
+This works, but it does not behave like a regular attribute:
+
+- It cannot be obtained through `model.get('fullName')`.
+- It cannot be used in the attribute list of queries.
+
+To remedy this, we can declare it as a virtual attribute using the `DataTypes.VIRTUAL` data type.
+This attribute type does not create an actual column in the table,
+but the attribute can still be used in JavaScript.
+
+This attribute type takes two arguments:
+
+- The type of the value.
+- The list of attributes it depends on. This is used to automatically load the dependencies when the attribute included in the attribute list of a query.
+
+```ts
+import { DataTypes } from '@sequelize/core';
+
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ declare firstName: string;
+
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ declare lastName: string;
+
+ // highlight-next-line
+ @Attribute(DataTypes.VIRTUAL(DataTypes.STRING, ['firstName', 'lastName']))
+ get fullName(): string {
+ return `${this.firstName} ${this.lastName}`;
+ }
+}
+```
+
+:::caution Uses in queries
+
+Remember, the `VIRTUAL` attribute does not exist in the database. Sequelize lets you specify it in the attribute list of queries,
+but it will not be included in the actual query, and cannot be used anywhere else in the query.
+
+```ts
+const users = await User.findAll({
+ // 'fullName' is a virtual attribute, which will make Sequelize
+ // load 'firstName' and 'lastName' instead.
+ attributes: ['id', 'fullName'],
+ where: {
+ // This will not work.
+ // error-next-line
+ fullName: 'John Doe',
+ },
+});
+```
+
+:::
+
+[^1]: Did you know? Not everyone's name can be neatly separated into [first name & last name](https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/).
diff --git a/docs/models/indexes.md b/docs/models/indexes.md
new file mode 100644
index 00000000..49fce333
--- /dev/null
+++ b/docs/models/indexes.md
@@ -0,0 +1,338 @@
+---
+title: Indexes & Uniques
+sidebar_position: 7
+---
+
+# Indexes & Uniques
+
+Sequelize supports adding indexes to the model definition which will be created during [model synchronization](./model-synchronization.md).
+You have multiple ways of defining indexes on a model.
+
+## Single-column index
+
+The `@Index` decorator is the simplest solution to define an index. It is ideal for single-column indexes.
+To define an index, you use the `@Index` decorator on the attribute you want to index:
+
+```ts
+import { Model, DataTypes } from '@sequelize/core';
+import { Index, Attribute, NotNull } from '@sequelize/core/decorators-legacy';
+
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ // highlight-next-line
+ @Index
+ declare firstName: string;
+}
+```
+
+The `@Index` decorator accepts an optional options bag as a parameter.
+The list of available options is available in the [API documentation](pathname:///api/v7/interfaces/_sequelize_core.index.AttributeIndexOptions.html#attribute).
+
+Here is an example of a single-column _gin_ index:
+
+```ts
+import { Model, DataTypes } from '@sequelize/core';
+import { Index, Attribute, NotNull } from '@sequelize/core/decorators-legacy';
+
+class User extends Model {
+ @Attribute(DataTypes.JSONB)
+ @NotNull
+ // highlight-next-line
+ @Index({ using: 'gin' })
+ declare data: object;
+}
+```
+
+Index names are automatically generated by Sequelize, but you can also specify a custom index name using the `name` option:
+
+```ts
+import { Model, DataTypes } from '@sequelize/core';
+import { Index, Attribute, NotNull } from '@sequelize/core/decorators-legacy';
+
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ // highlight-next-line
+ @Index({ name: 'user_first_name_index' })
+ declare firstName: string;
+}
+```
+
+## Simple Multi-column indexes
+
+In the above example, we gave our index a custom name using the `name` option. This can also be exploited to create multi-column indexes:
+You can use the same index name for multiple columns and Sequelize will create a single multi-column index for you:
+
+```ts
+import { Model, DataTypes } from '@sequelize/core';
+import { Index, Attribute, NotNull } from '@sequelize/core/decorators-legacy';
+
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ // highlight-next-line
+ @Index({ name: 'firstName-lastName' })
+ declare firstName: string;
+
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ // highlight-next-line
+ @Index({ name: 'firstName-lastName' })
+ declare lastName: string;
+}
+```
+
+The `attribute` option can be used to specify the options specific to that attribute in the multi-attribute index:
+
+```ts
+import { Model, DataTypes } from '@sequelize/core';
+import { Index, Attribute, NotNull } from '@sequelize/core/decorators-legacy';
+
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ // highlight-next-line
+ @Index({ name: 'firstName-lastName' })
+ declare firstName: string;
+
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ // highlight-start
+ @Index({
+ name: 'firstName-lastName',
+ // note: collations vary greatly between databases, this is just an example.
+ // You should check your database's documentation on collations for more information.
+ attribute: { collate: 'case_insensitive' },
+ })
+ // highlight-end
+ declare lastName: string;
+}
+```
+
+:::caution
+
+While it's possible to use options other than `name` and `attribute` in multi-column indexes that use the `@Index` decorator, the practice is discouraged as
+you would have to repeat the same options on each attribute. Use [`createIndexDecorator`](#complex-multi-column-indexes) for that use case instead.
+
+:::
+
+## Complex Multi-column indexes
+
+The `createIndexDecorator` API is a convenient way to define complex multi-column indexes, without having to repeat the same options on each attribute.
+
+Without `createIndexDecorator`, a complex multi-column index would look like this:
+
+```ts
+import { Model, DataTypes } from '@sequelize/core';
+import { Index, Attribute, NotNull } from '@sequelize/core/decorators-legacy';
+
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ @Index({
+ // error-start
+ name: 'firstName-lastName',
+ type: 'fulltext',
+ concurrently: true,
+ // error-end
+ })
+ declare firstName: string;
+
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ @Index({
+ // error-start
+ name: 'firstName-lastName',
+ type: 'fulltext',
+ concurrently: true,
+ // error-end
+ attribute: { collate: 'case_insensitive' },
+ })
+ declare lastName: string;
+}
+```
+
+Notice how we had to repeat the same options on each attribute. This can become verbose very quickly.
+With `createIndexDecorator`, you only have to specify the options once:
+
+```ts
+const NameIndex = createIndexDecorator('NameIndex', {
+ // success-start
+ name: 'firstName-lastName',
+ type: 'fulltext',
+ concurrently: true,
+ // success-end
+});
+
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ // highlight-next-line
+ @NameIndex
+ declare firstName: string;
+
+ @Attribute(DataTypes.STRING)
+ // highlight-start
+ // Attribute-specific options are still specified on the attribute:
+ @NameIndex({ collate: 'case_insensitive' })
+ // highlight-end
+ declare lastName: string;
+}
+```
+
+## Alternative Syntax (`@Table`)
+
+Another way to define an index is to use the `@Table` decorator on your model. This decorator accepts an `indexes` option, which is an array of index definitions.
+
+This option can define any index that can be defined using either the `@Index` decorator, or the `createIndexDecorator` API, but it can be more cumbersome to use. Here is an example:
+
+```ts
+import { Model, DataTypes } from '@sequelize/core';
+import { Table, Attribute, NotNull } from '@sequelize/core/decorators-legacy';
+
+@Table({
+ // highlight-start
+ indexes: [
+ {
+ name: 'firstName-lastName',
+ type: 'fulltext',
+ concurrently: true,
+ // The "fields" option defines the list of attributes that make up the index, and their options:
+ fields: [
+ 'firstName',
+ { name: 'lastName', collate: 'case_insensitive' },
+ ],
+ },
+ ],
+ // highlight-end
+})
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ declare firstName: string;
+
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ declare lastName: string;
+}
+```
+
+## Unique Indexes
+
+Unique constraints are basically the same as indexes, except that they also enforce uniqueness of the column(s) they are defined on.
+
+Creating a unique index is as simple as adding the `unique` option to the index definition:
+
+```ts
+import { Model, DataTypes } from '@sequelize/core';
+import { Index, Attribute, NotNull } from '@sequelize/core/decorators-legacy';
+
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ // highlight-next-line
+ @Index({ unique: true })
+ declare email: string;
+}
+```
+
+This option is available in all ways of defining indexes.
+
+### Shorthand Syntax
+
+If all you need is for the attribute(s) to be unique, and don't actually care about the other index options, you can use the `@Unique` decorator as a shorthand for the `unique` option:
+
+```ts
+import { Model, DataTypes } from '@sequelize/core';
+import { Unique, Attribute, NotNull } from '@sequelize/core/decorators-legacy';
+
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ // highlight-next-line
+ @Unique
+ declare email: string;
+}
+```
+
+You can also create composite uniques by giving the same name to multiple `@Unique` decorators:
+
+```ts
+import { Model, DataTypes } from '@sequelize/core';
+import { Unique, Attribute, NotNull } from '@sequelize/core/decorators-legacy';
+
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ // highlight-next-line
+ @Unique('firstName-lastName')
+ declare firstName: string;
+
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ // highlight-next-line
+ @Unique('firstName-lastName')
+ declare lastName: string;
+}
+```
+
+:::caution
+
+The `@Unique` decorator does not give access to very useful index options. One such option is `collate`, which can be used to
+create case-insensitive indexes. If you need to use this option, you should use the `@Index` decorator instead.
+
+:::
+
+## Some examples
+
+:::info Did you know?
+
+All index decorators (including `@Unique`) can be used more than once on the same attribute.
+This is useful if you need your column to be part of multiple, different, composite unique constraints.
+
+:::
+
+This is a gin index that uses the `jsonb_path_ops` operator:
+
+```ts
+import { Model, DataTypes } from '@sequelize/core';
+import { Index, Attribute, NotNull } from '@sequelize/core/decorators-legacy';
+
+class User extends Model {
+ @Attribute(DataTypes.JSONB)
+ @NotNull
+ // highlight-next-line
+ @Index({ using: 'gin', operator: 'jsonb_path_ops' })
+ declare data: Record;
+}
+```
+
+This is a btree index, with a few attribute options:
+
+```ts
+import { Model, DataTypes } from '@sequelize/core';
+import { createIndexDecorator, Attribute, NotNull } from '@sequelize/core/decorators-legacy';
+
+const MyIndex = createIndexDecorator('MyIndex', {
+ using: 'btree',
+});
+
+class Book extends Model {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ // highlight-next-line
+ @MyIndex
+ declare title: string;
+
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ // highlight-start
+ @MyIndex({
+ collate: 'en_US',
+ order: 'DESC',
+ length: 5
+ })
+ // highlight-end
+ declare title: string;
+}
+```
diff --git a/docs/models/inheritance.md b/docs/models/inheritance.md
new file mode 100644
index 00000000..41325f41
--- /dev/null
+++ b/docs/models/inheritance.md
@@ -0,0 +1,87 @@
+---
+title: Inheritance
+sidebar_position: 7
+---
+
+Model inheritance is a way to share common attributes and methods between models.
+
+Sequelize supports a style of inheritance called __Concrete Table Inheritance__. This means that each [non-abstract](#abstract-models)
+model in the hierarchy has its own table in the database:
+
+
+
+
+
+Concrete Table Inheritance - Source
+
+
+
+Here is how you would implement this in Sequelize:
+
+```ts
+import { Model, InferAttributes, InferCreationAttributes } from '@sequelize/core';
+import { Attribute, Default, PrimaryKey, NotNull } from '@sequelize/core/decorators-legacy';
+import { SqliteDialect } from '@sequelize/sqlite3';
+
+@Table.Abstract
+class Player extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ declare name: string;
+}
+
+class Footballer extends Player {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ declare club: string;
+}
+
+class Cricketer extends Player {
+ @Attribute(DataTypes.INTEGER)
+ @NotNull
+ declare battingAverage: number;
+}
+
+class Bowler extends Cricketer {
+ @Attribute(DataTypes.INTEGER)
+ @NotNull
+ declare bowlingAverage: number;
+}
+
+const sequelize = new Sequelize({
+ dialect: SqliteDialect,
+ models: [Footballer, Cricketer],
+});
+```
+
+## Abstract Models
+
+Some models are not meant to be used directly. They are meant to be used as a base class for other models.
+Those models are called abstract models.
+
+Those models should not be registered in the `Sequelize` instance, to prevent them from creating a table in the database.
+To prevent a model from being registered, you can use the [`@Table.Abstract`] decorator instead of [`@Table`] on the model class.
+
+```ts
+@Table.Abstract
+abstract class BaseModel extends Model {
+ // ...
+}
+```
+
+This decorator accepts the same options as the [`@Table`] decorator, except for `name` and `tableName`,
+as these options cannot be inherited.
+
+## Limitations
+
+You cannot define [`@HasOne`](../associations/has-one.md), [`@HasMany`](../associations/has-many.md), or [`@BelongsToMany`](../associations/belongs-to-many.md)
+associations on inherited models (the parent model).
+
+This limitation is due to the fact that these associations declare a foreign key on the target model.
+Having multiple models attempting to declare the same foreign key is not possible, as a foreign key can only point to a single model.
+
+Similarly, you cannot use the `inverse` option on [`@BelongsTo`](../associations/belongs-to.md) associations on inherited models,
+as this option declares an association on the target model, whose name must be unique.
+
+[`@Table.Abstract`]: pathname:///v7/variables/_sequelize_core.decorators_legacy.table.abstract
+[`@Table`]: pathname:///api/v7/functions/_sequelize_core.decorators_legacy.table-1
diff --git a/docs/models/migrations.md b/docs/models/migrations.md
new file mode 100644
index 00000000..85fc1f88
--- /dev/null
+++ b/docs/models/migrations.md
@@ -0,0 +1,33 @@
+---
+title: Migrations
+sidebar_position: 12
+---
+
+# Database Schema Upgrades
+
+Once your database has production data that should not be lost,
+you cannot follow the `sequelize.sync` approach of dropping the databases
+and recreating them with a new schema when you need to make changes.
+
+There are multiple possible approaches to upgrading your database schema.
+Which one works best for you will depend on your specific requirements.
+
+## Using Migrations
+
+Migrations are a way to version control your database schema,
+allowing you to easily upgrade and downgrade your database as your application evolves.
+
+Sequelize provides [`@sequelize/cli`](../cli.md), a Command Line Interface that can be used to create and run migrations.
+Head to [the `@sequelize/cli` page](../cli.md) for more information on how to write migrations.
+
+Of course, you are free to use any other migration tool:
+
+- [Umzug](https://github.com/sequelize/umzug) is a great alternative that the Sequelize CLI uses under the hood.
+- Third-party tools that can be used to help with migrations are also listed on the [Third-Party Resources](../other-topics/resources.md#migrations) page.
+
+## Using a Database Diff Tool
+
+You can use a database diff tool to compare the current database schema with the new schema you want to apply.
+One such tool is [pg-diff](https://michaelsogos.github.io/pg-diff/).
+
+In this approach, you would generate a diff as part of your release process and apply it to the database.
diff --git a/docs/models/model-synchronization.md b/docs/models/model-synchronization.md
new file mode 100644
index 00000000..2727e936
--- /dev/null
+++ b/docs/models/model-synchronization.md
@@ -0,0 +1,63 @@
+---
+sidebar_position: 11
+---
+
+# Synchronizing your Models (development)
+
+When you define a model, you're telling Sequelize a few things about its table in the database. However, what if the table actually doesn't even exist in the database? What if it exists, but it has different columns, less columns, or any other difference?
+
+This is where model synchronization comes in. A model can be synchronized with the database by calling [`model.sync(options)`](pathname:///api/v7/classes/_sequelize_core.index.Model.html#sync),
+an asynchronous function (that returns a Promise). With this call, Sequelize will automatically perform an SQL query to the database.
+Note that this only changes the table in the database, not the model on the JavaScript side.
+
+* `User.sync()` - This creates the table if it doesn't exist (and does nothing if it already exists)
+* `User.sync({ force: true })` - This creates the table, dropping it first if it already existed
+* `User.sync({ alter: true })` - This checks what is the current state of the table in the database (which columns it has, what are their data types, etc), and then performs the necessary changes in the table to make it match the model.
+
+:::caution
+
+While `sync()` is a very useful tool during development, it is not designed to be used in production and using its `alter` or `force` options may lead to data loss.
+See [Migrations](./migrations.md) to learn how to update your database schema in production.
+
+:::
+
+Example:
+
+```js
+await User.sync({ force: true });
+console.log('The table for the User model was just (re)created!');
+```
+
+## Synchronizing all models at once
+
+You can use [`sequelize.sync()`](pathname:///api/v7/classes/_sequelize_core.index.Sequelize.html#sync) to automatically synchronize all models. Example:
+
+```js
+await sequelize.sync({ force: true });
+console.log('All models were synchronized successfully.');
+```
+
+## Dropping tables
+
+To drop the table related to a model:
+
+```js
+await User.drop();
+console.log('User table dropped!');
+```
+
+To drop all tables:
+
+```js
+await sequelize.drop();
+console.log('All tables dropped!');
+```
+
+## Database safety check
+
+As shown above, the `sync` and `drop` operations are destructive. Sequelize accepts a `match` option as an additional safety check, which receives a RegExp:
+
+```js
+// This will run .sync() only if database name ends with '_test'
+sequelize.sync({ force: true, match: /_test$/ });
+```
diff --git a/docs/models/model-typing.mdx b/docs/models/model-typing.mdx
new file mode 100644
index 00000000..c2bb0ae0
--- /dev/null
+++ b/docs/models/model-typing.mdx
@@ -0,0 +1,106 @@
+---
+title: Typing your Model (TypeScript)
+sidebar_position: 8
+---
+
+# Typing your Model with TypeScript
+
+## Automated Attribute Typing
+
+Model methods are strictly typed based on your attributes, but require you to define you to write a bit of configuration first.
+`Model` accept two generic types that you can use to let it know what its Attributes & Creation Attributes are like:
+
+You can use [`InferAttributes`](pathname:///api/v7/types/_sequelize_core.index.inferattributes),
+and [`InferCreationAttributes`](pathname:///api/v7/types/_sequelize_core.index.infercreationattributes) to define them. They will extract Attribute typings
+directly from the Model:
+
+```typescript
+import { Attribute, PrimaryKey, AutoIncrement, NotNull } from '@sequelize/core/decorators-legacy';
+import { Model, InferAttributes, InferCreationAttributes, CreationOptional } from '@sequelize/core';
+
+// order of InferAttributes & InferCreationAttributes is important.
+export class User extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.INTEGER)
+ @PrimaryKey
+ @AutoIncrement
+ // 'CreationOptional' is a special type that marks the attribute as optional
+ // when creating an instance of the model (such as using Model.create()).
+ declare id: CreationOptional;
+
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ declare name: string;
+}
+
+// TypeScript will now be able to catch errors such as this typo:
+// error-next-line
+await User.create({ nAme: 'john' });
+```
+
+Important things to know about how [`InferAttributes`](pathname:///api/v7/types/_sequelize_core.index.inferattributes) & [`InferCreationAttributes`](pathname:///api/v7/types/_sequelize_core.index.infercreationattributes) work,
+they will select all declared properties of the class, except for:
+
+- Static fields and methods.
+- Methods (anything whose type is a function).
+- Those whose type uses the branded type [`NonAttribute`](pathname:///api/v7/types/_sequelize_core.index.nonattribute).
+- Those excluded by using InferAttributes like this: `InferAttributes`.
+- Those declared by the Model superclass (but not intermediary classes!).
+ If one of your attributes shares the same name as one of the properties of [`Model`](pathname:///api/v7/classes/_sequelize_core.index.Model.html), change its name.
+ Doing this is likely to cause issues anyway.
+- Getter & setters are not automatically excluded. Set their return / parameter type to [`NonAttribute`](pathname:///api/v7/types/_sequelize_core.index.nonattribute),
+ or add them to `omit` to exclude them.
+
+[`InferCreationAttributes`](pathname:///api/v7/types/_sequelize_core.index.infercreationattributes) works the same way as [`InferAttributes`](pathname:///api/v7/types/_sequelize_core.index.inferattributes)
+with one exception; properties typed using the [`CreationOptional`](pathname:///api/v7/types/_sequelize_core.index.creationoptional) type will be marked as optional.
+Note that attributes that accept `null`, or `undefined` do not need to use [`CreationOptional`](pathname:///api/v7/types/_sequelize_core.index.creationoptional):
+
+```typescript
+export class User extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ declare firstName: string;
+
+ // there is no need to use CreationOptional on firstName because nullable attributes
+ // are always optional when creating an instance of the model.
+ @Attribute(DataTypes.STRING)
+ declare lastName: string | null;
+}
+
+// last name omitted, but this is still valid!
+// success-next-line
+await User.create({ firstName: 'Zoé' });
+```
+
+You only need to use `CreationOptional` & `NonAttribute` on class instance fields or getters.
+
+Example of a minimal TypeScript project with strict type-checking for attributes:
+
+## Manual Attribute Typing
+
+`InferAttributes` & `InferCreationAttributes` don't cover all use cases yet. If these type don't work for you, you can also define your attributes manually,
+although it is much more verbose:
+
+```typescript
+import { Model } from '@sequelize/core';
+import type { PartialBy } from '@sequelize/utils';
+
+type UserAttributes = {
+ id: number,
+ name: string,
+};
+
+// we're telling the Model that 'id' is optional
+// when creating an instance of the model (such as using Model.create()).
+type UserCreationAttributes = PartialBy;
+
+class User extends Model {
+ @Attribute(DataTypes.INTEGER)
+ @PrimaryKey
+ @AutoIncrement
+ declare id: number;
+
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ declare string: number;
+}
+```
diff --git a/docs/models/naming-strategies.mdx b/docs/models/naming-strategies.mdx
new file mode 100644
index 00000000..2b0dbc74
--- /dev/null
+++ b/docs/models/naming-strategies.mdx
@@ -0,0 +1,322 @@
+---
+sidebar_position: 4
+sidebar_label: Table & Column Naming Strategies
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+# Configuring Table & Column names
+
+:::info
+
+We are working on a new Naming Strategy API and seeking feedback. Don't hesitate to take a look at [issue #15312](https://github.com/sequelize/sequelize/issues/15312).
+
+:::
+
+This chapter is about customizing the name of Database Columns & Tables, not to be confused with JavaScript [Attributes & Models](../terminology.md).
+
+## Table Names
+
+Sequelize will automatically infer table names from your model names, unless a table name is provided manually.
+
+:::info
+
+We recommend naming your Models using the singular form of a word, in [UpperCamelCase](https://en.wikipedia.org/wiki/Pascal_case).
+*e.g. __User__ instead of __users__*
+
+:::
+
+By default, Sequelize uses a pluralized version of the model name as the table name.
+This pluralization is done under the hood by a library called [inflection](https://www.npmjs.com/package/inflection),
+which means irregular plurals (such as `person -> people`) are computed correctly:
+
+```javascript
+import { Model, Sequelize } from '@sequelize/core';
+import { SqliteDialect } from '@sequelize/sqlite3';
+
+class User extends Model {}
+
+const sequelize = new Sequelize({
+ dialect: SqliteDialect,
+ models: [User],
+});
+
+console.log(User.table.tableName);
+// → Users
+```
+
+### Snake-case table names
+
+By default, Sequelize does not change the casing of your model name when inferring the table name.
+
+In some databases, it is conventional to have table names in [snake_case](https://en.wikipedia.org/wiki/Snake_case).
+You can make Sequelize generate a snake_cased table name by setting the [`underscored`](pathname:///api/v7/interfaces/_sequelize_core.index.ModelOptions.html#underscored) option.
+
+:::info
+
+The `underscored` option also impacts Column Names. [See *Snake-case column names*](#snake-case-column-names).
+
+:::
+
+```javascript
+import { Model, Sequelize } from '@sequelize/core';
+import { Table } from '@sequelize/core/decorators-legacy';
+import { SqliteDialect } from '@sequelize/sqlite3';
+
+@Table({
+ // highlight-next-line
+ underscored: true,
+})
+class User extends Model {}
+
+const sequelize = new Sequelize({
+ dialect: SqliteDialect,
+ models: [User],
+});
+
+console.log(User.table.tableName);
+// → users (notice the lowercase u)
+```
+
+This behavior can also be defined globally by setting the option in the Sequelize constructor:
+
+```js
+import { Model, Sequelize } from '@sequelize/core';
+import { SqliteDialect } from '@sequelize/sqlite3';
+
+class User extends Model {}
+
+const sequelize = new Sequelize({
+ dialect: SqliteDialect,
+ define: {
+ // highlight-next-line
+ underscored: true,
+ },
+ models: [User],
+});
+
+console.log(User.table.tableName);
+// → users
+```
+
+### Enforcing the table name to be equal to the model name
+
+You can stop the auto-pluralization performed by Sequelize using the [`freezeTableName: true`](pathname:///api/v7/interfaces/_sequelize_core.index.ModelOptions.html#freezeTableName) option.
+Sequelize will use the model name as the table name, without any modifications.
+
+Note that setting `freezeTableName` also causes Sequelize to ignore the `underscored` option for table names.
+
+```javascript
+import { SqliteDialect } from '@sequelize/sqlite3';
+import { Model, Sequelize } from '@sequelize/core';
+import { Table } from '@sequelize/core/decorators-legacy';
+
+@Table({
+ // highlight-next-line
+ freezeTableName: true,
+})
+class User extends Model {}
+
+const sequelize = new Sequelize({
+ dialect: SqliteDialect,
+ models: [User],
+});
+
+console.log(User.table.tableName);
+// → User
+```
+
+This behavior can also be defined globally by setting the option in the Sequelize constructor:
+
+```js
+import { Model, Sequelize } from '@sequelize/core';
+import { SqliteDialect } from '@sequelize/sqlite3';
+
+class User extends Model {}
+
+const sequelize = new Sequelize({
+ dialect: SqliteDialect,
+ define: {
+ // highlight-next-line
+ freezeTableName: true,
+ },
+ models: [User],
+});
+
+console.log(User.table.tableName);
+// → User
+```
+
+### Manually setting the table name
+
+You can also tell Sequelize the name of the table directly. This will skip any automatic table name generation options and bypass snake_casing and pluralization.
+
+```ts
+import { Model } from '@sequelize/core';
+import { Table } from '@sequelize/core/decorators-legacy';
+
+// highlight-next-line
+@Table({ tableName: 'users' })
+class User extends Model {}
+```
+
+## Column Names
+
+:::info
+
+We recommend naming your attributes in [lowerCamelCase](https://en.wikipedia.org/wiki/Pascal_case).
+*e.g. __createdAt__ instead of __created_at__*.
+
+:::
+
+By default, Sequelize will use the name of your attributes as-is as the name of your columns.
+
+### Snake-case column names
+
+Just like for [table names](#snake-case-table-names), the `underscored` option can be used
+to make Sequelize infer [snake_cased](https://en.wikipedia.org/wiki/Snake_case) column names from the attribute names.
+
+This option applies to any attribute that does not [explicitly set the `columnName` option](#manually-setting-the-column-name),
+including attributes automatically generated by Sequelize such as [association](../associations/basics.md) foreign-keys and [auto-generated timestamps](./auto-timestamps.mdx).
+
+---
+
+In the following example, we have the models User and Task, both using the `underscored` option.
+We also have a [One-to-Many relationship](../associations/basics.md#one-to-many-relationships) between them.
+
+
+
+
+```typescript
+import { Model, NonAttribute, InferAttributes, InferCreationAttributes } from '@sequelize/core';
+import { Table, Attribute, BelongsTo } from '@sequelize/core/decorators-legacy';
+
+@Table({ underscored: true })
+class User extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.STRING)
+ declare username: string | null;
+}
+
+@Table({ underscored: true })
+class Task extends Model {
+ @Attribute(DataTypes.STRING)
+ declare title: string | null;
+
+ @BelongsTo(() => User)
+ declare user: NonAttribute;
+
+ // added by @BelongsTo
+ declare UserId: number;
+}
+```
+
+
+
+
+```js
+import { Model } from '@sequelize/core';
+import { Table, Attribute, BelongsTo } from '@sequelize/core/decorators-legacy';
+
+@Table({ underscored: true })
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ username;
+}
+
+@Table({ underscored: true })
+class Task extends Model {
+ @Attribute(DataTypes.STRING)
+ title;
+
+ @BelongsTo(() => User)
+ user;
+}
+```
+
+
+
+
+:::info About Foreign Key Attributes
+
+[Associations](../associations/basics.md) automatically add the necessary Foreign Key attributes to your models.
+
+By default, the name of this attribute is equal to the name of the target model + the name of its primary key. In this example,
+an attribute called `UserId` is added on the `Task` model.
+
+:::
+
+Without the `underscored` option, Sequelize would automatically define:
+
+* A [`createdAt`](./auto-timestamps.mdx) attribute for each model, pointing to a column named `createdAt` in each table.
+* An [`updatedAt`](./auto-timestamps.mdx) attribute for each model, pointing to a column named `updatedAt` in each table.
+* A `UserId` attribute in the `Task` model, pointing to a column named `UserId` in the task table.
+
+With the `underscored` option enabled, Sequelize will instead define:
+
+* A [`createdAt`](./auto-timestamps.mdx) attribute for each model, pointing to a column named `created_at` in each table.
+* An [`updatedAt`](./auto-timestamps.mdx) attribute for each model, pointing to a column named `updated_at` in each table.
+* A `UserId` attribute in the `Task` model, pointing to a column named `user_id` in the task table.
+
+Note that in both cases the fields are still [camelCase](https://en.wikipedia.org/wiki/Camel_case) in the JavaScript side; this option only changes how these fields are mapped to the database itself. The `field` option of every attribute is set to their snake_case version, but the attribute itself remains camelCase.
+
+With `underscored: true`, calling `sequelize.sync()` after the above code will execute the following:
+
+```sql
+CREATE TABLE IF NOT EXISTS "users" (
+ "id" SERIAL,
+ "username" VARCHAR(255),
+ "created_at" TIMESTAMP WITH TIME ZONE NOT NULL,
+ "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL,
+ PRIMARY KEY ("id")
+);
+
+CREATE TABLE IF NOT EXISTS "tasks" (
+ "id" SERIAL,
+ "title" VARCHAR(255),
+ "created_at" TIMESTAMP WITH TIME ZONE NOT NULL,
+ "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL,
+ "user_id" INTEGER REFERENCES "users" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
+ PRIMARY KEY ("id")
+);
+```
+
+### Manually setting the column name
+
+It is also possible to manually set the name of a column by using the `@ColumnName` decorator.
+This name takes precedences and will be used as-is. The `underscored` option is ignored for this column.
+
+
+
+
+```ts
+import { DataTypes, Model, InferAttributes, InferCreationAttributes } from '@sequelize/core';
+import { Attribute, ColumnName } from '@sequelize/core/decorators-legacy';
+
+class User extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.STRING)
+ // This attribute will be named "first_name" in SQL, and "firstName" in JavaScript.
+ // highlight-next-line
+ @ColumnName('first_name')
+ declare firstName: number;
+}
+```
+
+
+
+
+```ts
+import { DataTypes, Model } from '@sequelize/core';
+import { Attribute, ColumnName } from '@sequelize/core/decorators-legacy';
+
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ // This attribute will be named "first_name" in SQL, and "firstName" in JavaScript.
+ // highlight-next-line
+ @ColumnName('first_name')
+ firstName;
+}
+```
+
+
+
diff --git a/docs/models/optimistic-locking.mdx b/docs/models/optimistic-locking.mdx
new file mode 100644
index 00000000..45a03be9
--- /dev/null
+++ b/docs/models/optimistic-locking.mdx
@@ -0,0 +1,50 @@
+---
+title: Optimistic Locking
+sidebar_position: 10
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Sequelize has built-in support for [optimistic locking](https://en.wikipedia.org/wiki/Optimistic_concurrency_control) through a model instance version count.
+
+Whenever Sequelize needs to modify the model,
+it will make sure that the version count on the model instance is equal to the version count in the database.
+If the counts are different, an `OptimisticLockError` will be thrown.
+
+## Enabling optimistic locking
+
+Optimistic locking is disabled by default and can be enabled by using the `@Version` decorator on the attribute of your model
+that should be used as the version count.
+
+
+
+
+
+```typescript
+import { InferCreationAttributes, InferAttributes, Model, CreationOptional } from '@sequelize/core';
+import { Version } from '@sequelize/core/decorators-legacy';
+
+class User extends Model, InferCreationAttributes> {
+ // highlight-next-line
+ @Version
+ declare version: CreationOptional;
+}
+```
+
+
+
+
+```javascript
+import { Model } from '@sequelize/core';
+import { Version } from '@sequelize/core/decorators-legacy';
+
+class User extends Model {
+ // highlight-next-line
+ @Version
+ version;
+}
+```
+
+
+
diff --git a/docs/core-concepts/paranoid.md b/docs/models/paranoid.mdx
similarity index 62%
rename from docs/core-concepts/paranoid.md
rename to docs/models/paranoid.mdx
index ebdeef9f..203cccee 100644
--- a/docs/core-concepts/paranoid.md
+++ b/docs/models/paranoid.mdx
@@ -1,29 +1,55 @@
---
-sidebar_position: 9
-title: Paranoid
+sidebar_position: 10
---
-Sequelize supports the concept of *paranoid* tables. A *paranoid* table is one that, when told to delete a record, it will not truly delete it. Instead, a special column called `deletedAt` will have its value set to the timestamp of that deletion request.
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+# Paranoid Models
+
+Sequelize supports the concept of *paranoid* tables.
+A *paranoid* table is one that, when told to delete a record,
+will not truly delete it.
+Instead, it will record that the model is deleted by setting its "deleted at" column to the current timestamp.
This means that paranoid tables perform a *soft-deletion* of records, instead of a *hard-deletion*.
## Defining a model as paranoid
-To make a model paranoid, you must pass the `paranoid: true` option to the model definition. Paranoid requires timestamps to work (i.e. it won't work if you also pass `timestamps: false`).
+To make a model paranoid, you must use the `@DeletedAt` decorator on an attribute.
+That attribute will then be used by Sequelize to record the deletion timestamp.
-You can also change the default column name (which is `deletedAt`) to something else.
+
+
-```js
-class Post extends Model {}
-Post.init({ /* attributes here */ }, {
- sequelize,
- paranoid: true,
+```typescript
+import { InferCreationAttributes, InferAttributes, Model } from '@sequelize/core';
+import { DeletedAt } from '@sequelize/core/decorators-legacy';
- // If you want to give a custom name to the deletedAt column
- deletedAt: 'destroyTime'
-});
+class User extends Model, InferCreationAttributes> {
+ // highlight-next-line
+ @DeletedAt
+ declare deletedAt: Date | null;
+}
```
+
+
+
+```javascript
+import { Model } from '@sequelize/core';
+import { DeletedAt } from '@sequelize/core/decorators-legacy';
+
+class User extends Model {
+ // highlight-next-line
+ @DeletedAt
+ deletedAt;
+}
+```
+
+
+
+
## Deleting
When you call the `destroy` method, a soft-deletion will happen:
@@ -106,3 +132,22 @@ await Post.findAll({
paranoid: false
}); // This will also retrieve soft-deleted records
```
+
+### Eager-loading soft-deleted records
+
+:::note
+
+Eager-loading is described in depth in the [SELECT Queries: In Depth guide](../querying/select-in-depth.md#eager-loading-include).
+
+:::
+
+If you want to eager load soft deleted records, you can do that by setting `include.paranoid` to `false`:
+
+```js
+User.findAll({
+ include: [{
+ association: 'projects',
+ paranoid: false,
+ }],
+});
+```
diff --git a/docs/models/validations-and-constraints.md b/docs/models/validations-and-constraints.md
new file mode 100644
index 00000000..c2f3298e
--- /dev/null
+++ b/docs/models/validations-and-constraints.md
@@ -0,0 +1,261 @@
+---
+sidebar_position: 6
+title: Validations & Constraints
+---
+
+[__Validations__](#validators) are checks performed by Sequelize, **in pure JavaScript**.
+They can be arbitrarily complex if you provide a custom validator function,
+or can be one of the [built-in validators](#attribute-validators) offered by Sequelize.
+If validation fails, no SQL query will be sent to the database at all.
+
+__Constraints__ are rules defined **at the SQL level** and are enforced by the Database.
+Common examples are `UNIQUE`. `NOT NULL` and foreign key constraints.
+
+## Not Null Constraints
+
+We already talked about Not Null Constraints in the [section about Defining Models](./defining-models.mdx#nullability).
+Using the [`@NotNull`] decorator, you can define a Not Null Constraint on a column.
+
+Sequelize also automatically adds a Not Null Validator to the attribute, meaning the Sequelize will also validate that the attribute is not null
+before sending the query to the database.
+
+If an attempt is made to set `null` to an attribute that does not allow null, a [`ValidationError`] will be thrown *without any SQL query being performed*.
+
+## Unique Constraints
+
+Unique constraints are created as unique indexes in the database. Read more about them in the [documentation on Indexes](./indexes.md#unique-indexes).
+
+## Foreign Key Constraints
+
+There are two ways of defining foreign key constraints in Sequelize:
+
+- By [defining an association](../associations/basics.md) between two models (**recommended**).
+- Using the [`references`](pathname:///api/v7/interfaces/_sequelize_core.index.ForeignKeyOptions.html#references) option of the [`@Attribute`] decorator.
+
+## Check Constraints
+
+:::note
+
+Sequelize does not provide any way to declare Check Constraints on tables yet, but you can use the low-level [`QueryInterface#addConstraint`] API to add one yourself.
+
+We plan on adding a way to easily declare check constraints on models. See [issue #11211](https://github.com/sequelize/sequelize/issues/11211).
+
+:::
+
+This example showcases how to add a check constraint after a table has been created though [`sync`](./model-synchronization.md):
+
+```ts
+import { Sequelize, Model, InferAttributes, InferCreationAttributes } from '@sequelize/core';
+import { NotNull, Attribute, AfterSync } from '@sequelize/core/decorators-legacy';
+import { SqliteDialect } from '@sequelize/sqlite3';
+
+class User extends Model, InferCreationAttributes> {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ declare email: string;
+
+ @AfterSync
+ static async onSync() {
+ // highlight-next-line
+ await this.sequelize.queryInterface.addConstraint(this.table, {
+ fields: ['email'],
+ type: 'check',
+ where: {
+ email: {
+ [Op.like]: '%@sequelizejs.com',
+ },
+ },
+ });
+ }
+}
+
+const sequelize = new Sequelize({
+ dialect: SqliteDialect,
+ models: [User],
+});
+
+await sequelize.sync();
+```
+
+:::caution
+
+We do not recommend using `sync` in production, as it can lead to data loss. See [Model Synchronization](./model-synchronization.md) for more information.
+
+:::
+
+## Validators
+
+Validators are JavaScript functions that are run before an instance is persisted or updated in the database.
+You can also run validators manually using [`Model#validate`](pathname:///api/v7/classes/_sequelize_core.index.Model.html#validate).
+
+### Attribute validators
+
+Attribute validators are used to validate the value of a single attribute.
+Sequelize provides a number of built-in validators, through extra packages, but you can also define your own.
+
+Sequelize provides the [`@ValidateAttribute`] decorator to define custom validators.
+
+```ts
+import { Model, DataTypes } from '@sequelize/core';
+import { Attribute, NotNull, ValidateAttribute } from '@sequelize/core/decorators-legacy';
+
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ // highlight-start
+ @ValidateAttribute((value: unknown, user: User, attributeName: string) => {
+ // this function will run when this attribute is validated.
+ if (name.length === 0) {
+ throw new Error('Name cannot be empty');
+ }
+ })
+ // highlight-end
+ declare name: string;
+}
+```
+
+### `@sequelize/validator.js`
+
+The [`@sequelize/validator.js`](https://www.npmjs.com/package/@sequelize/validator.js) package provides a number validators
+based on the [`validator.js`](https://www.npmjs.com/package/validator) package, such as email validation and regex matching.
+
+__⚠️ As indicated in the validator.js documentation, the library validates and sanitizes strings only.__
+
+```ts
+import { Model, DataTypes } from '@sequelize/core';
+import { Attribute, NotNull } from '@sequelize/core/decorators-legacy';
+import { IsEmail } from '@sequelize/validator.js';
+
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ @NotNull
+ // highlight-next-line
+ @IsEmail
+ declare email: string;
+}
+```
+
+Head to [its TypeDoc page](pathname:///api/v7/modules/_sequelize_validator_js.html) for the list of available validators.
+
+:::note Future development
+
+We're working on adding support for more validation libraries, see [issue #15497](https://github.com/sequelize/sequelize/issues/15497).
+
+:::
+
+### Model validators
+
+You can also define validators that run on the whole model, rather than on a single attribute.
+
+Model validator methods are called with the model object's context and are deemed to fail if they throw an error, otherwise pass.
+This is just the same as with custom attribute-specific validators.
+
+For example, you could ensure that either `latitude` and `longitude` are both set, or neither are.
+
+```ts
+class Place extends Model {
+ @Attribute(DataTypes.INTEGER)
+ declare latitude: number | null;
+
+ @Attribute(DataTypes.INTEGER)
+ declare longitude: number | null;
+
+ // highlight-start
+ @ModelValidator
+ validateCoords() {
+ if ((this.latitude === null) !== (this.longitude === null)) {
+ throw new Error('Either both latitude and longitude, or neither!');
+ }
+ }
+ // highlight-end
+}
+```
+
+If you need to validate an attribute based on the value of another attribute, we highly recommend using Model Validators instead of Attribute Validators,
+because Model Validators are always run, while Attribute Validators are only run if the attribute's value has changed.
+
+Model validator functions can also be static, in which case they will receive the instance to validate as the first argument:
+
+```ts
+class Place extends Model {
+ @Attribute(DataTypes.INTEGER)
+ declare latitude: number | null;
+
+ @Attribute(DataTypes.INTEGER)
+ declare longitude: number | null;
+
+ // highlight-start
+ @ModelValidator
+ static validateCoords(place: Place) {
+ if ((place.latitude === null) !== (place.longitude === null)) {
+ throw new Error('Either both latitude and longitude, or neither!');
+ }
+ }
+ // highlight-end
+}
+```
+
+:::caution
+
+As stated above, model validators are always run.
+
+If you did not load every attribute of a model, and you have a model validator that depends on an attribute that was not loaded,
+it will still run but the attribute will be `undefined`. Make sure your model validator can handle this case (for instance, by stopping validation if the attribute is `undefined`).
+
+:::
+
+### Asynchronous validators
+
+Both attribute and model validators can be asynchronous. To do so, simply return a promise from the validator function.
+
+This makes it possible to, for instance, load data from the database to validate the model. Be aware that _this can have serious impacts on your application's performance_.
+
+### Validation of nullable attributes
+
+The nullability validation takes precedence over the attribute validation. If the value of an attribute is null, its [__attribute validators__](#attribute-validators) are not executed.
+Only its nullability validation is run.
+
+On the other hand, [__model validators__](#model-validators) are always executed, even if the value of an attribute is null.
+This means that you can use model validators to implement custom nullability validation:
+
+```ts
+import { Model, DataTypes } from '@sequelize/core';
+import { Attribute, NotNull } from '@sequelize/core/decorators-legacy';
+import { IsEmail } from '@sequelize/validator.js';
+
+class User extends Model {
+ @Attribute(DataTypes.STRING)
+ declare name: string | null;
+
+ @Attribute(DataTypes.INTEGER)
+ declare age: number;
+
+ // highlight-start
+ @ModelValidator
+ onValidate() {
+ if (this.name === null && this.age !== 10) {
+ throw new Error("name can't be null unless age is 10");
+ }
+ }
+ // highlight-end
+}
+```
+
+[`Model`]: pathname:///api/v7/classes/_sequelize_core.index.Model.html
+[`@Table`]: pathname:///api/v7/functions/_sequelize_core.decorators_legacy.Table.html
+[`@Attribute`]: pathname:///api/v7/functions/_sequelize_core.decorators_legacy.Attribute.html
+[`@NotNull`]: pathname:///api/v7/functions/_sequelize_core.decorators_legacy.NotNull.html
+[`@Default`]: pathname:///api/v7/functions/_sequelize_core.decorators_legacy.Default.html
+[`@PrimaryKey`]: pathname:///api/v7/functions/_sequelize_core.decorators_legacy.PrimaryKey.html
+[`@AutoIncrement`]: pathname:///api/v7/functions/_sequelize_core.decorators_legacy.AutoIncrement.html
+[`importModels`]: pathname:///api/v7/functions/_sequelize_core.index.importModels.html
+[`import`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import
+[`require`]: https://nodejs.org/api/modules.html#requireid
+[`CreationOptional`]: pathname:///api/v7/types/_sequelize_core.index.CreationOptional.html
+[`fn`]: pathname:///api/v7/variables/_sequelize_core.index.sql.fn.html
+[`literal`]: pathname:///api/v7/variables/_sequelize_core.index.sql.literal.html
+[`Sequelize`]: pathname:///api/v7/classes/_sequelize_core.index.Sequelize.html
+[`ValidationError`]: pathname:///api/v7/classes/_sequelize_core.index.ValidationError.html
+[`QueryInterface#addConstraint`]: pathname:///api/v7/classes/_sequelize_core.index.AbstractQueryInterface.html#addConstraint
+[`Model#validate`]: pathname:///api/v7/classes/index.Model.html#validate
+[`@ValidateAttribute`]: pathname:///api/v7/functions/_sequelize_core.decorators_legacy.ValidateAttribute.html
diff --git a/docs/other-topics/_category_.json b/docs/other-topics/_category_.json
index ff794076..62b2ad10 100644
--- a/docs/other-topics/_category_.json
+++ b/docs/other-topics/_category_.json
@@ -1,5 +1,5 @@
{
- "position": 4,
+ "position": 7,
"label": "Other topics",
"collapsible": true,
"collapsed": false,
diff --git a/docs/other-topics/aws-lambda.md b/docs/other-topics/aws-lambda.md
index bfd62e8a..1d70fb79 100644
--- a/docs/other-topics/aws-lambda.md
+++ b/docs/other-topics/aws-lambda.md
@@ -23,7 +23,7 @@ const { Sequelize } = require('@sequelize/core');
let sequelize = null;
async function loadSequelize() {
- const sequelize = new Sequelize(/* (...) */, {
+ const sequelize = new Sequelize({
// (...)
pool: {
/*
@@ -441,9 +441,9 @@ Given the fact that AWS Lambda containers process one request at a time, one wou
configure `sequelize` as follows:
```js
-const { Sequelize } = require('@sequelize/core');
+import { Sequelize } from '@sequelize/core';
-const sequelize = new Sequelize(/* (...) */, {
+const sequelize = new Sequelize({
// (...)
pool: { min: 1, max: 1 }
});
@@ -457,20 +457,20 @@ presents a set of issues:
1. Lambdas that wait for the event loop to be empty will always time out. `sequelize` connection
pools schedule a `setTimeout` every
- [`options.pool.evict`](pathname:///api/v7/interfaces/PoolOptions.html#evict)
+ [`options.pool.evict`](pathname:///api/v7/interfaces/_sequelize_core.index.PoolOptions.html#evict)
ms until **all idle connections have been closed**. However, since `min` is set to `1`, there
will always be at least one idle connection in the pool, resulting in an infinite event loop.
1. Some operations like
- [`Model.findAndCountAll()`](pathname:///api/v7/classes/Model.html#findAndCountAll)
+ [`Model.findAndCountAll()`](pathname:///api/v7/classes/_sequelize_core.index.Model.html#findAndCountAll)
execute multiple queries asynchronously (e.g.
- [`Model.count()`](pathname:///api/v7/classes/Model.html#count) and
- [`Model.findAll()`](pathname:///api/v7/classes/Model.html#findAll)). Using a maximum of
+ [`Model.count()`](pathname:///api/v7/classes/_sequelize_core.index.Model.html#count) and
+ [`Model.findAll()`](pathname:///api/v7/classes/_sequelize_core.index.Model.html#findAll)). Using a maximum of
one connection forces the queries to be executed serially (rather than in parallel using two
connections). While this may be an acceptable performance compromise in order to
maintain a manageable number of database connections, long running queries may result in
- [`ConnectionAcquireTimeoutError`](pathname:///api/v7/classes/ConnectionAcquireTimeoutError.html)
+ [`ConnectionAcquireTimeoutError`](pathname:///api/v7/classes/_sequelize_core.index.ConnectionAcquireTimeoutError.html)
if a query takes more than the default or configured
- [`options.pool.acquire`](pathname:///api/v7/interfaces/PoolOptions.html#acquire)
+ [`options.pool.acquire`](pathname:///api/v7/interfaces/_sequelize_core.index.PoolOptions.html#acquire)
timeout to complete. This is because the serialized query will be stuck waiting on the pool until
the connection used by the other query is released.
1. If the AWS Lambda function times out (i.e. the configured AWS Lambda timeout is exceeded), the
@@ -568,7 +568,7 @@ new Runtime(client, handler, errorCallbacks).scheduleIteration();
```
All SQL queries invoked by a Lambda handler using `sequelize` are ultimately executed using
-[Sequelize.prototype.query()](pathname:///api/v7/classes/Sequelize.html#query).
+[Sequelize#query()](pathname:///api/v7/classes/_sequelize_core.index.Sequelize.html#query).
This method is responsible for obtaining a connection from the pool, executing the query, and
releasing the connection back to the pool when the query completes. The following snippet shows
a simplification of the method's logic for queries without transactions:
@@ -579,7 +579,7 @@ a simplification of the method's logic for queries without transactions:
class Sequelize {
// (...)
- query(sql, options) {
+ async query(sql, options) {
// (...)
const connection = await this.connectionManager.getConnection(options);
@@ -630,7 +630,7 @@ class ConnectionManager {
}
```
-The field `this.lib` refers to [`mysql2`](https://www.npmjs.com/package/mysql2) and the function
+The property `this.lib` refers to [`mysql2`](https://www.npmjs.com/package/mysql2) and the function
`createConnection()` creates a connection by creating an instance of a `Connection` class. The
relevant subset of this class is as follows:
diff --git a/docs/other-topics/connection-pool.md b/docs/other-topics/connection-pool.md
index 1b37d450..6dc94c10 100644
--- a/docs/other-topics/connection-pool.md
+++ b/docs/other-topics/connection-pool.md
@@ -2,10 +2,16 @@
title: Connection Pool
---
-If you're connecting to the database from a single process, you should create only one Sequelize instance. Sequelize will set up a connection pool on initialization. This connection pool can be configured through the constructor's `options` parameter (using `options.pool`), as is shown in the following example:
+Sequelize uses a connection pool,
+powered by [sequelize-pool](https://www.npmjs.com/package/sequelize-pool), to manage connections to the database.
+This provides better performance than creating a new connection for every query.
+
+## Pool Configuration
+
+This connection pool can be configured through the constructor's [`pool`](pathname:///api/v7/interfaces/_sequelize_core.index.PoolOptions.html) option:
```js
-const sequelize = new Sequelize(/* ... */, {
+const sequelize = new Sequelize({
// ...
pool: {
max: 5,
@@ -16,4 +22,75 @@ const sequelize = new Sequelize(/* ... */, {
});
```
-Learn more in the [API Reference for the Sequelize constructor](pathname:///api/v7/classes/Sequelize.html#constructor). If you're connecting to the database from multiple processes, you'll have to create one instance per process, but each instance should have a maximum connection pool size of such that the total maximum size is respected. For example, if you want a max connection pool size of 90 and you have three processes, the Sequelize instance of each process should have a max connection pool size of 30.
+By default, the pool has a maximum size of 5 active connections.
+Depending on your scale, you may need to adjust this value to avoid running out of connections by setting the `max` option.
+
+:::caution
+
+When increasing the connection pool size,
+keep in mind that your database server has a maximum number of allowed active connections.
+
+The `max` option should be set to a value that is less than the limit imposed by your database server.
+
+Keep in mind that the connection pool is _not shared_ between Sequelize instances.
+If your application uses multiple Sequelize instances, is running on multiple processes, or other applications are connecting
+to the same database, make sure to reserve enough connections for each instance.
+
+For instance, if your database server has a maximum of 100 connections, and your application is running on 2 processes,
+you should set the `max` option to 45; reserving 10 connections for other uses, such as database migrations, monitoring, etc.
+
+:::
+
+## Pool Monitoring
+
+Sequelize exposes a number of properties that can be used to monitor the state of the connection pool.
+
+You can access these properties via [`sequelize.connectionManager.pool.write`] and
+[`sequelize.connectionManager.pool.read`] (if you use [read replication](./read-replication.md)).
+
+These pools expose the following properties:
+
+- `size`: how many connections are currently in the pool (both in use and available)
+- `available`: how many connections are currently available for use in the pool
+- `using`: how many connections are currently in use in the pool
+- `waiting`: how many requests are currently waiting for a connection to become available
+
+You can also monitor how long it takes
+to acquire a connection from the pool
+by listening to the `beforePoolAcquire` and `afterPoolAcquire` [sequelize hooks](./hooks.mdx#instance-sequelize-hooks):
+
+```ts
+const acquireAttempts = new WeakMap();
+
+sequelize.hooks.addListener('beforePoolAcquire', options => {
+ acquireAttempts.set(options, Date.now());
+})
+
+sequelize.hooks.addListener('afterPoolAcquire', _connection, options => {
+ const elapsedTime = Date.now() - acquireAttempts.get(options);
+ console.log(`Connection acquired in ${elapsedTime}ms`);
+})
+```
+
+## `ConnectionAcquireTimeoutError`
+
+If you start seeing this error,
+it means that Sequelize was unable to acquire a connection from the pool within the configured `acquire` timeout.
+
+This can happen for a number of reasons, including:
+
+- Your server is doing too many concurrent requests, and the pool is unable to keep up. It may be necessary to increase the `max` option.
+- Some of your queries are taking too long to execute, and requests are piling up. Monitor your database server to see if there are any slow queries, and optimize them.
+- You have idle transactions that are not being committed or rolled back.
+ This can happen if you use [unmanaged transactions](../querying/transactions.md#unmanaged-transactions).
+ Make sure you are committing or rolling back your unmanaged transactions properly,
+ or use [managed transactions](../querying/transactions.md#managed-transactions-recommended) instead.
+ We also recommend monitoring for connections that have been idle in transaction for a long time.
+- You have other slow operations that are preventing your transactions from being committed in time.
+ For instance, if you are doing network requests inside a transaction,
+ and there is a network slowdown, your transaction is going to stay open for longer than usual,
+ and cause a cascade of issues.
+ To solve this, make sure to set [a timeout](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout_static) on relevant asynchronous operations.
+
+[`sequelize.connectionManager.pool.read`]: pathname:///api/v7/classes/_sequelize_core.index.unknown.ReplicationPool.html#read
+[`sequelize.connectionManager.pool.write`]: pathname:///api/v7/classes/_sequelize_core.index.unknown.ReplicationPool.html#write
diff --git a/docs/other-topics/constraints-and-circularities.md b/docs/other-topics/constraints-and-circularities.md
deleted file mode 100644
index 73d33947..00000000
--- a/docs/other-topics/constraints-and-circularities.md
+++ /dev/null
@@ -1,115 +0,0 @@
----
-title: Constraints & Circularities
----
-
-Adding constraints between tables means that tables must be created in the database in a certain order, when using `sequelize.sync`. If `Task` has a reference to `User`, the `User` table must be created before the `Task` table can be created. This can sometimes lead to circular references, where Sequelize cannot find an order in which to sync. Imagine a scenario of documents and versions. A document can have multiple versions, and for convenience, a document has a reference to its current version.
-
-```js
-const { Sequelize, Model, DataTypes } = require('@sequelize/core');
-
-class Document extends Model {}
-Document.init({
- author: DataTypes.STRING
-}, { sequelize, modelName: 'document' });
-
-class Version extends Model {}
-Version.init({
- timestamp: DataTypes.DATE
-}, { sequelize, modelName: 'version' });
-
-Document.hasMany(Version); // This adds documentId attribute to version
-Document.belongsTo(Version, {
- as: 'Current',
- foreignKey: 'currentVersionId'
-}); // This adds currentVersionId attribute to document
-```
-
-However, unfortunately the code above will result in the following error:
-
-```text
-Cyclic dependency found. documents is dependent of itself. Dependency chain: documents -> versions => documents
-```
-
-In order to alleviate that, we can pass `constraints: false` to one of the associations:
-
-```js
-Document.hasMany(Version);
-Document.belongsTo(Version, {
- as: 'Current',
- foreignKey: 'currentVersionId',
- constraints: false
-});
-```
-
-Which will allow us to sync the tables correctly:
-
-```sql
-CREATE TABLE IF NOT EXISTS "documents" (
- "id" SERIAL,
- "author" VARCHAR(255),
- "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL,
- "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
- "currentVersionId" INTEGER,
- PRIMARY KEY ("id")
-);
-
-CREATE TABLE IF NOT EXISTS "versions" (
- "id" SERIAL,
- "timestamp" TIMESTAMP WITH TIME ZONE,
- "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL,
- "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL,
- "documentId" INTEGER REFERENCES "documents" ("id") ON DELETE
- SET
- NULL ON UPDATE CASCADE,
- PRIMARY KEY ("id")
-);
-```
-
-## Enforcing a foreign key reference without constraints
-
-Sometimes you may want to reference another table, without adding any constraints, or associations. In that case you can manually add the reference attributes to your schema definition, and mark the relations between them.
-
-```js
-class Trainer extends Model {}
-Trainer.init({
- firstName: DataTypes.STRING,
- lastName: DataTypes.STRING
-}, { sequelize, modelName: 'trainer' });
-
-// Series will have a trainerId = Trainer.id foreign reference key
-// after we call Trainer.hasMany(series)
-class Series extends Model {}
-Series.init({
- title: DataTypes.STRING,
- subTitle: DataTypes.STRING,
- description: DataTypes.TEXT,
- // Set FK relationship (hasMany) with `Trainer`
- trainerId: {
- type: DataTypes.INTEGER,
- references: {
- model: Trainer,
- key: 'id'
- }
- }
-}, { sequelize, modelName: 'series' });
-
-// Video will have seriesId = Series.id foreign reference key
-// after we call Series.hasOne(Video)
-class Video extends Model {}
-Video.init({
- title: DataTypes.STRING,
- sequence: DataTypes.INTEGER,
- description: DataTypes.TEXT,
- // set relationship (hasOne) with `Series`
- seriesId: {
- type: DataTypes.INTEGER,
- references: {
- model: Series, // Can be both a string representing the table name or a Sequelize model
- key: 'id'
- }
- }
-}, { sequelize, modelName: 'video' });
-
-Series.hasOne(Video);
-Trainer.hasMany(Series);
-```
\ No newline at end of file
diff --git a/docs/other-topics/dialect-specific-things.md b/docs/other-topics/dialect-specific-things.md
index 861085af..bef3fbf2 100644
--- a/docs/other-topics/dialect-specific-things.md
+++ b/docs/other-topics/dialect-specific-things.md
@@ -1,292 +1,34 @@
---
-title: Dialect-Specific Things
+title: Dialect-Specific Features
---
-## Underlying Connector Libraries
+## Arrays of ENUMS
-### PostgreSQL
+:::info
-The underlying connector library used by Sequelize for PostgreSQL is the [pg](https://www.npmjs.com/package/pg) package.
-See [Releases](/releases#postgresql-support-table) to see which versions of PostgreSQL & pg are supported.
-
-You can provide custom options to it using the `dialectOptions` in the Sequelize constructor:
-
-```js
-const sequelize = new Sequelize('database', 'username', 'password', {
- dialect: 'postgres',
- dialectOptions: {
- // Your pg options here
- },
-});
-```
-
-The following fields may be passed to Postgres `dialectOptions`:
-
-- `application_name`: Name of application in pg_stat_activity. See the [Postgres docs](https://www.postgresql.org/docs/current/runtime-config-logging.html#GUC-APPLICATION-NAME) for details.
-- `ssl`: SSL options. See the [`pg` docs](https://node-postgres.com/features/ssl) for details.
-- `client_encoding`: // Setting 'auto' determines locale based on the client LC_CTYPE environment variable. See the [Postgres docs](https://www.postgresql.org/docs/current/multibyte.html) for details.
-- `keepAlive`: Boolean to enable TCP KeepAlive. See the [`pg` changelog](https://github.com/brianc/node-postgres/blob/master/CHANGELOG.md#v600) for details.
-- `statement_timeout`: Times out queries after a set time in milliseconds. Added in pg v7.3. See the [Postgres docs](https://www.postgresql.org/docs/current/runtime-config-client.html#GUC-STATEMENT-TIMEOUT) for details.
-- `idle_in_transaction_session_timeout`: Terminate any session with an open transaction that has been idle for longer than the specified duration in milliseconds. See the [Postgres docs](https://www.postgresql.org/docs/current/runtime-config-client.html#GUC-IDLE-IN-TRANSACTION-SESSION-TIMEOUT) for details.
-
-To connect over a unix domain socket, specify the path to the socket directory in the `host` option. The socket path must start with `/`.
-
-```js
-const sequelize = new Sequelize('database', 'username', 'password', {
- dialect: 'postgres',
- host: '/path/to/socket_directory',
-});
-```
-
-The default `client_min_messages` config in sequelize is `WARNING`.
-
-### Amazon Redshift
-
-:::caution
-
-While Redshift is based on PostgreSQL, it does not support the same set of features as PostgreSQL.
-Our PostgreSQL implementation is not integration tested against Redshift, and support is limited.
-
-:::
-
-Most of the configuration is same as PostgreSQL above.
-
-Redshift doesn't support `client_min_messages`, you must set it to `'ignore'`:
-
-```js
-const sequelize = new Sequelize('database', 'username', 'password', {
- dialect: 'postgres',
- dialectOptions: {
- // Your pg options here
- // ...
- clientMinMessages: 'ignore', // case insensitive
- },
-});
-```
-
-### MariaDB
-
-The underlying connector library used by Sequelize for MariaDB is the [mariadb](https://www.npmjs.com/package/mariadb) package.
-See [Releases](/releases#mariadb-support-table) to see which versions of MariaDB & mariadb (npm) are supported.
-
-You can provide custom options to it using the `dialectOptions` in the Sequelize constructor:
-
-```js
-const sequelize = new Sequelize('database', 'username', 'password', {
- dialect: 'mariadb',
- dialectOptions: {
- // Your mariadb options here
- // connectTimeout: 1000
- },
-});
-```
-
-`dialectOptions` are passed directly to the MariaDB connection constructor. A full list of options can be found in the [MariaDB docs](https://mariadb.com/kb/en/nodejs-connection-options/).
-
-### MySQL
-
-The underlying connector library used by Sequelize for MySQL is the [mysql2](https://www.npmjs.com/package/mysql2) package.
-See [Releases](/releases#mysql-support-table) to see which versions of MySQL & mysql2 are supported.
-
-You can provide custom options to it using the `dialectOptions` in the Sequelize constructor:
-
-```js
-const sequelize = new Sequelize('database', 'username', 'password', {
- dialect: 'mysql',
- dialectOptions: {
- // Your mysql2 options here
- },
-});
-```
-
-`dialectOptions` are passed directly to the MySQL connection constructor.
-A full list of options can be found in the [MySQL docs](https://www.npmjs.com/package/mysql#connection-options).
-
-### Microsoft SQL Server (mssql)
-
-The underlying connector library used by Sequelize for MSSQL is the [tedious](https://www.npmjs.com/package/tedious) package.
-See [Releases](/releases#microsoft-sql-server-mssql-support-table) to see which versions of SQL Server & tedious are supported.
-
-You can provide custom options to it using `dialectOptions.options` in the Sequelize constructor:
-
-```js
-const sequelize = new Sequelize('database', 'username', 'password', {
- dialect: 'mssql',
- dialectOptions: {
- // Observe the need for this nested `options` field for MSSQL
- options: {
- // Your tedious options here
- useUTC: false,
- dateFirst: 1,
- },
- },
-});
-```
-
-A full list of options can be found in the [tedious docs](https://tediousjs.github.io/tedious/api-connection.html#function_newConnection).
-
-#### MSSQL Domain Account
-
-In order to connect with a domain account, use the following format.
-
-```js
-const sequelize = new Sequelize('database', null, null, {
- dialect: 'mssql',
- dialectOptions: {
- authentication: {
- type: 'ntlm',
- options: {
- domain: 'yourDomain',
- userName: 'username',
- password: 'password',
- },
- },
- options: {
- instanceName: 'SQLEXPRESS',
- },
- },
-});
-```
-
-### SQLite
-
-The underlying connector library used by Sequelize for SQLite is the [sqlite3](https://www.npmjs.com/package/sqlite3) npm package.
-See [Releases](/releases#sqlite-support-table) to see which versions of sqlite3 are supported.
-
-You specify the storage file in the Sequelize constructor with the `storage` option (use `:memory:` for an in-memory SQLite instance).
-
-You can provide custom options to it using the `dialectOptions` in the Sequelize constructor:
-
-```js
-import { Sequelize } from 'sequelize';
-import SQLite from 'sqlite3';
-
-const sequelize = new Sequelize('database', 'username', 'password', {
- dialect: 'sqlite',
- storage: 'path/to/database.sqlite', // or ':memory:'
- dialectOptions: {
- // Your sqlite3 options here
- // for instance, this is how you can configure the database opening mode:
- mode: SQLite.OPEN_READWRITE | SQLite.OPEN_CREATE | SQLite.OPEN_FULLMUTEX,
- },
-});
-```
-
-The following fields may be passed to SQLite `dialectOptions`:
-
-- `mode`: Set the opening mode for the SQLite connection. Potential values are provided by the `sqlite3` package,
- and can include `SQLite.OPEN_READONLY`, `SQLite.OPEN_READWRITE`, or `SQLite.OPEN_CREATE`.
- See [sqlite3's API reference](https://github.com/TryGhost/node-sqlite3/wiki/API) and the [SQLite C interface documentation](https://www.sqlite.org/c3ref/open.html) for more details.
-
-### Snowflake
-
-:::note
-
-While this dialect is included in Sequelize,
-support for Snowflake is limited as it is not handled by the core team.
+This feature is only available in PostgreSQL
:::
-The underlying connector library used by Sequelize for Snowflake is the [snowflake-sdk](https://www.npmjs.com/package/snowflake-sdk) package.
-See [Releases](/releases#snowflake-support-table) to see which versions of Snowflake and snowflake-sdk are supported.
-
-In order to connect with an account, use the following format:
+Array(Enum) type requires special treatment. Whenever Sequelize will talk to the database, it has to typecast array values with ENUM name.
-```js
-const sequelize = new Sequelize('database', null, null, {
- dialect: 'snowflake',
- dialectOptions: {
- // put your snowflake account here,
- account: 'myAccount', // my-app.us-east-1
-
- // below option should be optional
- role: 'myRole',
- warehouse: 'myWarehouse',
- schema: 'mySchema',
- },
- // same as other dialect
- username: 'myUserName',
- password: 'myPassword',
- database: 'myDatabaseName',
-});
-```
-
-**NOTE** There is no test sandbox provided so the snowflake integration test is not part of the pipeline. Also it is difficult for core team to triage and debug. This dialect needs to be maintained by the snowflake user/community for now.
-
-For running integration test:
-
-```bash
-# using npm
-SEQ_ACCOUNT=myAccount SEQ_USER=myUser SEQ_PW=myPassword SEQ_ROLE=myRole SEQ_DB=myDatabaseName SEQ_SCHEMA=mySchema SEQ_WH=myWareHouse npm run test-integration-snowflake
-# using yarn
-SEQ_ACCOUNT=myAccount SEQ_USER=myUser SEQ_PW=myPassword SEQ_ROLE=myRole SEQ_DB=myDatabaseName SEQ_SCHEMA=mySchema SEQ_WH=myWareHouse yarn test-integration-snowflake
-```
-
-### Db2
-
-:::note
-
-While this dialect is included in Sequelize,
-support for Db2 is limited as it is not handled by the core team.
-
-:::
-
-
-The underlying connector library used by Sequelize for Db2 is the [ibm_db](https://www.npmjs.com/package/ibm_db) npm package.
-See [Releases](/releases#db2-support-table) to see which versions of DB2 and ibm_db are supported.
+So this enum name must follow this pattern `enum__`. If you are using `sync` then correct name will automatically be generated.
-### Db2 for IBM i
+## Table Hints
-:::note
+:::info
-While this dialect is included in Sequelize,
-support for *Db2 for IBM i* is limited as it is not handled by the core team.
+This feature is only available in MS SQL Server
:::
-The underlying connector library used by Sequelize for *Db2 for IBM i* is the [odbc](https://www.npmjs.com/package/odbc) npm package.
-See [Releases](/releases#db2-for-ibm-i-support-table) to see which versions of IBMi and odbc are supported.
-
-To learn more about using ODBC with IBM i, consult the [IBM i and ODBC documentation](https://ibmi-oss-docs.readthedocs.io/en/latest/odbc/README.html).
-
-When passing options to the constructor, the concept of `database` is mapped to the ODBC `DSN`. You can provide additional connection string options to Sequelize using the `dialectOptions.odbcConnectionString`. This connection string is then appended with the values found in the `database`, `username`, and `password` parameters:
-
-```js
-const sequelize = new Sequelize('MY_DSN', 'username', 'password', {
- dialect: 'ibmi',
- dialectOptions: {
- odbcConnectionString: 'CMT=1;NAM=0;...'
- },
-});
-```
-
-The final connection string generated by the above configuration would look like `CMT=1;NAMING=0;...;DSN=MY_DSN;UID=username;PWD=password;`. Additionally, the `host` option will map the the `SYSTEM=` connection string key.
-
-## Data type: TIMESTAMP WITHOUT TIME ZONE - PostgreSQL only
-
-If you are working with the PostgreSQL `TIMESTAMP WITHOUT TIME ZONE` and you need to parse it to a different timezone, please use the pg library's own parser:
-
-```js
-require('pg').types.setTypeParser(1114, stringValue => {
- return new Date(stringValue + '+0000');
- // e.g., UTC offset. Use any offset that you would like.
-});
-```
-
-## Data type: ARRAY(ENUM) - PostgreSQL only
-
-Array(Enum) type requireS special treatment. Whenever Sequelize will talk to the database, it has to typecast array values with ENUM name.
-
-So this enum name must follow this pattern `enum__`. If you are using `sync` then correct name will automatically be generated.
-
-## Table Hints - MSSQL only
-
The `tableHint` option can be used to define a table hint. The hint must be a value from `TableHints` and should only be used when absolutely necessary. Only a single table hint is currently supported per query.
Table hints override the default behavior of MSSQL query optimizer by specifying certain options. They only affect the table or view referenced in that clause.
```js
-const { TableHints } = require('@sequelize/core');
+import { TableHints } from '@sequelize/core';
+
Project.findAll({
// adding the table hint NOLOCK
tableHint: TableHints.NOLOCK,
@@ -294,14 +36,20 @@ Project.findAll({
});
```
-## Index Hints - MySQL/MariaDB only
+## Index Hints
+
+:::info
+
+This feature is only available in MySQL & MariaDB
+
+:::
The `indexHints` option can be used to define index hints. The hint type must be a value from `IndexHints` and the values should reference existing indexes.
-Index hints [override the default behavior of the MySQL query optimizer](https://dev.mysql.com/doc/refman/5.7/en/index-hints.html).
+Index hints [override the default behavior of the MySQL query optimizer](https://dev.mysql.com/doc/refman/8.0/en/index-hints.html).
```js
-const { IndexHints } = require('@sequelize/core');
+import { IndexHints } from '@sequelize/core';
Project.findAll({
indexHints: [
{ type: IndexHints.USE, values: ['index_project_on_name'] },
@@ -323,40 +71,6 @@ The above will generate a MySQL query that looks like this:
SELECT * FROM Project USE INDEX (index_project_on_name) WHERE name LIKE 'FOO %' AND id > 623;
```
-`Sequelize.IndexHints` includes `USE`, `FORCE`, and `IGNORE`.
+`IndexHints` includes `USE`, `FORCE`, and `IGNORE`.
See [Issue #9421](https://github.com/sequelize/sequelize/issues/9421) for the original API proposal.
-
-## Engines - MySQL/MariaDB only
-
-The default engine for a model is InnoDB.
-
-You can change the engine for a model with the `engine` option (e.g., to MyISAM):
-
-```js
-const Person = sequelize.define('person', { /* attributes */ }, {
- engine: 'MYISAM',
-});
-```
-
-Like every option for the definition of a model, this setting can also be changed globally with the `define` option of the Sequelize constructor:
-
-```js
-const sequelize = new Sequelize(db, user, pw, {
- define: { engine: 'MYISAM' },
-});
-```
-
-## Table comments - MySQL/MariaDB/PostgreSQL only
-
-You can specify a comment for a table when defining the model:
-
-```js
-class Person extends Model {}
-Person.init({ /* attributes */ }, {
- comment: "I'm a table comment!",
- sequelize,
-});
-```
-
-The comment will be set when calling `sync()`.
diff --git a/docs/other-topics/extending-data-types.md b/docs/other-topics/extending-data-types.md
index 4d7cf3c6..9c5af359 100644
--- a/docs/other-topics/extending-data-types.md
+++ b/docs/other-topics/extending-data-types.md
@@ -3,7 +3,7 @@ sidebar_position: 3
title: Custom Data Types
---
-Most likely the type you are trying to implement is already included in our built-in [DataTypes](./other-data-types.mdx).
+Most likely the type you are trying to implement is already included in our built-in [DataTypes](../models/data-types.mdx).
If the data type you need is not included, this manual will show how to write it yourself, or extend an existing one.
## Creating a new Data Type
@@ -27,7 +27,7 @@ You can then use your new data type in your models:
```typescript
import { MyDateType } from './custom-types.js';
-const sequelize = new Sequelize('sqlite::memory:');
+const sequelize = new Sequelize(/* options */);
const User = sequelize.define('User', {
birthday: {
@@ -50,7 +50,7 @@ CREATE TABLE IF NOT EXISTS "users" (
### Validating user inputs
Right now, our Data Type is very simple. It doesn't do any normalization, and passes values as-is to the database.
-It has the same base behavior as if we set our [attribute's type to a string](./other-data-types.mdx#custom-data-types).
+It has the same base behavior as if we set our [attribute's type to a string](../models/data-types.mdx#custom-data-types).
You can implement a series of methods to change the behavior of your data type:
@@ -102,8 +102,8 @@ export class MyDateType extends DataTypes.ABSTRACT {
We also have 4 methods that can be implemented to define how the Data Type serializes & deserializes values when interacting with the database:
- `parseDatabaseValue(value): unknown`: Transforms values retrieved from the database[^caveat-1].
-- `toBindableValue(value): unknown`: Transforms a value into a value accepted by the connector library when using [bind parameters](../core-concepts/raw-queries.md#bind-parameters).
-- `escape(value): string`: Escapes a value for inlining inside of raw SQL, such as when using [replacements](../core-concepts/raw-queries.md#replacements).
+- `toBindableValue(value): unknown`: Transforms a value into a value accepted by the connector library when using [bind parameters](../querying/raw-queries.mdx#bind-parameters).
+- `escape(value): string`: Escapes a value for inlining inside of raw SQL, such as when using [replacements](../querying/raw-queries.mdx#replacements).
By default, if `toBindableValue` returns a string, this method will escape that string as a SQL string.
```typescript
@@ -150,7 +150,7 @@ Just like with custom data types, use your Data Type class instead of the type y
```typescript
import { MyStringType } from './custom-types.js';
-const sequelize = new Sequelize('sqlite::memory:');
+const sequelize = new Sequelize(/* options */);
const User = sequelize.define('User', {
firstName: {
@@ -171,4 +171,4 @@ When using `DataTypes.ENUM`, Sequelize will automatically create the enum type i
If you need to create a custom type, you will need to create it manually in the database before you can use it in one of your models.
[^caveat-1]: `parseDatabaseValue` is only called if a Sequelize Data Type is specified in the query.
-This is the case when using model methods, but not when using [raw queries](../core-concepts/raw-queries.md) or when not specifying the model in [`QueryInterface`](pathname:///api/v7/classes/QueryInterface.html) methods
+This is the case when using model methods, but not when using [raw queries](../querying/raw-queries.mdx) or when not specifying the model in [`QueryInterface`](pathname:///api/v7/classes/_sequelize_core.index.AbstractQueryInterface.html) methods
diff --git a/docs/other-topics/faq.md b/docs/other-topics/faq.md
new file mode 100644
index 00000000..072b6538
--- /dev/null
+++ b/docs/other-topics/faq.md
@@ -0,0 +1,52 @@
+---
+title: FAQ & Troubleshooting
+---
+
+## Working with an existing database
+
+Sequelize works best when it handles the creation of your database tables for you.
+Many options are automatically configured for you to ease the development process.
+
+Sometimes, however, you may need to work with an existing database,
+and Sequelize must adapt to you, not the other way around.
+To do this, you will need to let Sequelize know about your existing tables and how they are structured.
+
+The following pages will teach you how to do this:
+
+- [Naming your tables](../models/naming-strategies.mdx#manually-setting-the-table-name)
+- [Naming your columns](../models/naming-strategies.mdx#manually-setting-the-column-name)
+- [Removing the primary key](../models/advanced.mdx#prevent-creating-a-default-pk-attribute)
+
+## Error: Can't create more than `max_prepared_stmt_count statements` (MySQL)
+
+If you're using MySQL and encounter this error,
+it means that Sequelize is creating more prepared statements than your MySQL server allows.
+
+To fix this, you need to configure the `maxPreparedStatements` option in your Sequelize constructor
+to a value that will not cause Sequelize to create more statements than the `max_prepared_stmt_count` value of your MySQL server.
+
+```ts
+const sequelize = new Sequelize({
+ // ...
+ dialectOptions: {
+ maxPreparedStatements: 100,
+ },
+});
+```
+
+The `max_prepared_stmt_count` limit is a database-wide limit,
+and prepared statements are created per-connection.
+This means that the `maxPreparedStatements` option should be set to a value
+that is less than the `max_prepared_stmt_count` value divided by the number of concurrent connections to your database.
+
+To figure out a good value for this option, you can do the following:
+
+1. Determine the value of `max_prepared_stmt_count` on your MySQL server:
+ ```sql
+ SHOW VARIABLES LIKE 'max_prepared_stmt_count';
+ ```
+2. Determine how many concurrent connections can be connected to your database at the same time.
+ ```sql
+ SHOW VARIABLES LIKE 'max_connections';
+ ```
+3. Set `maxPreparedStatements` to the result of `FLOOR(max_prepared_stmt_count / max_connections)`.
diff --git a/docs/other-topics/hooks.md b/docs/other-topics/hooks.mdx
similarity index 95%
rename from docs/other-topics/hooks.md
rename to docs/other-topics/hooks.mdx
index f33fc678..ec5253f3 100644
--- a/docs/other-topics/hooks.md
+++ b/docs/other-topics/hooks.mdx
@@ -2,6 +2,8 @@
title: Hooks
---
+import DecoratorInfo from '../_fragments/_decorator-info.mdx';
+
Hooks are events that you can listen to, and which are triggerred when their corresponding method is called.
They are useful for adding custom functionality to the core of the application.
@@ -36,6 +38,7 @@ Sequelize.hooks.addListener('beforeInit', () => {
| `beforeBulkSync`, `afterBulkSync` | ✅ | `sequelize.sync` is called |
| `beforeConnect`, `afterConnect` | ✅ | Whenever a new connection to the database is being created |
| `beforeDisconnect`, `afterDisconnect` | ✅ | Whenever a connection to the database is being closed |
+| `beforePoolAcquire`, `afterPoolAcquire` | ✅ | Whenever a new connection from pool is being acquired |
:::info
@@ -61,9 +64,9 @@ Instance Sequelize hooks can be registered in two ways:
1. Using the `hooks` property of the `Sequelize` instance:
```typescript
import { Sequelize } from '@sequelize/core';
-
+
const sequelize = new Sequelize(/* options */);
-
+
// highlight-next-line
sequelize.hooks.addListener('beforeDefine', () => {
console.log('A new Model is being initialized');
@@ -72,8 +75,9 @@ Instance Sequelize hooks can be registered in two ways:
2. Through the `Sequelize` options:
```typescript
import { Sequelize } from '@sequelize/core';
-
+
const sequelize = new Sequelize({
+ /* options */
// highlight-next-line
hooks: {
beforeDefine: () => {
@@ -112,13 +116,13 @@ Instance Sequelize hooks can be registered in three ways:
1. Using the `hooks` property of the `Sequelize` instance:
```typescript
import { Sequelize, DataTypes } from '@sequelize/core';
-
+
const sequelize = new Sequelize(/* options */);
-
+
const MyModel = sequelize.define('MyModel', {
name: DataTypes.STRING,
});
-
+
// highlight-next-line
MyModel.hooks.addListener('beforeFind', () => {
console.log('findAll has been called on MyModel');
@@ -139,11 +143,15 @@ Instance Sequelize hooks can be registered in three ways:
},
});
```
-3. Using decorators
+3. Using decorators.
+ There is a decorator for every model hook, their names are the same as the hook names, but in `PascalCase`.
+
+
+
```typescript
import { Sequelize, Model, Hook } from '@sequelize/core';
import { BeforeFind } from '@sequelize/core/decorators-legacy';
-
+
export class MyModel extends Model {
// highlight-next-line
@BeforeFind
@@ -216,7 +224,7 @@ However, we do not recommend removing hooks added through decorators, as it may
## Associations Method Hooks
-Methods added by [Associations](../core-concepts/assocs.md) on your model do provide hooks specific to them, but they are built on top
+Methods added by [Associations](../associations/basics.md) on your model do provide hooks specific to them, but they are built on top
of regular model methods, which will trigger hooks.
For instance, using the `add` / `set` mixin methods will trigger the `beforeUpdate` and `afterUpdate` hooks.
@@ -255,7 +263,7 @@ These include but are not limited to:
- Instances being deleted by the database because of an `ON DELETE CASCADE` constraint, [except if the `hooks` option is true](#hooks-for-cascade-deletes).
- Instances being updated by the database because of a `SET NULL` or `SET DEFAULT` constraint.
-- [Raw queries](../core-concepts/raw-queries.md).
+- [Raw queries](../querying/raw-queries.mdx).
- All QueryInterface methods.
If you need to react to these events, consider using your database's native and notification system instead.
@@ -303,7 +311,7 @@ class Post extends Model {
const sequelize = new Sequelize({
/* options */
- models: [User, Post],
+ models: [User, Post],
});
await sequelize.sync({ force: true });
@@ -344,8 +352,8 @@ await sequelize.transaction(async transaction => {
});
```
-If we had not included the transaction option in our call to `User.update` in the preceding code,
-no change would have occurred, since our newly created user does not exist in the database until the pending transaction
+If we had not included the transaction option in our call to `User.update` in the preceding code,
+no change would have occurred, since our newly created user does not exist in the database until the pending transaction
has been committed.
[^find-all]: **findAll**: Note that some methods, such as `Model.findOne`, `Model.findAndCountAll` and association getters will also call `Model.findAll` internally. This will cause the `beforeFind` hook to be called for these methods too.
diff --git a/docs/other-topics/indexes.md b/docs/other-topics/indexes.md
deleted file mode 100644
index 29e01a7d..00000000
--- a/docs/other-topics/indexes.md
+++ /dev/null
@@ -1,49 +0,0 @@
----
-title: Indexes
----
-
-Sequelize supports adding indexes to the model definition which will be created on [`sequelize.sync()`](pathname:///api/v7/classes/Sequelize.html#sync).
-
-```js
-const User = sequelize.define('User', { /* attributes */ }, {
- indexes: [
- // Create a unique index on email
- {
- unique: true,
- fields: ['email']
- },
-
- // Creates a gin index on data with the jsonb_path_ops operator
- {
- fields: ['data'],
- using: 'gin',
- operator: 'jsonb_path_ops'
- },
-
- // By default index name will be [table]_[fields]
- // Creates a multi column partial index
- {
- name: 'public_by_author',
- fields: ['author', 'status'],
- where: {
- status: 'public'
- }
- },
-
- // A BTREE index with an ordered field
- {
- name: 'title_index',
- using: 'BTREE',
- fields: [
- 'author',
- {
- name: 'title',
- collate: 'en_US',
- order: 'DESC',
- length: 5
- }
- ]
- }
- ]
-});
-```
diff --git a/docs/other-topics/legacy-model-definitions.mdx b/docs/other-topics/legacy-model-definitions.mdx
new file mode 100644
index 00000000..61b29382
--- /dev/null
+++ b/docs/other-topics/legacy-model-definitions.mdx
@@ -0,0 +1,274 @@
+---
+title: Legacy Model Definitions
+---
+
+import CodeBlock from '@theme/CodeBlock';
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+# Legacy Model Definitions
+
+:::caution
+
+This API is considered legacy. It is still supported, but we strongly recommend to use the [new way of defining models](../models/defining-models.mdx),
+as this legacy API can easily make your models complex if you need to declare an association.
+
+:::
+
+There are two legacy ways of defining models:
+
+* Extending [Model](pathname:///api/v7/classes/_sequelize_core.index.Model.html), and calling its [`init(attributes, options)`](pathname:///api/v7/classes/_sequelize_core.index.Model.html#init) static method
+* Calling [`sequelize.define(modelName, attributes, options)`](pathname:///api/v7/classes/_sequelize_core.index.Sequelize.html#define)
+
+## Extending [Model](pathname:///api/v7/classes/_sequelize_core.index.Model.html)
+
+
+
+
+```typescript
+import { Sequelize, DataTypes, Model, InferAttributes, InferCreationAttributes, CreationOptional } from '@sequelize/core';
+import { SqliteDialect } from '@sequelize/sqlite3';
+
+const sequelize = new Sequelize({ dialect: SqliteDialect });
+
+class User extends Model, InferCreationAttributes> {
+ declare id: CreationOptional;
+ declare firstName: string;
+ declare lastName: string | null;
+}
+
+User.init({
+ // Model attributes are defined here
+ id: {
+ type: DataTypes.INTEGER,
+ autoIncrement: true,
+ primaryKey: true,
+ },
+ firstName: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ },
+ lastName: {
+ type: DataTypes.STRING,
+ // allowNull defaults to true, except for primary keys
+ },
+}, {
+ // Other model options go here
+ sequelize, // We need to pass the connection instance
+});
+
+// Once init has been called, the model also becomes available through sequelize.models
+console.log(User === sequelize.models.User); // true
+```
+
+
+
+
+```javascript
+import { Sequelize, DataTypes, Model } from '@sequelize/core';
+import { SqliteDialect } from '@sequelize/sqlite3';
+
+const sequelize = new Sequelize({ dialect: SqliteDialect });
+
+class User extends Model {}
+
+User.init({
+ // Model attributes are defined here
+ firstName: {
+ type: DataTypes.STRING,
+ allowNull: false,
+ },
+ lastName: {
+ type: DataTypes.STRING,
+ // allowNull defaults to true, except for primary keys
+ },
+}, {
+ // Other model options go here
+ sequelize, // We need to pass the connection instance
+});
+
+// Once init has been called, the model also becomes available through sequelize.models
+console.log(User === sequelize.models.User); // true
+```
+
+
+
+
+## Using [`sequelize.define`](pathname:///api/v7/classes/_sequelize_core.index.Sequelize.html#define)
+
+Internally, `sequelize.define` calls `Model.init`, so both approaches are equivalent.
+
+```js
+import { Sequelize, DataTypes } from '@sequelize/core';
+
+const sequelize = new Sequelize({ dialect: SqliteDialect });
+
+const User = sequelize.define('User', {
+ // Model attributes are defined here
+ firstName: {
+ type: DataTypes.STRING,
+ allowNull: false
+ },
+ lastName: {
+ type: DataTypes.STRING
+ // allowNull defaults to true, except for primary keys
+ }
+}, {
+ // Other model options go here
+});
+
+// The model is also available through sequelize.models
+console.log(User === sequelize.models.User); // true
+```
+
+## With TypeScript
+
+### The case of `Model.init`
+
+`Model.init` requires an attribute configuration for each attribute declared in typings.
+
+Some attribute definitions don't actually need to be provided to `Model.init`, this is how you can make `Model.init` aware of this:
+
+- Methods used to define associations (`Model.belongsTo`, `Model.hasMany`, etc…) already handle
+ the configuration of the necessary foreign keys attributes.
+ Use the `ForeignKey<>` branded type to make `Model.init` aware of this:
+
+ ```typescript
+ import { Model, InferAttributes, InferCreationAttributes, DataTypes, ForeignKey } from '@sequelize/core';
+
+ class Project extends Model, InferCreationAttributes> {
+ id: number;
+ userId: ForeignKey;
+ }
+
+ // this configures the `userId` attribute.
+ Project.belongsTo(User);
+
+ // therefore, `userId` doesn't need to be specified here.
+ Project.init({
+ id: {
+ type: DataTypes.INTEGER,
+ primaryKey: true,
+ autoIncrement: true,
+ },
+ }, { sequelize });
+ ```
+
+- Timestamp attributes managed by Sequelize (by default, `createdAt`, `updatedAt`, and `deletedAt`) don't need to be configured using `Model.init`,
+ unfortunately `Model.init` has no way of knowing this. We recommend you use the minimum necessary configuration to suppress this type error:
+
+ ```typescript
+ import { Model, InferAttributes, InferCreationAttributes, DataTypes } from '@sequelize/core';
+
+ class User extends Model, InferCreationAttributes> {
+ id: number;
+ createdAt: Date;
+ updatedAt: Date;
+ }
+
+ User.init({
+ id: {
+ type: DataTypes.INTEGER,
+ primaryKey: true,
+ autoIncrement: true,
+ },
+ // technically, `createdAt` & `updatedAt` are added by Sequelize and don't need to be configured in Model.init
+ // but the typings of Model.init do not know this. Add the following to mute the typing error:
+ createdAt: DataTypes.DATE,
+ updatedAt: DataTypes.DATE,
+ }, { sequelize });
+ ```
+
+### Usage without strict types for attributes
+
+The typings for Sequelize v5 allowed you to define models without specifying types for the attributes.
+This is still possible for backwards compatibility and for cases where you feel strict typing for attributes isn't worth it.
+
+import modelInitNoAttributesExample from '!!raw-loader!@site/.sequelize/v7/packages/core/test/types/typescript-docs/model-init-no-attributes.ts';
+
+
+ {modelInitNoAttributesExample}
+
+
+### Usage of `Sequelize#define`
+
+In Sequelize versions before v5, the default way of defining a model involved using [`Sequelize#define`](pathname:///api/v7/classes/_sequelize_core.index.Sequelize.html#define).
+It's still possible to define models with that, and you can also add typings to these models using interfaces.
+
+import defineExample from '!!raw-loader!@site/.sequelize/v7/packages/core/test/types/typescript-docs/define.ts';
+
+
+ {defineExample}
+
+
+## Column Options
+
+When defining a column, apart from specifying the `type` of the column, and the `allowNull` and `defaultValue` options mentioned above,
+you can use many more options, for example:
+
+```js
+import { Model, DataTypes, Deferrable } from '@sequelize/core';
+
+class Foo extends Model {}
+Foo.init({
+ // instantiating will automatically set the flag to true if not set
+ flag: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: true },
+
+ // default values for dates => current time
+ myDate: { type: DataTypes.DATE, defaultValue: DataTypes.NOW },
+
+ // setting allowNull to false will add NOT NULL to the column, which means an error will be
+ // thrown from the DB when the query is executed if the column is null. If you want to check that a value
+ // is not null before querying the DB, look at the validations section below.
+ title: { type: DataTypes.STRING, allowNull: false },
+
+ // Creating two objects with the same value will throw an error. The unique property can be either a
+ // boolean, or a string. If you provide the same string for multiple columns, they will form a
+ // composite unique key.
+ uniqueOne: { type: DataTypes.STRING, unique: 'compositeIndex' },
+ uniqueTwo: { type: DataTypes.INTEGER, unique: 'compositeIndex' },
+
+ // The unique property is simply a shorthand to create a unique constraint.
+ someUnique: { type: DataTypes.STRING, unique: true },
+
+ // Go on reading for further information about primary keys
+ identifier: { type: DataTypes.STRING, primaryKey: true },
+
+ // autoIncrement can be used to create auto_incrementing integer columns
+ incrementMe: { type: DataTypes.INTEGER, autoIncrement: true },
+
+ // You can specify a custom column name via the 'columnName' attribute:
+ fieldWithUnderscores: { type: DataTypes.STRING, columnName: 'field_with_underscores' },
+
+ // It is possible to create foreign keys:
+ bar_id: {
+ type: DataTypes.INTEGER,
+
+ references: {
+ // This is a reference to another model
+ model: Bar,
+
+ // This is the column name of the referenced model
+ key: 'id',
+
+ // With PostgreSQL, it is optionally possible to declare when to check the foreign key constraint, passing the Deferrable type.
+ deferrable: Deferrable.INITIALLY_IMMEDIATE
+ // Options:
+ // - `Deferrable.INITIALLY_IMMEDIATE` - Immediately check the foreign key constraints
+ // - `Deferrable.INITIALLY_DEFERRED` - Defer all foreign key constraint check to the end of a transaction
+ // - `Deferrable.NOT` - Don't defer the checks at all (default) - This won't allow you to dynamically change the rule in a transaction
+ }
+ },
+
+ // Comments can only be added to columns in MySQL, MariaDB, PostgreSQL and MSSQL
+ commentMe: {
+ type: DataTypes.INTEGER,
+ comment: 'This is a column name that has a comment'
+ }
+}, {
+ sequelize,
+
+ // Using `unique: true` in an attribute above is exactly the same as creating the index in the model's options:
+ indexes: [{ unique: true, fields: ['someUnique'] }]
+});
+```
diff --git a/docs/other-topics/legacy.md b/docs/other-topics/legacy.md
deleted file mode 100644
index 2b787397..00000000
--- a/docs/other-topics/legacy.md
+++ /dev/null
@@ -1,91 +0,0 @@
----
-title: Working with Legacy Tables
----
-
-While out of the box Sequelize will seem a bit opinionated it's easy to work legacy tables and forward proof your application by defining (otherwise generated) table and field names.
-
-## Tables
-
-```js
-class User extends Model {}
-User.init({
- // ...
-}, {
- modelName: 'user',
- tableName: 'users',
- sequelize,
-});
-```
-
-## Fields
-
-```js
-class MyModel extends Model {}
-MyModel.init({
- userId: {
- type: DataTypes.INTEGER,
- field: 'user_id'
- }
-}, { sequelize });
-```
-
-## Primary keys
-
-Sequelize will assume your table has a `id` primary key property by default.
-
-To define your own primary key:
-
-```js
-class Collection extends Model {}
-Collection.init({
- uid: {
- type: DataTypes.INTEGER,
- primaryKey: true,
- autoIncrement: true // Automatically gets converted to SERIAL for postgres
- }
-}, { sequelize });
-
-class Collection extends Model {}
-Collection.init({
- uuid: {
- type: DataTypes.UUID,
- primaryKey: true
- }
-}, { sequelize });
-```
-
-And if your model has no primary key at all you can use `Model.removeAttribute('id');`
-
-Instances without primary keys can still be retrieved using `Model.findOne` and `Model.findAll`.
-While it's currently possible to use their instance methods (`instance.save`, `instance.update`, etc…), doing this will lead to subtle bugs,
-and is planned for removal in a future update.
-
-:::info
-
-If your model has no primary keys, you need to use the static equivalent of the following instance methods, and provide your own `where` parameter:
-
-- `instance.save`: `Model.update`
-- `instance.update`: `Model.update`
-- `instance.reload`: `Model.findOne`
-- `instance.destroy`: `Model.destroy`
-- `instance.restore`: `Model.restore`
-- `instance.decrement`: `Model.decrement`
-- `instance.increment`: `Model.increment`
-
-:::
-
-## Foreign keys
-
-```js
-// 1:1
-Organization.belongsTo(User, { foreignKey: 'owner_id' });
-User.hasOne(Organization, { foreignKey: 'owner_id' });
-
-// 1:M
-Project.hasMany(Task, { foreignKey: 'tasks_pk' });
-Task.belongsTo(Project, { foreignKey: 'tasks_pk' });
-
-// N:M
-User.belongsToMany(Role, { through: 'user_has_roles', foreignKey: 'user_role_user_id' });
-Role.belongsToMany(User, { through: 'user_has_roles', foreignKey: 'roles_identifier' });
-```
diff --git a/docs/other-topics/naming-strategies.md b/docs/other-topics/naming-strategies.md
deleted file mode 100644
index d0e797b8..00000000
--- a/docs/other-topics/naming-strategies.md
+++ /dev/null
@@ -1,162 +0,0 @@
----
-title: Naming Strategies
----
-
-## The `underscored` option
-
-Sequelize provides the `underscored` option for a model. When `true`, this option will set the `field` option on all attributes to the [snake_case](https://en.wikipedia.org/wiki/Snake_case) version of its name (unless manually set). This also applies to table names and foreign keys automatically generated by associations and other automatically generated fields. Example:
-
-
-```js
-const User = sequelize.define('User', { username: DataTypes.STRING }, {
- underscored: true
-});
-const Task = sequelize.define('Task', { title: DataTypes.STRING }, {
- underscored: true
-});
-User.hasMany(Task);
-Task.belongsTo(User);
-```
-
-Above we have the models User and Task, both using the `underscored` option. We also have a One-to-Many relationship between them. Also, recall that since `timestamps` is true by default, we should expect the `createdAt` and `updatedAt` fields to be automatically created as well.
-
-Without the `underscored` option, Sequelize would automatically define:
-
-* A `Users` table for the User model and a `Tasks` table for the Task model.
-* A `createdAt` attribute for each model, pointing to a column named `createdAt` in each table
-* An `updatedAt` attribute for each model, pointing to a column named `updatedAt` in each table
-* A `userId` attribute in the `Task` model, pointing to a column named `userId` in the task table
-
-With the `underscored` option enabled, Sequelize will instead define:
-
-* A `users` table for the User model and a `tasks` table for the Task model.
-* A `createdAt` attribute for each model, pointing to a column named `created_at` in each table
-* An `updatedAt` attribute for each model, pointing to a column named `updated_at` in each table
-* A `userId` attribute in the `Task` model, pointing to a column named `user_id` in the task table
-
-Note that in both cases the fields are still [camelCase](https://en.wikipedia.org/wiki/Camel_case) in the JavaScript side; this option only changes how these fields are mapped to the database itself. The `field` option of every attribute is set to their snake_case version, but the attribute itself remains camelCase.
-
-This way, calling `sync()` on the above code will generate the following:
-
-```sql
-CREATE TABLE IF NOT EXISTS "users" (
- "id" SERIAL,
- "username" VARCHAR(255),
- "created_at" TIMESTAMP WITH TIME ZONE NOT NULL,
- "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL,
- PRIMARY KEY ("id")
-);
-CREATE TABLE IF NOT EXISTS "tasks" (
- "id" SERIAL,
- "title" VARCHAR(255),
- "created_at" TIMESTAMP WITH TIME ZONE NOT NULL,
- "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL,
- "user_id" INTEGER REFERENCES "users" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
- PRIMARY KEY ("id")
-);
-```
-
-## Singular vs. Plural
-
-At a first glance, it can be confusing whether the singular form or plural form of a name shall be used around in Sequelize. This section aims at clarifying that a bit.
-
-Recall that Sequelize uses a library called [inflection](https://www.npmjs.com/package/inflection) under the hood, so that irregular plurals (such as `person -> people`) are computed correctly. However, if you're working in another language, you may want to define the singular and plural forms of names directly; sequelize allows you to do this with some options.
-
-### When defining models
-
-Models should be defined with the singular form of a word. Example:
-
-```js
-sequelize.define('foo', { name: DataTypes.STRING });
-```
-
-Above, the model name is `foo` (singular), and the respective table name is `foos`, since Sequelize automatically gets the plural for the table name.
-
-### When defining a reference key in a model
-
-```js
-sequelize.define('foo', {
- name: DataTypes.STRING,
- barId: {
- type: DataTypes.INTEGER,
- allowNull: false,
- references: {
- model: "bars",
- key: "id"
- },
- onDelete: "CASCADE"
- },
-});
-```
-
-In the above example we are manually defining a key that references another model. It's not usual to do this, but if you have to, you should use the table name there. This is because the reference is created upon the referenced table name. In the example above, the plural form was used (`bars`), assuming that the `bar` model was created with the default settings (making its underlying table automatically pluralized).
-
-### When retrieving data from eager loading
-
-When you perform an `include` in a query, the included data will be added to an extra field in the returned objects, according to the following rules:
-
-* When including something from a single association (`hasOne` or `belongsTo`) - the field name will be the singular version of the model name;
-* When including something from a multiple association (`hasMany` or `belongsToMany`) - the field name will be the plural form of the model.
-
-In short, the name of the field will take the most logical form in each situation.
-
-Examples:
-
-```js
-// Assuming Foo.hasMany(Bar)
-const foo = Foo.findOne({ include: Bar });
-// foo.bars will be an array
-// foo.bar will not exist since it doens't make sense
-
-// Assuming Foo.hasOne(Bar)
-const foo = Foo.findOne({ include: Bar });
-// foo.bar will be an object (possibly null if there is no associated model)
-// foo.bars will not exist since it doens't make sense
-
-// And so on.
-```
-
-### Overriding singulars and plurals when defining aliases
-
-When defining an alias for an association, instead of using simply `{ as: 'myAlias' }`, you can pass an object to specify the singular and plural forms:
-
-```js
-Project.belongsToMany(User, {
- as: {
- singular: 'líder',
- plural: 'líderes'
- }
-});
-```
-
-If you know that a model will always use the same alias in associations, you can provide the singular and plural forms directly to the model itself:
-
-```js
-const User = sequelize.define('user', { /* ... */ }, {
- name: {
- singular: 'líder',
- plural: 'líderes',
- }
-});
-Project.belongsToMany(User);
-```
-
-The mixins added to the user instances will use the correct forms. For example, instead of `project.addUser()`, Sequelize will provide `project.getLíder()`. Also, instead of `project.setUsers()`, Sequelize will provide `project.setLíderes()`.
-
-Note: recall that using `as` to change the name of the association will also change the name of the foreign key. Therefore it is recommended to also specify the foreign key(s) involved directly in this case.
-
-```js
-// Example of possible mistake
-Invoice.belongsTo(Subscription, { as: 'TheSubscription' });
-Subscription.hasMany(Invoice);
-```
-
-The first call above will establish a foreign key called `theSubscriptionId` on `Invoice`. However, the second call will also establish a foreign key on `Invoice` (since as we know, `hasMany` calls places foreign keys in the target model) - however, it will be named `subscriptionId`. This way you will have both `subscriptionId` and `theSubscriptionId` columns.
-
-The best approach is to choose a name for the foreign key and place it explicitly in both calls. For example, if `subscription_id` was chosen:
-
-```js
-// Fixed example
-Invoice.belongsTo(Subscription, { as: 'TheSubscription', foreignKey: 'subscription_id' });
-Subscription.hasMany(Invoice, { foreignKey: 'subscription_id' });
-```
diff --git a/docs/other-topics/optimistic-locking.md b/docs/other-topics/optimistic-locking.md
deleted file mode 100644
index 69d757cd..00000000
--- a/docs/other-topics/optimistic-locking.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-title: Optimistic Locking
----
-
-Sequelize has built-in support for optimistic locking through a model instance version count.
-
-Optimistic locking is disabled by default and can be enabled by setting the `version` property to true in a specific model definition or global model configuration.
-See [model configuration](../core-concepts/model-basics.md) for more details.
-
-Optimistic locking allows concurrent access to model records for edits and prevents conflicts from overwriting data. It does this by checking whether another process has made changes to a record since it was read and throws an OptimisticLockError when a conflict is detected.
diff --git a/docs/other-topics/query-interface.md b/docs/other-topics/query-interface.md
index 8b605259..8e8008b6 100644
--- a/docs/other-topics/query-interface.md
+++ b/docs/other-topics/query-interface.md
@@ -6,14 +6,14 @@ An instance of Sequelize uses something called **Query Interface** to communicat
The methods from the query interface are therefore lower-level methods; you should use them only if you do not find another way to do it with higher-level APIs from Sequelize. They are, of course, still higher-level than running raw queries directly (i.e., writing SQL by hand).
-This guide shows a few examples, but for the full list of what it can do, and for detailed usage of each method, check the [QueryInterface API](pathname:///api/v7/classes/QueryInterface.html).
+This guide shows a few examples, but for the full list of what it can do, and for detailed usage of each method, check the [QueryInterface API](pathname:///api/v7/classes/_sequelize_core.index.AbstractQueryInterface.html).
## Obtaining the query interface
-From now on, we will call `queryInterface` the singleton instance of the [QueryInterface](pathname:///api/v7/classes/QueryInterface.html) class, which is available on your Sequelize instance:
+From now on, we will call `queryInterface` the singleton instance of the [QueryInterface](pathname:///api/v7/classes/_sequelize_core.index.AbstractQueryInterface.html) class, which is available on your Sequelize instance:
```js
-const { Sequelize, DataTypes } = require('@sequelize/core');
+import { Sequelize, DataTypes } from '@sequelize/core';
const sequelize = new Sequelize(/* ... */);
const queryInterface = sequelize.getQueryInterface();
```
@@ -151,4 +151,4 @@ DROP TABLE `Person_backup`;
## Other
-As mentioned in the beginning of this guide, there is a lot more to the Query Interface available in Sequelize! Check the [QueryInterface API](pathname:///api/v7/classes/QueryInterface.html) for a full list of what can be done.
+As mentioned in the beginning of this guide, there is a lot more to the Query Interface available in Sequelize! Check the [QueryInterface API](pathname:///api/v7/classes/_sequelize_core.index.AbstractQueryInterface.html) for a full list of what can be done.
diff --git a/docs/other-topics/read-replication.md b/docs/other-topics/read-replication.md
index 303f1c59..6e4e0a87 100644
--- a/docs/other-topics/read-replication.md
+++ b/docs/other-topics/read-replication.md
@@ -5,15 +5,19 @@ title: Read Replication
Sequelize supports [read replication](https://en.wikipedia.org/wiki/Replication_%28computing%29#Database_replication), i.e. having multiple servers that you can connect to when you want to do a SELECT query. When you do read replication, you specify one or more servers to act as read replicas, and one server to act as the main writer, which handles all writes and updates and propagates them to the replicas (note that the actual replication process is **not** handled by Sequelize, but should be set up by database backend).
```js
-const sequelize = new Sequelize('database', null, null, {
- dialect: 'mysql',
+import { MySqlDialect } from '@sequelize/mysql';
+
+const sequelize = new Sequelize({
+ dialect: MySqlDialect,
+ // Note: connection options that are not specified in "replication" will be inherited from the top level options
port: 3306,
+ database: 'database',
replication: {
read: [
- { host: '8.8.8.8', username: 'read-1-username', password: process.env.READ_DB_1_PW },
- { host: '9.9.9.9', username: 'read-2-username', password: process.env.READ_DB_2_PW }
+ { host: '8.8.8.8', user: 'read-1-username', password: process.env.READ_DB_1_PW },
+ { host: '9.9.9.9', user: 'read-2-username', password: process.env.READ_DB_2_PW }
],
- write: { host: '1.1.1.1', username: 'write-username', password: process.env.WRITE_DB_PW }
+ write: { host: '1.1.1.1', user: 'write-username', password: process.env.WRITE_DB_PW }
},
pool: { // If you want to override the options used for the read/write pool you can do so here
max: 20,
@@ -22,7 +26,11 @@ const sequelize = new Sequelize('database', null, null, {
})
```
-If you have any general settings that apply to all replicas you do not need to provide them for each instance. In the code above, database name and port is propagated to all replicas. The same will happen for user and password, if you leave them out for any of the replicas. Each replica has the following options:`host`,`port`,`username`,`password`,`database`.
+If you have any general settings that apply to all replicas, you do not need to provide them for each instance.
+In the code above, database name and port are propagated to all replicas.
+The same will happen for user and password if you leave them out for any of the replicas.
+
+Each replica accepts the connection options specific to your dialect.
Sequelize uses a pool to manage connections to your replicas. Internally Sequelize will maintain two pools created using `pool` configuration.
diff --git a/docs/other-topics/resources.md b/docs/other-topics/resources.md
index ae45be27..cdcf1848 100644
--- a/docs/other-topics/resources.md
+++ b/docs/other-topics/resources.md
@@ -9,6 +9,7 @@ A curated list of awesome projects surrounding Sequelize.
* [sequelize-typescript](https://www.npmjs.com/package/sequelize-typescript) - Decorators and some other features for sequelize (built-in as of Sequelize 7).
* [Sequelize-Nest](https://docs.nestjs.com/recipes/sql-sequelize) - Sequelize integration in [nest](https://github.com/nestjs/nest).
* [sequelizejs-decorators](https://www.npmjs.com/package/sequelizejs-decorators) decorators for composing sequelize models.
+* [kysely-sequelize](https://www.npmjs.com/pacakge/kysely-sequelize) - A toolkit (dialect, type translators, etc.) that allows using your existing Sequelize instance with [Kysely](https://www.kysely.dev).
## Code generation & visualisers
@@ -16,7 +17,7 @@ A curated list of awesome projects surrounding Sequelize.
* [sequelizer](https://github.com/andyforever/sequelizer) - A GUI Desktop App for generating Sequelize models. Support for Mysql, Mariadb, Postgres, Sqlite, Mssql.
* [sequelize-auto](https://github.com/sequelize/sequelize-auto) Generating models for SequelizeJS via the command line is another choice.
* [pg-generator](https://pg-generator.com/v4/builtin-templates--nc,d1/sequelize.html) - Auto generate/scaffold Sequelize models for PostgreSQL database.
-* [meteor modeler](https://www.datensen.com/) - Desktop tool for visual definition of Sequelize models and asssociations.
+* [meteor modeler](https://www.datensen.com/) - Desktop tool for visual definition of Sequelize models and associations.
* [sequel-ace-typescript-bundles](https://github.com/binlabs/sequel-ace-typescript-bundles) - A plugin for Sequel Ace that allows generation of Sequelize models from selected database tables.
## Performance
@@ -32,7 +33,7 @@ A curated list of awesome projects surrounding Sequelize.
* [sequelizemm](https://github.com/hasinoorit/sequelizemm) - CLI tool to generate a migration script from models
## Miscellaneous
-
+
* [sequelize-pg-utilities](https://github.com/davesag/sequelize-pg-utilities) - Opinionated set of database utilities.
* [sequelize-test-helpers](https://github.com/davesag/sequelize-test-helpers) - A collection of utilities to help with unit-testing Sequelize models and code that needs those models.
* [Sequelize-fixtures](https://github.com/domasx2/sequelize-fixtures) - Simple lib to load data to database using sequelize.
@@ -44,6 +45,7 @@ A curated list of awesome projects surrounding Sequelize.
* [sequelize-tokenify](https://github.com/pipll/sequelize-tokenify) - Add unique tokens to sequelize models
* [sqlcommenter-sequelize](https://github.com/google/sqlcommenter/tree/master/nodejs/sqlcommenter-nodejs/packages/sqlcommenter-sequelize) A [sqlcommenter](https://google.github.io/sqlcommenter/) plugin with [support for Sequelize](https://google.github.io/sqlcommenter/node/sequelize/) to augment SQL statements with comments that can be used later to correlate application code with SQL statements.
* [@rematter/sequelize-paranoid-delete](https://www.npmjs.com/package/@rematter/sequelize-paranoid-delete) - Enables onDelete when using paranoid mode.
+* [@hatchifyjs/sequelize-create-with-associations](https://github.com/bitovi/sequelize-create-with-associations) - Automatically creates, bulkCreates and updates records that have relationships to each other without extra code.
## Outdated
diff --git a/docs/other-topics/scopes.md b/docs/other-topics/scopes.md
index c4317ce0..0f160431 100644
--- a/docs/other-topics/scopes.md
+++ b/docs/other-topics/scopes.md
@@ -2,9 +2,19 @@
title: Scopes
---
-Scopes are used to help you reuse code. You can define commonly used queries, specifying options such as `where`, `include`, `limit`, etc.
+:::warning
+
+Scopes are a fragile feature that we do not recommend using beyond very simple scopes. Proceed with caution.
+
+:::
+
+:::info
-This guide concerns model scopes. You might also be interested in the [guide for association scopes](../advanced-association-concepts/association-scopes.md), which are similar but not the same thing.
+This section concerns model scopes, not to be confused with [association scopes](../associations/association-scopes.md).
+
+:::
+
+Scopes are used to help you reuse code. You can define commonly used queries, specifying options such as `where`, `include`, `limit`, etc.
## Definition
@@ -53,7 +63,7 @@ Project.init({
});
```
-You can also add scopes after a model has been defined by calling [`YourModel.addScope`](pathname:///api/v7/classes/Model.html#addScope). This is especially useful for scopes with includes, where the model in the include might not be defined at the time the other model is being defined.
+You can also add scopes after a model has been defined by calling [`YourModel.addScope`](pathname:///api/v7/classes/_sequelize_core.index.Model.html#addScope). This is especially useful for scopes with includes, where the model in the include might not be defined at the time the other model is being defined.
The default scope is always applied. This means, that with the model definition above, `Project.findAll()` will create the following query:
@@ -172,9 +182,9 @@ Using `.scope('scope1', 'scope2')` will yield the following WHERE clause:
WHERE firstName = 'bob' AND age > 20 AND age < 30 LIMIT 10
```
-Note how `limit` is overwritten by `scope2`, while `firstName` and both conditions on `age` are preserved. The `limit`, `offset`, `order`, `paranoid`, `lock` and `raw` fields are overwritten, while `where` fields are merged using the `AND` operator. The merge strategy for `include` will be discussed later on.
+Note how `limit` is overwritten by `scope2`, while `firstName` and both conditions on `age` are preserved. The `limit`, `offset`, `order`, `paranoid`, `lock` and `raw` options are overwritten, while `where` options are merged using the `AND` operator. The merge strategy for `include` will be discussed later on.
-Note that `attributes` keys of multiple applied scopes are merged in such a way that `attributes.exclude` are always preserved. This allows merging several scopes and never leaking sensitive fields in final scope.
+Note that `attributes` keys of multiple applied scopes are merged in such a way that `attributes.exclude` are always preserved. This allows merging several scopes and never leaking sensitive attributes in final scope.
The same merge logic applies when passing a find object directly to `findAll` (and similar finders) on a scoped model:
diff --git a/docs/other-topics/sub-queries.md b/docs/other-topics/sub-queries.md
deleted file mode 100644
index dd4b9eae..00000000
--- a/docs/other-topics/sub-queries.md
+++ /dev/null
@@ -1,166 +0,0 @@
----
-title: Sub Queries
----
-
-Consider you have two models, `Post` and `Reaction`, with a One-to-Many relationship set up, so that one post has many reactions:
-
-```js
-const Post = sequelize.define('post', {
- content: DataTypes.STRING
-}, { timestamps: false });
-
-const Reaction = sequelize.define('reaction', {
- type: DataTypes.STRING
-}, { timestamps: false });
-
-Post.hasMany(Reaction);
-Reaction.belongsTo(Post);
-```
-
-*Note: we have disabled timestamps just to have shorter queries for the next examples.*
-
-Let's fill our tables with some data:
-
-```js
-async function makePostWithReactions(content, reactionTypes) {
- const post = await Post.create({ content });
- await Reaction.bulkCreate(
- reactionTypes.map(type => ({ type, postId: post.id }))
- );
- return post;
-}
-
-await makePostWithReactions('Hello World', [
- 'Like', 'Angry', 'Laugh', 'Like', 'Like', 'Angry', 'Sad', 'Like'
-]);
-await makePostWithReactions('My Second Post', [
- 'Laugh', 'Laugh', 'Like', 'Laugh'
-]);
-```
-
-Now, we are ready for examples of the power of subqueries.
-
-Let's say we wanted to compute via SQL a `laughReactionsCount` for each post. We can achieve that with a sub-query, such as the following:
-
-```sql
-SELECT
- *,
- (
- SELECT COUNT(*)
- FROM reactions AS reaction
- WHERE
- reaction.postId = post.id
- AND
- reaction.type = "Laugh"
- ) AS laughReactionsCount
-FROM posts AS post
-```
-
-If we run the above raw SQL query through Sequelize, we get:
-
-```json
-[
- {
- "id": 1,
- "content": "Hello World",
- "laughReactionsCount": 1
- },
- {
- "id": 2,
- "content": "My Second Post",
- "laughReactionsCount": 3
- }
-]
-```
-
-So how can we achieve that with more help from Sequelize, without having to write the whole raw query by hand?
-
-The answer: by combining the `attributes` option of the finder methods (such as `findAll`) with the `sequelize.literal` utility function, that allows you to directly insert arbitrary content into the query without any automatic escaping.
-
-This means that Sequelize will help you with the main, larger query, but you will still have to write that sub-query by yourself:
-
-```js
-Post.findAll({
- attributes: {
- include: [
- [
- // Note the wrapping parentheses in the call below!
- sequelize.literal(`(
- SELECT COUNT(*)
- FROM reactions AS reaction
- WHERE
- reaction.postId = post.id
- AND
- reaction.type = "Laugh"
- )`),
- 'laughReactionsCount'
- ]
- ]
- }
-});
-```
-
-*Important Note: Since `sequelize.literal` inserts arbitrary content without escaping to the query, it deserves very special attention since it may be a source of (major) security vulnerabilities. It should not be used on user-generated content.* However, here, we are using `sequelize.literal` with a fixed string, carefully written by us (the coders). This is ok, since we know what we are doing.
-
-The above gives the following output:
-
-```json
-[
- {
- "id": 1,
- "content": "Hello World",
- "laughReactionsCount": 1
- },
- {
- "id": 2,
- "content": "My Second Post",
- "laughReactionsCount": 3
- }
-]
-```
-
-Success!
-
-## Using sub-queries for complex ordering
-
-This idea can be used to enable complex ordering, such as ordering posts by the number of laugh reactions they have:
-
-```js
-Post.findAll({
- attributes: {
- include: [
- [
- sequelize.literal(`(
- SELECT COUNT(*)
- FROM reactions AS reaction
- WHERE
- reaction.postId = post.id
- AND
- reaction.type = "Laugh"
- )`),
- 'laughReactionsCount'
- ]
- ]
- },
- order: [
- [sequelize.literal('laughReactionsCount'), 'DESC']
- ]
-});
-```
-
-Result:
-
-```json
-[
- {
- "id": 2,
- "content": "My Second Post",
- "laughReactionsCount": 3
- },
- {
- "id": 1,
- "content": "Hello World",
- "laughReactionsCount": 1
- }
-]
-```
\ No newline at end of file
diff --git a/docs/other-topics/transactions.md b/docs/other-topics/transactions.md
deleted file mode 100644
index a1070a8c..00000000
--- a/docs/other-topics/transactions.md
+++ /dev/null
@@ -1,314 +0,0 @@
----
-title: Transactions
----
-
-Sequelize does not use [transactions](https://en.wikipedia.org/wiki/Database_transaction) by default. However, for production-ready usage of Sequelize, you should definitely configure Sequelize to use transactions.
-
-Sequelize supports two ways of using transactions:
-
-1. **Unmanaged transactions:** Committing and rolling back the transaction should be done manually by the user (by calling the appropriate Sequelize methods).
- Note that innoDB (MariaDB and MySQL) will still automatically rollback transactions in case of deadlock. [Read more on this here](https://github.com/sequelize/sequelize/pull/12841).
-
-2. **Managed transactions**: Sequelize will automatically rollback the transaction if any error is thrown, or commit the transaction otherwise. Also, if CLS (Continuation Local Storage) is enabled, all queries within the transaction callback will automatically receive the transaction object.
-
-## Unmanaged transactions
-
-Let's start with an example:
-
-```js
-// First, we start a transaction and save it into a variable
-const t = await sequelize.transaction();
-
-try {
-
- // Then, we do some calls passing this transaction as an option:
-
- const user = await User.create({
- firstName: 'Bart',
- lastName: 'Simpson'
- }, { transaction: t });
-
- await user.addSibling({
- firstName: 'Lisa',
- lastName: 'Simpson'
- }, { transaction: t });
-
- // If the execution reaches this line, no errors were thrown.
- // We commit the transaction.
- await t.commit();
-
-} catch (error) {
-
- // If the execution reaches this line, an error was thrown.
- // We rollback the transaction.
- await t.rollback();
-
-}
-```
-
-As shown above, the *unmanaged transaction* approach requires that you commit and rollback the transaction manually, when necessary.
-
-## Managed transactions
-
-Managed transactions handle committing or rolling back the transaction automatically. You start a managed transaction by passing a callback to `sequelize.transaction`. This callback can be `async` (and usually is).
-
-The following will happen in this case:
-
-* Sequelize will automatically start a transaction and obtain a transaction object `t`
-* Then, Sequelize will execute the callback you provided, passing `t` into it
-* If your callback throws, Sequelize will automatically rollback the transaction
-* If your callback succeeds, Sequelize will automatically commit the transaction
-* Only then the `sequelize.transaction` call will settle:
- * Either resolving with the resolution of your callback
- * Or, if your callback throws, rejecting with the thrown error
-
-Example code:
-
-```js
-try {
-
- const result = await sequelize.transaction(async (t) => {
-
- const user = await User.create({
- firstName: 'Abraham',
- lastName: 'Lincoln'
- }, { transaction: t });
-
- await user.setShooter({
- firstName: 'John',
- lastName: 'Boothe'
- }, { transaction: t });
-
- return user;
-
- });
-
- // If the execution reaches this line, the transaction has been committed successfully
- // `result` is whatever was returned from the transaction callback (the `user`, in this case)
-
-} catch (error) {
-
- // If the execution reaches this line, an error occurred.
- // The transaction has already been rolled back automatically by Sequelize!
-
-}
-```
-
-Note that `t.commit()` and `t.rollback()` were not called directly (which is correct).
-
-### Throw errors to rollback
-
-When using the managed transaction you should *never* commit or rollback the transaction manually. If all queries are successful (in the sense of not throwing any error), but you still want to rollback the transaction, you should throw an error yourself:
-
-```js
-await sequelize.transaction(async t => {
- const user = await User.create({
- firstName: 'Abraham',
- lastName: 'Lincoln'
- }, { transaction: t });
-
- // Woops, the query was successful but we still want to roll back!
- // We throw an error manually, so that Sequelize handles everything automatically.
- throw new Error();
-});
-```
-
-### Automatically pass transactions to all queries
-
-In the examples above, the transaction is still manually passed, by passing `{ transaction: t }` as the second argument. To automatically pass the transaction to all queries you must install the [cls-hooked](https://github.com/Jeff-Lewis/cls-hooked) (CLS) module and instantiate a namespace in your own code:
-
-```js
-const cls = require('cls-hooked');
-const namespace = cls.createNamespace('my-very-own-namespace');
-```
-
-To enable CLS you must tell sequelize which namespace to use by using a static method of the sequelize constructor:
-
-```js
-const Sequelize = require('@sequelize/core');
-Sequelize.useCLS(namespace);
-
-new Sequelize(....);
-```
-
-Notice, that the `useCLS()` method is on the *constructor*, not on an instance of sequelize. This means that all instances will share the same namespace, and that CLS is all-or-nothing - you cannot enable it only for some instances.
-
-CLS works like a thread-local storage for callbacks. What this means in practice is that different callback chains can access local variables by using the CLS namespace. When CLS is enabled sequelize will set the `transaction` property on the namespace when a new transaction is created. Since variables set within a callback chain are private to that chain several concurrent transactions can exist at the same time:
-
-```js
-sequelize.transaction((t1) => {
- namespace.get('transaction') === t1; // true
-});
-
-sequelize.transaction((t2) => {
- namespace.get('transaction') === t2; // true
-});
-```
-
-In most case you won't need to access `namespace.get('transaction')` directly, since all queries will automatically look for a transaction on the namespace:
-
-```js
-sequelize.transaction((t1) => {
- // With CLS enabled, the user will be created inside the transaction
- return User.create({ name: 'Alice' });
-});
-```
-
-## Concurrent/Partial transactions
-
-You can have concurrent transactions within a sequence of queries or have some of them excluded from any transactions. Use the `transaction` option to control which transaction a query belongs to:
-
-**Note:** *SQLite does not support more than one transaction at the same time.*
-
-### With CLS enabled
-
-```js
-sequelize.transaction((t1) => {
- return sequelize.transaction((t2) => {
- // With CLS enabled, queries here will by default use t2.
- // Pass in the `transaction` option to define/alter the transaction they belong to.
- return Promise.all([
- User.create({ name: 'Bob' }, { transaction: null }),
- User.create({ name: 'Mallory' }, { transaction: t1 }),
- User.create({ name: 'John' }) // this would default to t2
- ]);
- });
-});
-```
-
-## Passing options
-
-The `sequelize.transaction` method accepts options.
-
-For unmanaged transactions, just use `sequelize.transaction(options)`.
-
-For managed transactions, use `sequelize.transaction(options, callback)`.
-
-## Isolation levels
-
-The possible isolations levels to use when starting a transaction:
-
-```js
-const { Transaction } = require('@sequelize/core');
-
-// The following are valid isolation levels:
-Transaction.ISOLATION_LEVELS.READ_UNCOMMITTED // "READ UNCOMMITTED"
-Transaction.ISOLATION_LEVELS.READ_COMMITTED // "READ COMMITTED"
-Transaction.ISOLATION_LEVELS.REPEATABLE_READ // "REPEATABLE READ"
-Transaction.ISOLATION_LEVELS.SERIALIZABLE // "SERIALIZABLE"
-```
-
-By default, sequelize uses the isolation level of the database. If you want to use a different isolation level, pass in the desired level as the first argument:
-
-```js
-const { Transaction } = require('@sequelize/core');
-
-await sequelize.transaction({
- isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE
-}, async (t) => {
- // Your code
-});
-```
-
-You can also overwrite the `isolationLevel` setting globally with an option in the Sequelize constructor:
-
-```js
-const { Sequelize, Transaction } = require('@sequelize/core');
-
-const sequelize = new Sequelize('sqlite::memory:', {
- isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE
-});
-```
-
-**Note for MSSQL:** _The `SET ISOLATION LEVEL` queries are not logged since the specified `isolationLevel` is passed directly to `tedious`._
-
-## Usage with other sequelize methods
-
-The `transaction` option goes with most other options, which are usually the first argument of a method.
-
-For methods that take values, like `.create`, `.update()`, etc. `transaction` should be passed to the option in the second argument.
-
-If unsure, refer to the API documentation for the method you are using to be sure of the signature.
-
-Examples:
-
-```js
-await User.create({ name: 'Foo Bar' }, { transaction: t });
-
-await User.findAll({
- where: {
- name: 'Foo Bar'
- },
- transaction: t
-});
-```
-
-## The `afterCommit` hook
-
-A `transaction` object allows tracking if and when it is committed.
-
-An `afterCommit` hook can be added to both managed and unmanaged transaction objects:
-
-```js
-// Managed transaction:
-await sequelize.transaction(async (t) => {
- t.afterCommit(() => {
- // Your logic
- });
-});
-
-// Unmanaged transaction:
-const t = await sequelize.transaction();
-t.afterCommit(() => {
- // Your logic
-});
-await t.commit();
-```
-
-The callback passed to `afterCommit` can be `async`. In this case:
-
-* For a managed transaction: the `sequelize.transaction` call will wait for it before settling;
-* For an unmanaged transaction: the `t.commit` call will wait for it before settling.
-
-Notes:
-
-* The `afterCommit` hook is not raised if the transaction is rolled back;
-* The `afterCommit` hook does not modify the return value of the transaction (unlike most hooks)
-
-You can use the `afterCommit` hook in conjunction with model hooks to know when a instance is saved and available outside of a transaction
-
-```js
-User.hooks.addListener('afterSave', (instance, options) => {
- if (options.transaction) {
- // Save done within a transaction, wait until transaction is committed to
- // notify listeners the instance has been saved
- options.transaction.afterCommit(() => /* Notify */)
- return;
- }
- // Save done outside a transaction, safe for callers to fetch the updated model
- // Notify
-});
-```
-
-## Locks
-
-Queries within a `transaction` can be performed with locks:
-
-```js
-return User.findAll({
- limit: 1,
- lock: true,
- transaction: t1
-});
-```
-
-Queries within a transaction can skip locked rows:
-
-```js
-return User.findAll({
- limit: 1,
- lock: true,
- skipLocked: true,
- transaction: t2
-});
-```
diff --git a/docs/other-topics/typescript.mdx b/docs/other-topics/typescript.mdx
deleted file mode 100644
index cab9e9c3..00000000
--- a/docs/other-topics/typescript.mdx
+++ /dev/null
@@ -1,282 +0,0 @@
----
-title: TypeScript
----
-
-Sequelize provides its own TypeScript definitions.
-
-Please note that only **TypeScript >= 4.5** is supported.
-Our TypeScript support does not follow SemVer. We will support TypeScript releases for at least one year, after which they may be dropped in a SemVer MINOR release.
-
-As Sequelize heavily relies on runtime property assignments, TypeScript won't be very useful out of the box.
-A decent amount of manual type declarations are needed to make models workable.
-
-## Installation
-
-In order to avoid clashes with different Node versions, the typings for Node are not included. You must install [`@types/node`](https://www.npmjs.com/package/@types/node) manually.
-
-## Usage
-
-**Important**: You must use `declare` on your class properties typings to ensure TypeScript does not emit those class properties.
-See [Caveat with Public Class Fields](../core-concepts/model-basics.md#caveat-with-public-class-fields)
-
-Sequelize Models accept two generic types to define what the model's Attributes & Creation Attributes are like:
-
-```typescript
-import { Model, Optional } from '@sequelize/core';
-
-// We don't recommend doing this. Read on for the new way of declaring Model typings.
-
-type UserAttributes = {
- id: number,
- name: string,
- // other attributes...
-};
-
-// we're telling the Model that 'id' is optional
-// when creating an instance of the model (such as using Model.create()).
-type UserCreationAttributes = Optional;
-
-class User extends Model {
- declare id: number;
- declare string: number;
- // other attributes...
-}
-```
-
-This solution is verbose. Sequelize >=6.14.0 provides new utility types that will drastically reduce the amount
-of boilerplate necessary: [`InferAttributes`](pathname:///api/v7/index.html#InferAttributes), and [`InferCreationAttributes`](pathname:///api/v7/index.html#InferCreationAttributes). They will extract Attribute typings
-directly from the Model:
-
-```typescript
-import { Model, InferAttributes, InferCreationAttributes, CreationOptional } from '@sequelize/core';
-
-// order of InferAttributes & InferCreationAttributes is important.
-class User extends Model, InferCreationAttributes