Sequelize umzug migrations - node.js

I am using sequelize js and developed a node js application, which is deployed to production and have live DB.
while in development mode, if I need to alter the DB, I used to do it using Sequelize.sync({ force: true }) and it worked well.
But now, after development is done I want to alter a table and add a column to it.
I searched many posts but, didn't get an exact example on how to run these migrations.
I tried to use Umzug and run a migration, but it is throwing me errors.
Here is the code I tried,
migrations/barmigration.js:
var Sequelize = require('sequelize');
"use strict";
module.exports = {
up: function(migration, DataTypes) {
return [
migration.addColumn(
'Bars',
'PrinterId',
Sequelize.STRING
),
migration.addColumn(
'Bars',
'PrinterStatus',
Sequelize.STRING
)]
},
down: function(migration, DataTypes) {
return
[
migration.removeColumn('Bars', 'PrinterStatus'),
migration.removeColumn('Bars', 'PrinterId')
]
}
};
Here is the umzug configuration:
var Umzug = require('umzug');
var sequelize = require('sequelize');
var umzug = new Umzug({
// storage: 'sequelize',
model: 'Bar',
storageOptions: {
sequelize: sequelize,
},
migrations: {
path: './migrations',
pattern: /\.js$/
}
});
// umzug.up().then(function(migrations) {
// console.log('Migration complete!');
// });
umzug.down().then(function(migrations) {
console.log('Migration complete!');
});
When I run that file, I am getting an error in up function, at this position return migration.addColumn
Error:
Unhandled rejection TypeError: Cannot read property 'addColumn' of undefined
So, the parameter migration seems to be undefined. Pls help me out.

You need to define the params property inside migrations attribute of the object passed to the constructor of Umzug - it defines the parameters that are passed to the up and down functions. I suppose that the configuration object should be as follows
{
storage: 'sequelize',
storageOptions: {
sequelize: sequelize // here should be a sequelize instance, not the Sequelize module
},
migrations: {
params: [
sequelize.getQueryInterface(),
Sequelize // Sequelize constructor - the required module
],
path: './migrations',
pattern: /\.js$/
}
}
With above definition the migration parameter would now become the sequelize.getQueryInterface(), so simply queryInterface, and the DataTypes parameter is the Sequelize itself.
The model attribute is used to define a Sequelize migrations model, you do not need to define it if you are satisfied with the default SequelizeMeta table creation. Here is how the Umzug constructor object should look like, and here is how the storageOptions can look like.
EDIT
In separate file you need to create a Sequelize instance (further used to define models etc.) and export it. Let's assume files tree as below
- db
- database.js
- umzug.js
So in the database.js we create a Sequelize instance and export it.
// database.js
const Sequelize = require('sequelize');
const db = {
sequelize: new Sequelize(connectionString, options),
Sequelize: Sequelize
};
module.exports = db;
Then, in the Umzug configuration file
// umzug.js
const db = require('./database');
// here comes the configuration and initialization of Umzug instance with use of db object
// db.sequelize -> sequelize instance
// db.Sequelize -> sequelize constructor (class)

Related

Sequelize 'no such table:' despite table and database clearly existing and correctly configured

