Benny's answer is completely correct. However, I noticed that you specifically mention Postgres, in which case its syntax knex.raw will not work for you. Here is the method that I have used successfully.
Add function
If you have multiple migration files in the given order, you may need to artificially change the date in the file name to run it first (or just add it to the first migration file). If you cannot go back, you may need to complete this step manually using psql . However, for new projects:
const ON_UPDATE_TIMESTAMP_FUNCTION = ` CREATE OR REPLACE FUNCTION on_update_timestamp() RETURNS trigger AS $$ BEGIN NEW.updated_at = now(); RETURN NEW; END; $$ language 'plpgsql'; ` const DROP_ON_UPDATE_TIMESTAMP_FUNCTION = `DROP FUNCTION on_update_timestamp` exports.up = knex => knex.raw(ON_UPDATE_TIMESTAMP_FUNCTION) exports.down = knex => knex.raw(DROP_ON_UPDATE_TIMESTAMP_FUNCTION)
The function should now be available for all subsequent migrations.
Define an auxiliary trigger knex.raw
I find it more expressive not to repeat large chunks of SQL in the migration files if I can avoid this. I used knexfile.js here, but if you don't like complicating this, you can define it everywhere.
module.exports = { development: { // ... }, production: { // ... }, onUpdateTrigger: table => ` CREATE TRIGGER ${table}_updated_at BEFORE UPDATE ON ${table} FOR EACH ROW EXECUTE PROCEDURE on_update_timestamp(); ` }
Use helper
Finally, we can quite conveniently define automatic update triggers:
const { onUpdateTrigger } = require('../knexfile') exports.up = knex => knex.schema.createTable('posts', t => { t.increments() t.string('title') t.string('body') t.timestamps(true, true) }) .then(() => knex.raw(onUpdateTrigger('posts'))) exports.down = knex => knex.schema.dropTable('posts')
Note that deleting the table is enough to get rid of the trigger: we do not need an explicit DROP TRIGGER .
This may seem like a lot of work, but it is quite βrememberedβ after you have done this, and is useful if you want to avoid using ORM.