npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

adbm

v2.0.5

Published

Simple, pluggable async database migration tool for node.js >= 12.0.0

Downloads

69

Readme

adbm

adbm is a simple tool for managing database migrations. It follows the UNIX philosophy of "do one thing and do it well" - therefore it does not include a CLI for creating migration files, nor does it currently sport any other bells and whistles.

In a nutshell, adbm itself will include all .js files from a particular directory (migrations/ by default), see if they export an object that contains both an up and a down function and then run one of these functions (depending on whether you're migrating up or down of course).

Since the core of adbm is database agnostic, it requires an adapter for all operations that need to persist data (such as registering successfully run migrations or retrieving the list of already applied migrations).

Installation

yarn add adbm

To actually work with adbm you'll also need to install an adapter (unless you're rolling your own):

yarn add adbm-mongodb

API

adbm(db, adapter, injections) -> Function

  • db (required) Database driver instance (as returned by await mongodb.connect() for instance). This will be passed to all migration functions as well as all adapter functions.
  • adapter(required) Adapter object (see Adapters section).
  • injections (optional):
    • getMigrationObjects (optional, default getMigrationObjectsFromDirectory) A function that returns an array of migration objects, i.e. objects that expose an up and a down function, as well as an id.
    • metadata (optional, default _adbm) Name of the table or collection containing migration metadata (i.e. the list of already applied migrations).
    • logger (optional, defaults to console logging) Logger object providing debug, verbose, info, warn and error functions (e.g. a winston instance).

This will return an async function which will run (or revert) available migrations. The returned function will have the following API:

migrate({ direction, exclude }) -> Promise<Array<{ id, duration }>>

  • direction (optional, default up) Migration direction. up applies pending migrations (i.e. those not in the list of already applied migrations), down reverts migrations that have already been applied
  • exclude (optional, default [ ]) List of migration ids to ignore

This function will return a promise that resolves to an array containing a list of all performed migrations.

Example

import { adbm, getMigrationObjectsFromDirectory } from 'adbm'
import { adapter } from 'adbm-mongodb'
import { MongoClient } from 'mongodb'

export const runMigrations = async () => {
  const client = await MongoClient.connect('mongodb://localhost/myDatabase')
  const db = client.db('myDatabase')
  const migrate = adbm(db, adapter, { 
    // Here we're setting the migrations directory to a custom path. If your migrations reside in the
    // default directory ("migrations/"), you do not need to pass the third argument at all 
    getMigrationObjects: args => getMigrationObjectsFromDirectory({
      ...args,
      directory: 'custom/migrations/directory'
    })
  })
  
  const migrations = await migrate() // same as "await migrate({ direction: 'up', exclude: [] })"
  
  console.dir(migrations)
}

Migration Files

All migration files must be CJS modules (ESM and TS are not currently supported) and export an object that implements both an up and a down function. Migrations are identified by their filename (i.e. 01-init-db.js will have 01-init-db as it's migration id) and will be executed in the order retrieved from the filesystem.

up and down methods can (and probably should) be async functions (they will be awaited) and will each receive the following parameters when they're executed:

  • db Database driver instance (as originally passed to adbm())
  • logger Logger instance (as originally passed to adbm())

Example

const id = 123
const collection = 'users'

module.exports = {
  async up (db, logger) {
    logger.info('Creating user %s', id)
    
    await db.collection(collection).insertOne({ id, name: 'Some Guy', group: 'admins' })
  },
  async down (db, logger) {
    logger.warn('Removing user %s', id)
    
    await db.collection(collection).removeOne({ id })
  }
}

Adapters

Adapters are small objects that handle all database specific tasks. One adapter compatible with v2 of this library is currently available on npm: adbm-mongodb. To write your own adapter for any other dbms you'll simply need to implement the adapter API and provide your adapter object to the adbm() initialization function.

API

Parameters

Adapter functions will receive a subset of the following parameters (see below for details on which function receives what):

  • id Migration ID
  • db Database driver instance
  • metadata Name of metadata table or collection
  • logger Logger object

Functions

Adapters need to implement the following functions (all of which will propably need to be async/Promise returning):

  • init({ db, metadata, logger }) -> void Will be called before any migrations take place. Can/should be used to create the database and/or the migration metadata table if necessary.
  • getCompletedMigrationIds({ db, metadata, logger }) -> Array<String> Expected to return an array of already applied migration IDs.
  • registerMigration({ id, db, metadata, logger }) -> void Called upon successful completion of a migration. Should be used to store migration ID in the metadata table so it will be included when getCompletedMigrationIds is called the next time.
  • unregisterMigration({ id, db, metadata, logger }) -> void Called when a migration has been reverted. Should be used to remove the migration ID from the list of completed migrations.

Error Handling

Similar to its spiritual predecessor library reconsider, adbm does not handle any migration errors. The reasoning behind this has remained the exact same: handling migration errors would either involve too much guesswork or introduce a host of new config options for no good reason (Revert everything? Don't revert anything? Attempt to call the failed migration's down method?). It is the caller's responsibility to handle errors appropriately.

One consequence of adbm's lack of error handling is that an error in any migration will prevent all subsequent migrations from running. This, too, is intended behavior, since database migrations will more often than not rely on changes introduced by previous migrations. Since no automatic rollback is performed, and since all successful migrations will still register, migrate() can safely be called again once the problem has been resolved.

This should also encourage the user to write small migrations that change one thing at a time, as opposed to huge migration files that change several things at once.

Testing

yarn run test

As of 2.0.0, tests no longer require a connection to a mongodb server.

Author

Michael Smesnik at crystallize

License

MIT