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

electron-tx

v1.0.5

Published

NodeJS based system to enforce atomic transactions for multiple asychronous actions

Downloads

7

Readme

Electron Logo

Electron

Electron is a NodeJS based system to enforce atomic transactions for multiple asynchronous interactions. Electron works to ensure that each async (or sync) stage of multi step operation is successful and if any of the stages fails it rolls backs automatically to the previous state of the application.

Lets break this down in plain english! You are building an API which needs to call the Google Maps API, Store the data in your database, and then finally process a users payment for their transaction. This operation has multiple steps and if any one of them fails your will be in a sticky situation as a developer! Can you imagine if you processed a users payment but your code failed to update the database saying that the user paid you so you never actually deliver the product to a user!

With electron we aim to solve this problem by implementing a fully transactional system which processes each stage (calling Google, updating db, processing payment) sequentially and if any one of the stages fails electron will automatically rollback so you never have a half updated database!

Getting Started

These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system.

Prerequisites

In order to run this software, Node JS is needed. You can download Node JS which comes pre bundled with NPM by following this link.

Installing

You can install this software via npm by using

npm install electron-tx --save

or by including the source files in your project. The entry point to the library is the index.js file. For example: const Electron = require('my/project/path/electron/index.js');

Try out electron with a simple example!

const Electron = require('electron-tx');

const transaction = new Electron();

transaction.addStage('My First Stage!', {
    up: () => {
       console.log('Trying to do some async action here!'); 
    },
    down: () => {
        console.log('Dont worry I can undo the async action!');
    }
});

transaction.execute()
    .then(() => {
        console.log('The transaction was successful!');
    })
    .catch(err => {
        console.log('There was some issue in one of the stages :(', err);
    });

Electron Stages

An electron transaction consists of a series of stages. Each stage should be completely independent of other operations in the application and should have all the data necessary to make decisions and perform its action to completion.

Stages take two parameters a unique name which identifies the stage and the stage configuration object. A stage is defined using the addStage() function and when the stage executes it calls the up() function that is defined in the stage configuration.

Stages are run one at a time and will only proceed to the next stage if the previous stage succeeds. If any of the stages fails electron will automatically iterate through all the previous stages and roll back the changes that were applied by using the down() method. You can think of the down() function as the command + Z of the operation!

It is up to the developer to provide the implementation for both applying a change (with up()) and reverting a change (with down()).

You can return any type of data to the up/down methods (including async actions and promises) as they will be wrapped in promises under the hood.

For example:

const stripe = require('stripe');
const AWS = require('aws-sdk');
const request = require('request-promise-native');

const Electron = require('electron-tx');
const transaction = new Electron();

transaction.addStage('Get Customers', {
    up: () => {
         return stripe.customers.find('cus_1234')
    },
    down: () => {
      ...  
    },
});

transaction.addStage('Update Database', {
    up: () => {
        // We can return promises to these 
        return AWS.DynamoDB.put(...).promise()
    },
    down: () => {
        return AWS.DynamoDB.delete(...).promise()
    }
});

transaction.addStage('Call API', {
   up: () => {
       return request.get(...)
   },
   down: () => {
       return true; // Nothing to undo
   }
});

transaction.execute();

Running transaction.execute() will start executing all the stages one by one in the order in which they were added.

Stage Inter-dependencies & Data Flow

You may be wondering about interdependencies between stages and passing data between them. What happens when my second stage depends on data generated by my first stage? For this you will need to shift your mindset from a synchronous way of writing code to that of callbacks and Promises. The following example won't work and is the incorrect way to pass data between stages.

const stripe = require('stripe');
const AWS = require('aws-sdk');

const Electron = require('electron-tx');
const transaction = new Electron();


let customerId = null;

transaction.addStage('get_customers', {
    up: (data) => {
         console.log(data); // null (this is the first stage there is no data)
         const customer = stripe.customers.find('cus_1234');
         
         // This is bad and will not work as expected
         customerId = customer.id;
         return true;
    },
    down: () => ...,
});

transaction.addStage('update_database', {
    up: (data) => {
        console.log(data); // { 'get_customers': true }
        // Throws Error cannot update customerId of null expected String
        return AWS.DynamoDB.update(customerId).promise()
        
        // or
        
        // Throws error Cannot update customerId of true expected String
        return AWS.DynamoDB.update(data.get_customers).promise();
        
    },
    down: () => {
        return AWS.DynamoDB.delete(...).promise()
    }
});

transaction.execute();

Luckily for us Electron automatically passes returned values from stage to stage keyed by the stages name! Each stage will have the output of the resolved promise returned to the up() function of all the previous stages. Check out the following (correct) example

const stripe = require('stripe');
const AWS = require('aws-sdk');

const Electron = require('electron-tx');
const transaction = new Electron();

transaction.addStage('get_customers', {
    up: (data) => {
         console.log(data); // null (this is the first stage there is no data)
         return stripe.customers.find('cus_1234')
    },
    down: () => {
      ...  
    },
});

transaction.addStage('update_database', {
    up: (data) => {
        console.log(data); // { 'get_customers': { ... data returned from promise } }
        return AWS.DynamoDB.update(data['get_customers'].customerId).promise()
    },
    down: () => {
        return AWS.DynamoDB.delete(...).promise()
    }
});

transaction.execute();

Note: The down() method does not have access to all the previous stages data. It only has access to the data produced by its respective up method.

transaction.addStage('update_database', {
    up: () => {
        return AWS.DynamoDB.update(...).promise()
    },
    down: (data) => {
        console.log(data); // { Item: { ... } } only the result of this stages up() call
        return AWS.DynamoDB.delete(...).promise()
    }
});

Event Hooks

Electron comes with event hooks on a per stage basis. You can trigger events using the before() and after() functions within the stage configuration. See the following example of using event hooks. The before hook is always called but the after hook is only called if the stage was successful.

transaction.addStage('update_database', {
    before: (data) => {
       console.log('Attempting to update database!');
       console.log(data); // { prev_stage_1: {}, prev_stage_2: {}, etc...}
    },
    up: () => true,
    down: () => true,
    after: () => {
      console.log('Database update successful!')  
    },
});

Running the tests

Automated tests for this system can be run using the command

npm test

Deployment

Coming Soon

Built With

  • NodeJS - The server side library used
  • Javascript - Programming language used
  • Lodash - A modern JavaScript utility library delivering modularity, performance & extras.
  • NPM - Dependency Management

Contributing

Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull requests to us.

Versioning

We use SemVer for versioning. For the versions available, see the tags on this repository.

Authors

  • Christian Bartram - Creator & Developer - cbartram

See also the list of contributors who participated in this project.

License

This project is licensed under the MIT License - see the LICENSE.md file for details