Good evening.
I'm building an Express REST API using SQLite, with Sequelize being my ORM of choice.
I was working with an existing database, so I used a package called sequelize-auto in order
to generate my models.
Problem is, Sequelize for some reason won't see my database, a .db file, or won't detect the table, despite being clearly defined. The problem occurs in this block of code:
const Sequelize = require('sequelize');
const sequelize = new Sequelize('expenserdb', 'user', 'pass', {
dialect: 'sqlite',
host: '../database/expenserdb.db'
});
const initModels = require('../models/init-models');
const models = initModels(sequelize);
module.exports = class dbService {
editUserName(newName) {
models.User.update({Name: newName}, {
where: {
UserId: 1
}
})
.catch((err) => {console.log(err)});
}
}
This is my dbService.js file, and as you can see, the database is clearly defined, with the name, location and everything being correct. Despite all of this, I'm still getting:
Error: SQLITE_ERROR: no such table: User {errno: 1, code: 'SQLITE_ERROR', sql: 'UPDATE `User` SET `Name`=$1 WHERE `UserId` = $2'
This service is being injected into my controller via awilix, and the method is called inside the controller:
const container = require("../containerConfig");
const dbService = container.resolve("dbService");
exports.runNameChange = async (req) => {
const newName = JSON.stringify(req.body.name);
const result = await dbService.editUserName(newName);
return result;
};
And in turn, the controller method is ran when this endpoint is hit:
app.post('/updateuser', async function(req, res) {
const result = await userController.runNameChange(req);
res.status(200).send(String(result));
});
Also, the dboptions.json file:
{
"storage":"./database/expenserdb.db"
}
I'm new to Node, Express, and Sequelize in general. I might have missed something crucial, but I can't seem to realize what. Help is greatly appreciated.
Update: just figured it out, you need the FULL path and not the relative path in the
verbose constructor, as such:
const sequelize = new Sequelize('expenserdb', 'user', 'pass', {
dialect: 'sqlite',
storage: 'E:/desktopshortcuts/yahalom/expenser/expenser-server/database/expenserdb.db'
});

Sequelize not creating any tables in postgresql

I just pulled a project to a new machine, I use PostgreSQL for my DB, sequelize as an ORM, and feathersjs and express or middleware. I try to start the project as I always do but it doesn't work.
I have an .env file with all my environment configuration info, what I usually do is first run sequelize db:create, to create the DB, after that, I use npm start to create the tables. However when I do it it doesn't create any table or show any error, it just shows the info: Feathers application started on http://localhost:3030 but when I open pgAdmin the Database is empty. I tried logging in in sequelize but it's not showing anything at all. I also deleted the ENV and it doesn't show any error at npm start (it does show an error at sequelize db:create. This is driving me nuts.
Heres my sequelize.js:
require('dotenv').config();
const Sequelize = require('sequelize');
const { Op } = Sequelize;
const operatorsAliases = {
// ...
};
module.exports = function (app) {
const connectionString = `postgres://${process.env.DB_USERNAME}:${process.env.DB_PASSWORD}#` +
`${process.env.DB_HOST}:${process.env.DB_PORT}/${process.env.DB_NAME}`;
const sequelize = new Sequelize(connectionString, {
dialect: 'postgres',
dialectOptions: {
ssl: false
},
logging: console.log,
operatorsAliases,
define: {
freezeTableName: true
}
});
const oldSetup = app.setup;
app.set('sequelizeClient', sequelize);
app.setup = function (...args) {
const result = oldSetup.apply(this, args);
// Set up data relationships
const models = sequelize.models;
Object.keys(models).forEach(name => {
if ('associate' in models[name]) {
models[name].associate(models);
}
});
// Sync to the database
sequelize.sync({ logging: console.log });
return result;
};
};
My npm start script just points to an app.js file where I just import sequelize with app.configure(sequelize);
You may need to run sequelize db:migrate after creating the database
So it turns out is actually an issue with node 14. I had to install a newer version of node-Postgres (pg#8.0.3) as pointed out in here

Assigning an object of an array to sequelize

Am new to using sql in with nodejs as i just read about sequelize and i tried to convert one of my running nodejs and mongodb application into a sql database structured application.
So i am changing my mongoose schema to sequelize model schema. So i got stucked in trying to convert the code below to work in sql.
const Sequelize = require('sequelize');
const db = require('../config/database');
const Auto = db.define('auto', {
packages : {
container: {
type: Sequelize.ARRAY(Sequelize.TEXT)
},
roro: {
type: Sequelize.ARRAY(Sequelize.TEXT)
}
}
});
module.exports = Auto;
but i kept getting this error.
Unrecognized datatype for attribute "auto.packages"
The problem is your model definition.
According to the docs: https://sequelize.org/master/manual/model-basics.html#model-definition
You can't define a nested object as properties of a model.
I would recommend you to refactor your model definition according to the docs by extending Sequelize.Model:
const Sequelize = require('sequelize');
const { Sequelize, DataTypes, Model } = require('sequelize');
const db = require('../config/database');
class Auto extends Model {
// You can extend your model as you wish.
}
Auto.init(
{
container: {
type: Sequelize.ARRAY(Sequelize.TEXT)
},
roro: {
type: Sequelize.ARRAY(Sequelize.TEXT)
}
},
{
db, // We need to pass the connection instance
modelName: 'auto' // We need to choose the model name
// Any other model configurations you need.
});
module.exports = Auto;

How to get model from sequelize.model after use sequelize-cli

I use sequelize-cli to auto generate code for model Student:
module.exports = (sequelize, DataTypes) => {
var _stu = sequelize.define('stu', {
name: DataTypes.STRING,
password: DataTypes.STRING,
gender: DataTypes.INTEGER,
}, {
classMethods: {
associate: function(models) {
// associations can be defined here
}
}
});
return _stu
};
My question are
How to get model named stu? As the sequelize is defined in function signature.
When sequelize model:generate,sequelize read config in config.json,where dbname,passowrd are specified.
How the sequelize in the function signature knows database connection config,say,name,password,etc as I don't specify in this js file.
Answer for question 1:
"sequelize.import" will do this job.
Try this code:
const Sequelize = require('sequelize')
const sequelize = new Sequelize(...)
const stu = sequelize.import('./models/stu')
stu.findAll.then(...)
When you generate models via the CLI, it creates a handy models/index.js file that handles passing in a Sequelize instance for you. You can simply require cherry picked, existing models via ES6 destructuring like this:
var { stu } = require("./models");
stu.findAll().then(...);
Alternatively, you could require them all at once, and then access specific models as needed:
var models = require("./models");
models.stu.findAll().then(...);
The way i make it works was importing model with the require() function and then call it with required parameters.
Explanation
By require function you will get another function that returns your model.
module.exports = (sequelize, DataTypes) => {
const stu = sequelize.define('stu', {
// ...database fields
}, {});
stu.associate = function(models) {
// associations can be defined here
};
return sty;
} // note that module.exports is a function
Now, this function requires the initialized sequelize object and a DataTypes object, so you just have to pass the instance of sequelize and Sequelize.DataTypes and then it will return your model, example:
const Sequelize = require('sequelize');
const sequelize = new Sequelize(...);
const Stu = require('./models/stu')(sequelize, Sequelize.DataTypes);
Finally you can get your database rows with Stu.findAll().
Hope this can help you.

Sequelize: Require vs Import

In the documentation for sequlize they use the import function like so
// in your server file - e.g. app.js
var Project = sequelize.import(__dirname + "/path/to/models/project")
// The model definition is done in /path/to/models/project.js
// As you might notice, the DataTypes are the very same as explained above
module.exports = function(sequelize, DataTypes) {
return sequelize.define("Project", {
name: DataTypes.STRING,
description: DataTypes.TEXT
})
}
However, what would be so wrong with this?
// in your server file - e.g. app.js
var Project = require(__dirname + "/path/to/models/project")
// The model definition is done in /path/to/models/project.js
var Project = sequelize.define("Project", {
name: Sequelize.STRING,
description: Sequelize.TEXT
});
module.exports = Project
Well, as you can see your model definition needs two things:
Sequelize or DataTypes
sequelize
In your first example when using sequelize.import('something'); it is similar to use require('something')(this, Sequelize); (this being the sequelize instance)
Both are necessary to initialize your model, but the important thing to understand is: One of these is a classtype so it's global, the other one is an instance and has to be created with your connection parameters.
So if you do this:
var Project = sequelize.define("Project", {
name: Sequelize.STRING,
description: Sequelize.TEXT
});
module.exports = Project
Where does sequelize come from? It has to be instantiated and passed somehow.
Here is an example with require instead of import:
// /path/to/app.js
var Sequelize = require('sequelize');
var sequelize = new Sequelize(/* ... */);
var Project = require('/path/to/models/project')(sequelize, Sequelize);
// /path/to/models/project.js
module.exports = function (sequelize, DataTypes) {
sequelize.define("Project", {
name: DataTypes.STRING,
description: DataTypes.TEXT
});
};
module.exports = Project
You could even change it so you wouldn't have to pass Sequelize by requiring it in the model itself, but you would still need to create a sequelize instance prior to define the model.
sequelize.import is deprecated as of sequelize 6
As mentioned at https://sequelize.org/master/manual/models-definition.html
Deprecated: sequelize.import
Note: You should not use sequelize.import. Please just use require instead.
So you should just port:
// index.js
const sequelize = new Sequelize(...)
const User = sequelize.import('./user')
// user.js
module.exports = (sequelize, DataTypes) => {
// Define User.
return User;
}
to:
// index.js
const sequelize = new Sequelize(...)
const User = require('./user')(sequelize)
// user.js
const { DataTypes } = require('sequelize')
module.exports = (sequelize) => {
// Define User.
return User;
}

Resources