How to create assocations in Sequelize migrations? - node.js

I am using migrations to create entities. Naturally, some have relations between them. Until now, by using sync(true), I enjoyed the benefit of Sequelize implementing the relations for me at the database level.
How do I express new relations in a migration?
One-to-many: Should I be taking care of the foreign key columns?
Many-to-many: Should I be creating the intermediate table and setting foreign keys on each entity's table?
Or: Am I supposed to run the migration and then sync(false)?
What about relations that are no longer relevant?

When I use migrations, i set sync:false.
And to set associations, my model ends up like so:
User.js:
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define("User", {
"fname": {
"type": DataTypes.STRING,
"unique": false,
"allowNull": false
},
"lname": {
"type": DataTypes.STRING,
"allowNull": false
}
}, {
"tableName": "Users",
"classMethods": {
"associate": function (models) {
Locale.hasMany(models.Permissions);
}
}
});
return User;
};
This will still create the table if it doesn't exist. However I recommend that creating a table is still part of a migration. Please note that in your migration to create a table to include the id, updatedAt, createdAt and PermissionId columns (for the example above)

Related

how to create parent/child associations in sequelize?

I have a Employee and their dependents model defined in sequlieze. I have an employeeId foreign key column defined in the dependent table ( see my model class below) . but when i try to execute following command
models.Employee.findOne({where: { id: Number(id) }, include: [{ model: models.Dependent }]});
i get an error -> EagerLoadingError [SequelizeEagerLoadingError]: Dependent is not associated to Employee!
isn't specifying a foreign key in the dependent model , enough?
Employee model
module.exports = function(sequelize, DataTypes) {
const Employee = sequelize.define('Employee ', {
id : {
type: DataTypes.INTEGER(11),
allowNull: false,
autoIncrement:true,
primaryKey:true
},
Name : { ... }
Dependent model
module.exports = function(sequelize, DataTypes) {
const Dependent = sequelize.define('Dependent', {
id : {
type: DataTypes.INTEGER(11),
allowNull: false,
autoIncrement:true,
primaryKey:true
},
EmployeeId : {
type: DataTypes.INTEGER(11),
allowNull: true,
references : {
model : 'Employee ',
key:'id'
}
},
Name : { ... }
isn't specifying a foreign key in the dependent model, enough?
No, it's NOT enough if you want to use Eager Loading of sequelize.
You need to create sequelize associations for these models(Employee and Dependent for your case) in pairs, see Why associations are defined in pairs?
The references option in the model only creates an FK relationship between Dependent and Employee tables in the database(relationship for db level). Does not include associations of sequelize model(relationship for application level). When to use? see Enforcing a foreign key reference without constraints
Employee has many dependents and dependent belongs to one Employee. So you need to create One-To-Many relationships.
Employee.hasMany(Dependent);
Dependent.belongsTo(Employee);
create associations like above will create FK reference for tables implicitly.
Execution results:
Executing (default): CREATE TABLE IF NOT EXISTS "employees" ("id" SERIAL , "name" VARCHAR(255), PRIMARY KEY ("id"));
Executing (default): CREATE TABLE IF NOT EXISTS "dependents" ("id" SERIAL , "name" VARCHAR(255), "EmployeeId" INTEGER REFERENCES "employees" ("id") ON DELETE SET NULL ON UPDATE CASCADE, PRIMARY KEY ("id"));

Am i supposed to keep Sequelize models and migrations in sync?

I'm new to Sequelize.js and Databases in general, i haven't used migrations before, but i know that they can be used to make changes to the tables structure in a non-destructive way.
However i'm not sure where to declare column options (notNull, references, validate, ENUM values, etc...)
Should i declare such options in the the model file, or migration file? or both?
Wouldn't adding the options to both model and migration cause duplicate code?
(keep in mind that i'm talking about the initial migrations that create tables to the database, not the migrations that add columns and stuff...)
Any help would be appreciated!
I see three options you can take. The first two options might be edge cases but it helps to understand.
Destructive option
You want to prototype a project and you don't mind losing your data, then you could potentially do not care about migration files and synchronize your database according to your model with:
await sequelize.sync({ force: true });
It will execute on all your models:
DROP TABLE IF EXISTS "your_model" CASCADE;
CREATE TABLE IF NOT EXISTS "your_model" (...)
This command can be executed at the start of your application for example.
 Static option
As you mentioned you don't want to add columns and stuff, it's probably a good option.
Now if you don't want to lose data, you could simply use the sync method without the force option:
await sequelize.sync({ });
It will only generate:
CREATE TABLE IF NOT EXISTS "your_model" (...)
Hence, your tables are created according to your models and you don't have to create migration files.
However, if you want to modify your model and it's the most frequent usecase, the new column will not be generated in the table dynamically that's why you need migration scripts.
Flexible option
You will have to define both migration file and your model. That's what the cli does. Here is an example:
# npx sequelize-cli init or create migrations and models folders
npx sequelize-cli model:generate --name User --attributes firstName:string,email:string
Now you will have two more files:
// migrations/<date>-create-user.js
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
firstName: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
// I usually remove this and create the table only if not exists
return queryInterface.dropTable('Users');
}
};
// models/users.js
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
firstName: DataTypes.STRING,
email: DataTypes.STRING
}, {});
User.associate = function(models) {
// associations can be defined here
};
return User;
};
You could refactor the code from the migration and the model, however it will be rather cumbersome because some migration files will only add one column, so merging all of them into the model might probably be less clear.
You should do it in both because as time goes by your models and inital migration will differ from each other. So I suppose you should determine a final structure in models and after that create an initial migration.
Constraints are defined and run on SQL level while validations are run on application level. Sequelize supports having validations and constraints on Models, only constraints can be defined in migrations.
My opinion is to put all constraints in migrations, and validations in Models. That way you have some kind of separation of concern as validations are run before query is made to the database - where constraints are run. You can read more on Sequelize's Validations and Constraints Validations and Constraints

Best way to programmatically add tables to node and express app with Sequelize

I have been doing some research on this for a bit but can't seem to find the right answer. I am building a personal bugetting application with node, Express and Sequelize. In order to give users as much flexibility as possible, I want the app to dynamically generate tables for user budgets. Users can create as many budgets as they want, and add up to 10 or 12 columns. I was thinking of using a Sequelize raw query, but dynamically generating the query is messy and I'm not sure it's very good practice. Should I use a migration and the umzug library? The thing is I'm still pretty new to server side development and databases so I just need a bit of guidance on this. Here is my current raw query. I haven't tested it yet as I have been building out other core components of the app.
/* Handle dynamic budget table creation and queries */
const Db = require('./lib/Db.js');
module.exports = class Budget extends Db
{
/**
* Create a new budget table
* #param {String} name - budget name
* #param {Object} columns - object of column names and their values
* #param {String} user - user name
* #param {Number} userId - user id
*/
async createNewBudget(name, columns, user, userId)
{
let query = `CREATE TABLE ${name}_budget (`;
for (let key in columns) {
query += `${key} NUMERIC, `;
}
query += ")";
await this.db.sequelize.query(query);
let insert = `INSERT INTO ${name}_budget(`;
for (let key in columns) {
insert += `${key}, `;
}
insert += ") VALUES (";
for (let key in columns) {
insert += `${columns[key]}, `;
}
insert += ")";
await this.db.sequelize.query(insert);
}
}
The Db class is a simple class that makes available the sequelize instance:
/* Base class for all classes interacting with the database via raw queries */
module.exports = class Db
{
constructor()
{
this.db = require('../models/index.js');
}
}
I have these files in my lib directory. I just have a feeling that the way I'm going about this isn't that good. Any helpful suggestions would be greatly appreciated.
I'm a little curious as to why you want a user to be able to create new tables... I think you should be using migration to setup your original database, and think it through to the point where users have id's, then you have a user_budget table that is a many to many connection to budget an array of options after that, database design does take some planning but it is not rocket science and if/when you don't get it right, it's not too hard to change... postgres, mySQL etc are great at handling scads of rows efficiently and many relationships, but i think you might be creating a bunch of technical debt and non-scalable solution to create new tables for new budgets, that is unnecessary with an RDBMS. You are creating a new table for something that could just be a couple rows in a well designed DB
We can define the model in one file, say models/budget.js:
module.exports = function (sequelize, DataTypes) {
const Budget = sequelize.define('budget', {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
},
category: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
},
name: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
},
...,
version: {
type: DataTypes.INTEGER,
defaultValue: 0,
allowNull: false,
},
}, {
version: true,
paranoid: true
});
return Budget;
};
Then create a "migrations" file which loads the model into the DB:
'use strict';
const models = require('../models');
module.exports = {
up: function (queryInterface) {
return queryInterface.createTable(models.Budget.tableName, models.Budget.rawAttributes);
},
down: function (queryInterface) {
return queryInterface.dropTable(models.Budget.tableName);
},
};

