
Security News
Deno 2.6 + Socket: Supply Chain Defense In Your CLI
Deno 2.6 introduces deno audit with a new --socket flag that plugs directly into Socket to bring supply chain security checks into the Deno CLI.
@joinbox/loopback-component-jb-migration
Advanced tools
Component providing data structures to perform custom migrations in Loopback applications
Migration component for loopback, providing data structures to perform migrations in Loopback applications.
Note: The code is in a really early stage, is barely tested and only supports postgres. Since there's a high possibility that the interface will change, we decided to go for version 1 as a first version.
Basically you hook in the component using the component-config.json:
{
"@joinbox/loopback-component-jb-migration": {
"exposeAt": "migration"
}
}
The exposeAt property defines where to expose the component ('jb-migration' by default). Currently
it has no methods on it and only provides access to the data structures:
// 00-boot-migration.js
const migration = app.get('migration');
const queue = new migration.MigrationQueue();
You can also require the package, for now this does not make a difference.
const migration = require('@joinbox/loopback-component-jb-migration';
module.exports = class MyTask extends migration.Task {
}
All migrations are tied together by a migration model. The migration model basically determines
which datasource you want to apply your migration tasks to (the one it is attached to) and will
be used to persist the result of a task. A migration model has to fulfill a certain interface,
as given by the migration-mixin. Your MyMigration model could
look as follows:
{
"name": "MyMigration",
"base": "PersistedModel",
"mixins": {
"MigrationMixin": true
}
}
The migration-mixin defines the interface your model has to fulfill. You can use the mixin in your
application by referencing it in the mixins section of you model-config.json:
{
"_meta": {
"mixins": [
"@joinbox/loopback-component-jb-migration/src/mixins"
]
}
}
Be aware: this is similar to a deep include and should be optimized in future versions.
Basically every element is a Task (or a corresponding subclass). The basic structure of your setup should look as follows:
You can access all the classes either by including the package or using the component itself as described above.
All steps in a migration are defined as tasks. All the tasks have a
async run(depencencies, previousResult) method. Dependencies is currently only an object
containing the MigrationModel reference and a transaction if it exists. And was introduced to
provide a flexible interface for future versions. Every task has an identifier.
If you want to create a custom task, just extend the Task class and implement the aforementioned
run method.
You might note the version parameter in the Task class. Be aware that it is not used yet.
A MigrationTask is an extended version of the basic Task class which delegates to a concrete task if the task did not already run. It performs the following steps:
MigrationModel.taskRanSuccessfully(task, transaction)
method. The migration model by default tries to load an instance of a migration with the identifier
of the task in the database.MigrationModel using Migration.startOne can prevent the MigrationTask from running a task only once by setting the runAlways option
to true.
We also use this to bypass this check when creating the table for the migrations, which will otherwise lead to an error.
class MyTask extends Task {
constructor() {
const identifier = 'MyTask';
const options = {
runAlways: true,
};
super({identifier, options });
}
}
The MigrationQueue takes all your tasks (doesn't matter if it is a MigrationTask or a plain Task) and runs them one after the other. It determines which is the current MigrationModel and opens a transaction on the underlying connector if configured accordingly:
const { MigrationQueue } = require('@joinbox/loopback-component-jb-migration');
const tasks = [/*your tasks*/];
const queue = new MigrationQueue(tasks, {
// name of the migration model, defaults to 'Migration'
migrationModelName: 'MyMigration',
transactionConfig: {}
});
queue.run({ app });
The transactionConfig object has two purposes:
After all tasks are executed, the queue will commit the transaction or roll it back on error.
All the tasks have to be aware of transactions and have to pass them to all the methods that might
use a transaction (via the options). The transaction is passed to the tasks in the dependencies
object used by the run method. A transaction is fully optional.
The package provides some pre-made tasks that can be accessed using the tasks property.
A simplified interface to execute sql statements. The class takes either a filePath or a statement
as a parameter to the constructor. It will either read in the file or execute the passed statement
directly on the data source of the migration model:
const path = require('path');
const jbMigration = require('@joinbox/loopback-component-jb-migration');
const { ExecutSQLStatementTask } = jbMigration.tasks;
const createMyTable = new ExecuteSqlStatementTask({
identifier: 'CreateMyModelTable',
filePath: path.resolve(__dirname, './createMyModelTable.sql'
});
Concrete Tasks to create or drop the table for the basic migration model:
const path = require('path');
const jbMigration = require('@joinbox/loopback-component-jb-migration');
const {
CreateMigrationTable,
DropMigrationTable,
} = jbMigration.tasks.postgres;
const createMigrationTableTask = new CreateMigrationTable();
const dropMigrationTableTask = new DropMigrationTable();
You should perform your migrations at the beginning of the boot phase of Loopback, e.g: in a file
called 00-create-model-tables.js:
module.exports = async (app) => {
const migration = app.get('migration');
const tasks = [
new migration.tasks.postgres.CreateMigrationTable(),
new migration.tasks.ExecuteSQLStatementTask({
identifier: 'CreateMyModelTable',
statement: `CREATE TABLE IF NOT EXISTS "mySchema"."myModel" (
id SERIAL PRIMARY KEY
)`,
}),
];
// wrap all tasks in a migration task to see what has happened
const migrationTasks = tasks.map((task) => new migration.MigrationTask(task));
// queue with transactions
const queue = new migration.MigrationQueue(migrationTasks, {
transactionConfig: {},
migrationModelName: 'MyMigration',
}
);
return queue.run({ app });
};
Currently we only provide explicit support for the postgres connector, but basically, all data structures provided by the package can be used with an arbitrary data source.
In your tasks you can execute raw sql (have a look at the CreateMigrationTable class in the tasks
folder). One can usually override the schema per model using connector specific
configuration, i.e.
{
"name": "MyMigration",
"options": {
"validateUpsert" : true,
"postgresql": {
"table": "MyMigration",
"schema": "my_migration_schema"
}
}
}
or per datasource. As soon as you perform raw sql queries you'll be responsible to handle the schema yourself.
Note: Be aware that Postgres resolves the schema based on the
search_pathwhich is"$user", publicby default. So if you have a schema that has got the same name as the user opening the connection, it misleadingly looks like your queries do respect the schema out of the box. They don't!
Note: Don't touch the
search_pathit is evil!
FAQs
Component providing data structures to perform custom migrations in Loopback applications
We found that @joinbox/loopback-component-jb-migration demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Deno 2.6 introduces deno audit with a new --socket flag that plugs directly into Socket to bring supply chain security checks into the Deno CLI.

Security News
New DoS and source code exposure bugs in React Server Components and Next.js: what’s affected and how to update safely.

Security News
Socket CEO Feross Aboukhadijeh joins Software Engineering Daily to discuss modern software supply chain attacks and rising AI-driven security risks.