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

atomicity

v1.0.1

Published

AtomicityJS helps you ensure the atomicity of cross database transactions using high friendly code

Downloads

4

Readme

AtomicityJS

AtomicityJS helps you ensure the atomicity of cross database transactions using high friendly code.

Supported Databases:

  • MySQL
  • PostgreSQL
  • SQL Server
  • MongoDB

Coming soon:

  • SQLite3
  • Neo4j
  • Oracle
  • And more

Before start:

  • AtomicityJS is in beta
  • Currently for the relational database transactions supported, it is necessary to configure a Knex connection to be passed to AtomicityJS.
  • And as for the MongoDB, Mongoose must be used.
  • The plan is that Atomicity will in future support pure database node drivers and also Sequelize ORM.

Get started

Installation:

npm install atomicity

or:

yarn add atomicity

Let's make some code !

First of all you should know that Atomicity will make cross database transactions and common transactions. And under the hoods it chooses the best way to do it.

Setting some Knex connections to start:

//myDatabaseConfig.js
import knex from 'knex';


export const mysqlKnex = (() => knex({
  client: 'mysql', 
  connection: {
    host: 'localhost',
    user: 'dbuser',
    port: 3306,
    password: 'pleasedonthackme',
    database: 'mydatabase',
  }
}))();


export const postgreKnex = (() => knex({
  client: 'pg',
  connection: {
    host: 'localhost',
    user: 'dbuser',
    port: 3306,
    password: 'pleasedonthackme',
    database: 'mydatabase',
  }
}))();


export const sqlServerKnex = (() => knex({
  client: 'mssql',
  connection: {
    host: 'localhost',
    user: 'dbuser',
    port: 3306,
    password: 'pleasedonthackme',
    database: 'mydatabase',
  }
}))();

How you can see basically the knex configuration does not change so much from database to database.

Setting a Mongo connection:

IMPORTANT: MongoDB supports transactions only from version 4.0 upwards and only in replica set.

You just need to configure Mongoose normally and it will be ready to use.

The Atomicity constructor:

The Atomicity constructor is very simple, you just need to pass an object with the connections you will use in your transaction.

For instance, if you want to make a transaction with MySQL and MongoDB you will just need to pass your MySQL Knex connection and your mongoose configured:


const atomic = new Atomicity({ mysql: connectKnexMysql, mongoDb: mongoose });

The complete list of options that you can pass in the constructor is: mysql, sqlServer, postgreSql and mongoDb

The transact method:

After initialize the constructor correctly you will be able to use the transact method.

The method will receive just a callback that will run all your database instructions. The Atomicity will inject a param in your callback.

For each database configured in the Atomicity constructor a transaction reference will be injected in an object that will be passed as param in your callback.

For the instance, let's see how it would work with the Atomicity that we configured in last section:

const atomic = new Atomicity({ mysql: connectKnexMysql, mongoDb: mongoose });
const transactionReturnedData = await atomic.transact( async ({ mysqlTrx, mongoSession }) => {

  await someMySQLDatabaseInstruction(mysqlTrx);

  const mongoReturnedData = await someMongoDatabaseInstruction(mongoSession);

  return mongoReturnedData;
})

How you can see, for each connection ( mysql, mongoDb ) a transaction reference (mysqlTrx, mongoSession) is injected in the param object of your callback.

The transact method also return your callback return.

Each connection in the Atomicity configuration will generate a transaction reference. The relationship between configured connections and tansactions references is:

  • mysql generates mysqlTrx
  • sqlServer generates sqlserverTrx
  • postgreSql generates postgresqlTrx
  • mongoDb generates mongoSession

A simple relational transaction:

Assuming we have already configured our knex connections as in the examples above, let's start with a simple MySQL relational transaction:

import Atomicity from 'atomicity';
import { mysqlKnex } from './myDatabaseConfig';

//TABLES SHORTHANDS
const User = () => mysqlKnex('user');
const Pet = () => mysqlKnex('user_pet');