Creating Static Values into a Sequelize model (no experience with migration)

I am working on a group project that tests our use of Sequelize vs. normal ORM generated back-end items. Two of our models in a MySQL DB are category tables. These will not be dynamically created, updated or destroyed, but need to be there when the program runs. The class I am a part of hasn't covered instances or migrations. Here is the model thus far..
module.exports = function(sequelize, DataTypes){
var maincategories = sequelize.define("maincategories", {
maincategories_id: {
//make primary key
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement:true
},
maincategories_name: {
type: DataTypes.STRING,
allowNull: false
}
},
{
classMethods: {
associate: function(models){
maincategories.hasMany(models.posts),
maincategories.hasMany(models.subcategories)
}
}
});
// maincategories.create({ maincategories_name: 'For Sale'}).then(function(insertedCategory){
// console.log(insertedCategory.dataValues);
// });
maincategories.create({ maincategories_name: 'Housing'})
maincategories.create({ maincategories_name: 'Personals'});
return maincategories;
// maincategories.hasMany(posts);
// maincategories.hasMany(subcategories);
};
How can I get the Category table to have those values added to it at the time or prior to the node.js app starting? Also, would the code reside in the model, api route or a separate file that is required in somewhere else? As you can see I tried to do persistent instances but these did not work. It would state that I had created these items in node CLI but nothing showed in the actual database.
Thank you.
You need maincategories.sync() in order for sequelize to sync the defined models to your db.
http://docs.sequelizejs.com/en/v3/api/sequelize/#syncoptions-promise

