Skip to content

New feature: support sql files #189

Description

@luukvhoudt

Because 90% of all use cases involve SQL, or at least that's my suspicion, when using a migration tool like this package provides. That's why I think it might be good addition to include an utility function what adds support for doing something based on a list of (SQL) files their content.

Currently I'm using a function in one of my projects just like that and I thought why not share it?

...

All what is needed is a file structure like this (note as of v1.7.0, you can't have this structure in the same directory as where your migrations are stored, it should be on a higher level):

.
├── up
│   ├── 00-tables.sql
│   └── 123-triggers.sql
└── down
    ├── 00-tables.sql
    └── 123-triggers.sql

Then the next thing to do is to setup a single migration:

const {upFromFiles, downFromFiles} = require("migrate"); // Not available yet?
const database = require("./database");

const upHandler = async (fileContents) => {
  // Do something with the contents of the file.
  // This example is querying the database
  const pool = database.getPool(); // Setup a database connection
  await pool.query(fileContents); // Send a query to the database
};

// This function is needed if you maintain the same order in the down SQL files as you do in the up files.
// Example:
// - up file: create table posts (...); create table comments (..., post_id int references posts);
// - down file: drop table posts; drop table comments;
// This function reverses the order of those statements: drop table comments; drop table posts;
const downHandler = async (fileContents) => {
  const seperator = ";";
  const reversedContents = fileContents
    .split(seperator)
    .reverse()
    .join(seperator);
  await upHandler(reversedContents);
};

module.exports.up = async () => {
  await upFromFiles("./up", upHandler);
};

module.exports.down = async () => {
  await downFromFiles("./down", downHandler);
};

The functions upFromFiles and downFromFiles could be defined as following:

const fs = require("fs");
const path = require("path");
const chalk = require("chalk");

const log = (key: string, msg: string, last: boolean = false) => {
  const prefix = "  " + (last ? "└──" : "├──");
  console.log(`${prefix} ${chalk.grey(key)} : ${chalk.cyan(msg)}`);
};

const fromFiles = async (key, fileNames, handler) => {
  for (const index in fileNames) {
    const fileName = fileNames[index];
    const filePath = path.join(dir, path.sep, fileName);
    const contents = fs.readFileSync(filePath, "utf8").toString();
    log(key, fileName, Number(index) === fileNames.length - 1);
    await handler(contents);
  }
};

module.exports.upFromFiles = async (dir, handler) => {
  const fileNames = fs.readdirSync(dir);
  await fromFiles("up", fileNames, handler);
};

module.exports.downFromFiles = async (dir, handler) => {
  const fileNames = fs.readdirSync(dir).reverse();
  await fromFiles("down", fileNames, handler);
}

Maybe if the package author agrees this code can be integrated with this package. If not and people do prefer to have this around for the taking. Then I might consider to publish into a separate package.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions