I\'m close to having my project ready to launch. I have big plans for after launch and the database structure is going to change -- new columns in existing tables as well as
There is even more simple way (avoiding Sequalize). Which goes like this:
You type a command inside your project: npm run migrate:new
This creates 3 files. A js file, and two sql files named up and down
For this to work, please have a look at the db-migrate module.
Once you get it setup (which is not difficult), changing your DB is really easy and saves a lot of time.
I went through this post and similar questions, it didn't really answer it for me. Migrations are useful for spinning up local databases and for updating data in production
I asked the question here and answered it as well: Workflow for handling sequelize migrations and initialization?
TL-DR version for a greenfield project
.sql
file oversequelize init:migrate
in the whatever folder where your models
are atsequelize migration:generate --name [name_of_your_migration]
("use strict");
/**
* DROP SCHEMA public CASCADE; CREATE SCHEMA public
* ^ there's a schema file with all the tables in there. it drops all of that, recreates
*/
const fs = require("fs");
const initialSqlScript = fs.readFileSync("./migrations/sql/Production001.sql", {
encoding: "utf-8",
});
const db = require("../models");
module.exports = {
up: () => db.sequelize.query(initialSqlScript),
down: () =>
db.sequelize.query(`DROP SCHEMA public CASCADE; CREATE SCHEMA public;
`),
};
with this general folder structure
sequelize migration:generate --name [name_of_your_migration]
up
and down
migration paths. These are your ALTER statements to change column names, DELETE, ADD columns etcsequelize db:migrate
npm install sequelize-auto
.sequelize-auto -o "./models" -d sequelize_auto_test -h localhost -u my_username -p 5432 -x my_password -e postgres
found under https://github.com/sequelize/sequelize-autoYou can use git to see difflogs on your model, there should be only changes reflecting changes in the database model. As a side note, do not ever modify the models
directly if you use sequelize auto
, as this will generate them for you. Likewise, you no longer should modify your database schema directly with SQL files, granted this is an option as you can import those .sql
files as well
Now your database schema is up to date, and you've officially moved over to sequelize database migrations only.
Everything is version controlled. This is the ideal workflow for database and backend developer
Use version. Version of the application depends on the version of the database. If the new version requires an update of a database, create migration for it.
update: I decided to abandon the migration (KISS) and run script update_db (sync forse: false) when it is needed.
Here is my current workflow. I'm open to suggestions.
That way you don't have to manually update the migrations table and have to worry about fat fingers, but you still get an ORM.
In your case, the most reliable way is to do it almost manually. I would suggest to use sequelize-cli tool. The syntax is rather plain:
sequelize init
...
sequelize model:create --name User --attributes first_name:string,last_name:string,bio:text
This will create both model AND migration. Then, manually merge your existing models with generated with sequelize-cli, and do the same with migrations. After doing this, wipe database (if possible), and run
sequelize db:migrate
This will create schema will migrations. You should do this only once to switch to proper process of schema developments (without sync:force, but with authoritative migrations).
Later, when you need to change schema:
sequelize migration:create
sequelize db:migrate
Obviously you can't ssh to production server and run migrations by hands. Use umzug, framework agnostic migration tool for Node.JS to perform pending migrations before app starts.
You can get a list of pending/not yet executed migrations like this:
umzug.pending().then(function (migrations) {
// "migrations" will be an Array with the names of
// pending migrations.
});
Then execute migrations (inside callback). The execute method is a general purpose function that runs for every specified migrations the respective function:
umzug.execute({
migrations: ['some-id', 'some-other-id'],
method: 'up'
}).then(function (migrations) {
// "migrations" will be an Array of all executed/reverted migrations.
});
And my suggestion is to do it before app starts and tries to serve routes every time. Something like this:
umzug.pending().then(function(migrations) {
// "migrations" will be an Array with the names of
// pending migrations.
umzug.execute({
migrations: migrations,
method: 'up'
}).then(function(migrations) {
// "migrations" will be an Array of all executed/reverted migrations.
// start the server
app.listen(3000);
// do your stuff
});
});
I can't try this right now, but at first look it should work.
After a year, still useful, so sharing my current tips. For now, I'm installing sequelize-cli
package as required live dependancy, and then modify NPM startup scripts in package.json
like this:
...
"scripts": {
"dev": "grunt && sequelize db:migrate && sequelize db:seed:all && node bin/www",
"start": "sequelize db:migrate && sequelize db:seed:all && node bin/www"
},
...
The only thing I need to do on production server is npm start
. This command will run all migrations, apply all seeders and start app server. No need to call umzug manually.
Now with the new sequelize migration is very simple.
This is a example what you can do.
'use strict';
var Promise = require('bluebird'),
fs = require('fs');
module.exports = {
up: function (queryInterface, Sequelize) {
return Promise
.resolve()
.then(function() {
return fs.readFileSync(__dirname + '/../initial-db.sql', 'utf-8');
})
.then(function (initialSchema) {
return queryInterface.sequelize.query(initialSchema);
})
},
down: function (queryInterface, Sequelize) {
return Promise
.resolve()
.then(function() {
return fs.readFileSync(__dirname + '/../drop-initial-db.sql', 'utf-8');
})
.then(function (dropSql) {
return queryInterface.sequelize.query(dropSql);
});
}
};
Remember you have to set:
"dialectOptions": { "multipleStatements": true }
on database config.