//CREATE USER AND HIS PET TRANSACTION

const createUserTrx = (name, email, trx) =>
  User()
    .insert({ name, email }, 'id')
    .transacting(trx);


const createPetTrx = (userId, petName, trx) =>
  Pet()
    .insert({ id_user: userId, name: pet })
    .transacting(trx);


const createUser = async (name, email, pet) => {

  const atomic = new Atomicity({ mysql: mysqlKnex });

  const userId = await atomic.transact( async ({ mysqlTrx }) => {

    const userId = ( await createUserTrx(name, email, mysqlTrx) )[0];
    await createPetTrx(userId, pet, trx);
    return userId;

  });

}

If any error occurs while executing the callback of the transact method, all changes already made will be reversed.

A transaction that cross three different databases:

IMPORTANT: Atomicity does not support yet transactions with different connections but the same database client. For instance, currently is not possible to do transactions between two databases where both are MySQL, but is totally possible perform a transaction between PostgreSQL and MySQL connections. This feature will be available in the future.

Now let's imagine that we want put the user and pet data in different databases in different ways. We want to do just like we did in the example above, but we also want to add in SQL Server and MongoDB.

Let's consider a schema for Mongo that will look like this structure:

{
  userIdMysql: Number,
  userIdSQLServer: Number,
  lastNameKnown: String,
  lastEmailKnown: String
}

The code will be something like this:

import Atomicity from 'atomicity';
import mongoose from 'mongoose';
import { mysqlKnex, sqlServerKnex } from './myDatabaseConfig';
import { user } from './mongoModels';

//TABLES SHORTHANDS
const UserMySQL = () => mysqlKnex('user');
const UserSQLServer = () => sqlServerKnex('user');
const PetMySQL = () => mysqlKnex('user_pet');
const PetSQLServer = () => sqlServerKnex('user_pet');


//CREATE USER AND HIS PET TRANSACTION

//MySQL functions
const createUserOnMySQLTrx = (name, email, trx) =>
  UserMySQL()
    .insert({ name, email }, 'id')
    .transacting(trx);


const createPetOnMySQLTrx = (userId, petName, trx) =>
  PetMySQL()
    .insert({ id_user: userId, name: pet })
    .transacting(trx);


//SQL Server functions
const createUserOnSQLServerTrx = (name, email, trx) =>
  UserMySQL()
    .insert({ name, email }, 'id')
    .transacting(trx);


const createPetOnSQLServerTrx = (userId, petName, trx) =>
  PetMySQL()
    .insert({ id_user: userId, name: pet })
    .transacting(trx);


//MongoDB function
const createUserOnMongoTrx = (userIdMySQL, userIdSQLServer, name, email, session) =>
  user.create(
    [{
      userIdMysql: Number,
      userIdSQLServer: Number,
      lastNameKnown: String,
      lastEmailKnown: String
    }], { session });

//The atomic transaction
const createUser = async (name, email, pet) => {
  const atomic = new Atomicity({ mysql: connectKnexMysql, sqlServer: sqlServerKnex, mongoDb: mongoose });
  const transactionReturnedData = await atomic.transact( async ({ mysqlTrx, sqlserverTrx, mongoSession }) => {

    const userIdMySQL = ( await createUserOnMySQLTrx(name, email, mysqlTrx) )[0];
    const userIdSQLServer = ( await createUserOnSQLServerTrx(name, email, mysqlTrx) )[0];

    await createPetOnMySQLTrx(userIdMySQL, pet, mysqlTrx);
    await createPetOnSQLServerTrx(userIdSQLServer, pet, sqlserverTrx);

    await mongoResume = await createUserOnMongoTrx(userIdMySQL, userIdSQLServer, name, email, mongoSession);

    return mongoResume;

  });
}

And again, just to remember, if any error occurs while executing the callback of the transact method, all changes already made will be reversed.