This short article is to explain how you can add a new non-null
and unique
column in an existing Relational database using sequelize
migration in nodeJS.
Note: This is for development environments and not recommended for production.
Sometimes while writing any application we might require to add new non-null and unique column in the existing database without dropping the current table, so I tried doing it in my demo project where I'm using sequelize ORM, and I'm writing this article because I couldn't find the complete solution at one place, so here's my attempt at solving this.
Problem description
Consider we have users
table with the following columns and now we want to add a new username
column which has to be unique and non-null to this without dropping the table.
| id | name | email | age | hobby |
|----|-------|-----------------------|-----|---------|
| 1 | bob | bob@mars.com | 23 | coding |
| 2 | alice | alice01@jupyter.com | 25 | dance |
| 3 | John | superjohn@earth.com | 22 | singing |
| 4 | frank | frank@saturn.com | 28 | reading |
Solution
One simple solution is to insert the same email
field in username
column as both are unique but we'll take general example where this is not possible.
Let's start by creating a migration file using sequelize-cli
npx sequelize migration:create --name=add_username_users
Here's a screen capture of pseudo code
To explain, First we add username
column with just unique
constraint on its success we write any logic to update every row of newly added username
column with any logic you like. I used { type: QueryTypes.SELECT }
in SELECT
query as we don't need metadata here and to get query results in an array on its success will alter the column to add not-null
constraint. For the down part of sequelize migration will just drop the username
column.
Here's my final code to add a new column with a randomly generated username, which can be updated later or can be kept as it is during the development environment.
const { QueryTypes } = require('sequelize');
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.addColumn('users','username',{
type: Sequelize.STRING,
unique: true
}).then((data) => {
queryInterface.sequelize
.query(`SELECT id from users`, {
type: QueryTypes.SELECT
})
.then((rows) => {
rows.map((row) => {
queryInterface.sequelize.query(
`UPDATE users SET username='${Math.random()
.toString(36).substr(2, 8)}' WHERE id=${row.id};`
);
});
Promise.resolve();
})
.then(() => {
queryInterface.changeColumn('users', 'username', {
type: Sequelize.STRING,
allowNull: false
});
});
})
},
down: async (queryInterface, Sequelize) => {
await queryInterface.removeColumn('users', 'username');
}
};
Thank you for reading and please share your thoughts on it.
References:
random generation: stackOverflow answer
code idea: sequelize github issue
Top comments (3)
very helpful! sample with async syntax:
Some slight changes due to postgres syntax
Great stuff man
Really Helpful