Skip to content

Instantly share code, notes, and snippets.

@scryptonite
Last active October 24, 2019 01:52
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save scryptonite/3bee4b3d55485230db113af639a0601f to your computer and use it in GitHub Desktop.
Save scryptonite/3bee4b3d55485230db113af639a0601f to your computer and use it in GitHub Desktop.
Dumps all Sequelize models to JSON to help with creating the first migration script.
  1. Modify & run dump-sequelize-schema.js.
    • It currently uses a few lodash methods, so be sure to temporarily install them—or if you feel up to it you can rewrite the script so they're not needed.
    • npm i lodash.sortby lodash.pick lodash.omit lodash.mapvalues
  2. Inspect sequelize-schema.json and make sure it appears represent all of your tables, attributes, indexes, constraints, references, etc...
  3. When you are satisfied, copy and rename sequelize-schema.json file into a migration-extras/ directory that is next to your migrations/. Name it initial-sequelize-schema.json.
    • migration-extras/
      • -> initial-sequelize-schema.json
    • migrations/
      • (this folder should probably be empty)
  4. Run sequelize migration:create and then copy the contents of 2018XXXXXXXXXX-initial-migration.js into the newly generated migration script. No additional modifications are required if your initial-sequelize-schema.json is in the right place relative to your new migration script.
  5. Run your migration using sequelize db:migrate. You should see it synchronize all of your model definitions to your DB.
  6. ???
  7. Congratulations, your project's first migration script was a snap!
    • Go ahead and inspect in a DB explorer to verify everything is as you intended; It should be exactly the same as if you had called sequelize.sync() in your project.
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
const {sequelize} = queryInterface;
// Setup all models:
const models = require("../migration-extras/initial-sequelize-schema.json")
// First, restore the sequelize attribute type property so sequelize recognizes it:
.map(def => Object.assign(def, {
attributes: Object.entries(def.attributes)
.map(([field, attribute]) => {
let type = Object.assign({}, attribute.type);
if(type && "_constructor" in type && type._constructor) {
try {
type = Object.assign(Object.create(Sequelize.DataTypes[type._constructor].prototype), type);
delete type._constructor;
} catch(err){
throw new TypeError(`Failed type def creation for ${def.name}.${field}, ${err}`);
}
} else {
console.error(type);
throw new TypeError(`Unexpected attribute type for ${def.name}.${field}, ${type}`);
}
return {
[field]: Object.assign({}, attribute, {
type
})
};
})
.reduce((_, b) => Object.assign(_, b), {}),
}))
// Second, define the model using the model definition:
.map(def => ({
[def.name]: sequelize.define(def.name, def.attributes, def.options)
}))
// Third, merge all { [modelName]: model } objects into one object
// (It will be exactly like the `sequelize.models` property):
.reduce((_, b) => Object.assign(_, b), {});
// Fourth, create a promise chain of synchronizing the models to the DB:
return Object.values(models)
.reduce((sync, model) =>
sync.then(() => {
console.log(`Synchronizing ${model.name} (table: ${model.options.tableName})...`);
return model.sync();
})
, Promise.resolve());
},
down: (queryInterface, Sequelize) => {
const tables = require("../migration-extras/initial-sequelize-schema.json")
.map(({ options: {tableName} }) => tableName);
// Create a promise chain of dropping the model tables from the DB:
return tables.reduce((dropOperation, tableName) => {
return dropOperation.then(() => {
console.log(`Dropping table "${tableName}"...`);
return queryInterface.dropTable(tableName);
});
}, Promise.resolve(undefined));
}
};
const _ = {
sortBy: require("lodash.sortby"),
pick: require("lodash.pick"),
mapValues: require("lodash.mapvalues"),
omit: require("lodash.omit"),
};
const fs = require("fs");
const sequelize = ...; // [TODO] point to your sequelize instance
// Make sure you have all of your models defined already,
// as well as their associations have been setup too
// (e.g. using an associate() method or similar pattern).
const models = _.sortBy(Object.values(sequelize.models).map(
(model) =>
_.mapValues(_.pick(model, [
// keep only the model name, the provided options, and the defined attributes.
"name",
"options",
"attributes"
]), (value, key) => {
switch(key){
case "options": {
// Omit the sequelize instance reference from the options property to
// avoid a circular reference error when we JSON.stringify:
return _.omit(value, [
"sequelize"
]);
}
case "attributes": {
// Preserve sequelize column type (as a string in a _constructor property):
return _.mapValues(value, (attribute, column) => {
return Object.assign(
{},
attribute,
{
type: Object.assign({
_constructor: attribute.type.key
}, attribute.type)
}
);
});
}
case "tableName":
case "name":
default: {
return value;
}
}
})
), ["name"]);
// Save the schema to a file:
fs.writeFileSync("./sequelize-schema.json", JSON.stringify(models, null, 4));
@t16n
Copy link

t16n commented May 13, 2019

Hey I just tried out your script thanks for providing it!
Unfortunately the result of dump-sequelize-schema.js doesn't look too promising (without the schema) what am i doing wrong?
By the way I used the model definition described here: https://github.com/sequelize/express-example/blob/master/models/index.js
as an example.
[ { "name": "Task", "options": { "timestamps": true, "validate": {}, "freezeTableName": false, "underscored": false, "paranoid": false, "rejectOnEmpty": false, "whereCollection": null, "schema": null, "schemaDelimiter": "", "defaultScope": {}, "scopes": {}, "indexes": [], "name": { "plural": "Tasks", "singular": "Task" }, "omitNull": false, "hooks": {} } }, { "name": "User", "options": { "timestamps": true, "validate": {}, "freezeTableName": false, "underscored": false, "paranoid": false, "rejectOnEmpty": false, "whereCollection": null, "schema": null, "schemaDelimiter": "", "defaultScope": {}, "scopes": {}, "indexes": [], "name": { "plural": "Users", "singular": "User" }, "omitNull": false, "hooks": {} } } ]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment