Creating Static Values into a Sequelize model (no experience with migration) - node.js

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

Related

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);
},
};

Customizing sequelize-cli generated IDs

Created a model using:
sequelize-cli model:create --name User --attributes "dispName:string,email:string,phoneNum1:string,vendorId:integer"
Which resulted in the following migration:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
dispName: {
type: Sequelize.STRING
},
// plus others...
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};
I want to change the automatically defined ID to:
cognitoId: {
allowNull: false,
primaryKey: true,
type: Sequelize.STRING(100)
}
So:
Will sequelize be able to recognize this as the ID?
Where all do I need to make this change? I could only identify the migration file.
The model file doesn't have a definition for the cognitoId (or the original auto-generated id field): how will I be able to get the value of a User instance's cognitoId (in the data returned by queries)?
Will changing the auto-generated id field have repercussions down the line?
Is the field name id "magical"? I.e., does the primary key have to be named id?
Is there a better way to do this?
Will changing the types of the fields from Sequelize.STRING to Sequelize.STRING(100) create any issues down the line?
Why doesn't the models file generated by sequelize-cli have the id field defined?
When generating models+migrations from the command-line I couldn't find any syntax to specify the ID or any other customization for the fields.
Using:
[Node: 12.14.1, CLI: 5.5.1, ORM: 5.21.3]
PS: relatively new to NodeJS & completely new to Sequelize.
Yes
You should declare custom named PK in your model
see p.2. If you don't declare PK in your model then sequelize assumes you have id PK with an integer type, autoincremented. If you wish to assign your PK another name you should declare it in the model.
Depends on what changes you make
It is the default PK name in sequelize (see p.3). You can set different name to your PK manually declaring it in your model (see p.3)
Personally I prefer to declare all PKs in my models even if they have id name and default PK type and value.
No issues if all PK values do not exceed this length
see p.3
You can define names and types only for fields while generating models from the command line.

Sequelize how not to redefine models every time server starts

In Sequelize tutorials, it is said that a single model is generated in this way:
const User = sequelize.define('user', {
firstName: {
type: Sequelize.STRING
},
lastName: {
type: Sequelize.STRING
}
});
And than saved (i.e. create table) like this :
User.sync().then(() => {
// do whatever
});
But I expect to do that just once, I need to create tables just once. So the next time I run the script how to just retrieve models (i.e. tables) that were defined before with the above code.
in sync method you can pass an option to avoid sync of database tables every time. This option will make sure that your application checks for table in database, if it exist then it will not create otherwise it will create table.
User.sync(
{force: false}).then(() => {
// do whatever
});
Let me know if you still face issue. I am using sequalize and i am not getting this issue.

How to create assocations in Sequelize migrations?

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)

Resources