Node/Express Project: Create and Sync a "Join Class/Model" to a Postgres Database

I'm building a Node/Express/Postgres version of an app that I already built in Rails. I'm learning Node, so I figured I'd rebuild something that I know works.
For now, I'm dumping everything in one file (set up my database, defined my models, etc.), just to make sure I have everything set up correctly before I divvy them up into different files.
I set up my postgres database at the very top of the file, like so:
var Sequelize = require('sequelize');
var db = new Sequelize('my_database_name', 'my_username', null, {
host: 'localhost',
dialect: 'postgres',
});
With regard to my models, I have a Politician model:
var Politician = db.define("politician", {
name: {
type: Sequelize.STRING,
},
politicalParty: {
type: Sequelize.STRING
}
});
A Category model:
var Category = db.define("category", {
name: {
type: Sequelize.STRING
},
keywords: {
type: Sequelize.ARRAY(Sequelize.TEXT)
},
});
And a join model of Politician and Category, called "Interest". Because Interest is a join model, it will have a "politicianId" and "categoryId" properties....but will those properties automatically generate in the database? And so, is this how I would define the Interest model, with no properties?
Interest Model:
var Interest = db.define("interest")
Or, will I have to be specific, and create "politicianId" and "categoryId" properties? Like so:
Interest Model:
var Interest = db.define("interest", {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
categoryId: {
type: Sequelize.INTEGER,
foreignKey: true
},
politicianId: {
type: Sequelize.INTEGER,
foreignKey: true
}
});
Also, do I need the "foreignKey: true" bit? Or will it automatically know that those properties are foreign keys? Also, do I need the "id" property? I know models automatically create their own primary key "id"...but again, I've been at this for hours, looking at docs, and trying everything.
I then defined my associations (again, all of this is the same file):
Politician.belongsToMany(Category, {through: "Interest"});
Category.belongsToMany(Politician, {through: "Interest"});
The Node/Sequelize docs seems to suggest that defining those 2 associations above will automatically "create a new model called Interest with the equivalent foreign keys politicianId and categoryId." So, do I even need to define a "Interest" model? Also, do I need the follow associations to describe that Interest belongs to Politician and Category?
Interest.belongsTo(Politician);
Interest.belongsTo(Category);
If I don't write the associations saying that Interest belongs to Politican and Catetory, I don't get the "politicianId" and "categoryId" columns in the Interest table. Just the "id" and createdAt/updatedAt columns.
I then created an instance of Politician, Category, and Interest, to persist everything to the database, to see if everything is there and set up correctly:
Politician Object:
var politician1 = Politician.sync({force: true}).then(function(){
return Politician.create(aPoliticianObjectDefinedInthisFile);
});
This works perfectly. I see this object in the politician table in the database.
Category Object:
var category1 = Category.sync({force: true}).then(function(){
return Category.create(aCategoryObjectDefinedInThisFile);
});
This works perfectly. I see this object in the category table in the database.
Here is what doesn't work. Creating an instance/object of Interest and synching it to the database. My thinking is, if I put integers as values, it will know that "politicianId: 1" means point to the politician object with an id of 1, and the same for "categoryId: 1". But when I write it as I have it below, the Interest table doesn't even show up in the Postgres database at all.
Interest Object:
Interest.sync({force: true}).then(function(){
return Interest.create(
{
politicianId: 1,
categoryId: 1
}
);
});
However, when I create the object of Interest like this, with no properties defined, the Interest table appears in the database, along with the "politicianId" and "categoryId" columns, however, those columns are empty. The object's primary id is in there at 1, and the "createdAt" and "updatedAt" columns have data too. But the foreign key columns are blank.
Interest Object:
Interest.sync({force: true}).then(function()
{
return Interest.create(
{
// No properties defined.
}
);
}
);
Sorry for this long post, lol, but, in all:
Am I creating the "Interest" model correctly?
Am I writing the associations for "Interest" correctly?
Do I even need to write associations for Interest, if I already have associations for its parent classes, Politican and Category defined?
In my Rails app, my associations for Politican and Category are like so:
Politician has_many interests
Politican has_many categories through interests
Category has_many interests
Category has_many politicians through interests
Interest belongs_to politician
Interest belongs_to category
But I use the "belongsToMay" association in Node because I got an error telling me to do so.
Basically, I need to create an instance of Politician, an instance of Category, and an instance of Interest that has "politicianId" and "categoryId" columns that point to those aforementioned instances of those classes.
politicanABC -- id: 1
categoryABC -- id: 1
instanceABC -- id: 1; politicanId: 1 (referring to politicanABC); categoryid: 1 (referring to categoryABC).
My app is set up like that in Rails and works wonderfully.
Help and thank you in advance :-)
You don't have to define the Interest model if you are not going to add any additional fields. Sequelize will internally define the model and add all required fields once you do following:
Politician.belongsToMany(Category, {through: "Interest"});
Category.belongsToMany(Politician, {through: "Interest"});
Sync needs to run on database level and not on tables since Interest model is implicit at this point.
db.sync({force: true});
Sequelize will add relationship build methods on both Politician and Category instances. Category will have methods addPolitician(), addPoliticians([]), setPoliticians([]), getPliticians(). Politician instances will have similar functions to associate categories to them. You can connect these after create option is performed on both objects successfully.
Politician.create({name: 'John Doe', politicalParty: 'Nice Party'})
.then(function(politician) {
Category.create({name: 'Nicers'})
.then(function(category) {
politician.addCategory(category);
});
});
You can also search and associate existing items using helper methods. Alternatively you can associate objects manually by accessing db.models.Interest model and running creates on it.

Resources