{"_id":"any-db-transaction","_rev":"3018669","name":"any-db-transaction","description":"Transaction object for Any-DB adapters","dist-tags":{"latest":"2.3.0"},"maintainers":[{"name":"grncdr","email":"glurgle@gmail.com"}],"time":{"modified":"2023-11-06T06:32:45.000Z","created":"2013-12-22T00:01:57.403Z","2.3.0":"2020-12-01T11:41:31.302Z","2.2.2":"2014-10-17T14:54:57.887Z","2.2.1":"2014-05-21T12:02:53.899Z","2.2.0":"2014-05-20T21:05:30.213Z","2.1.3":"2014-03-14T01:17:24.318Z","2.1.2":"2014-01-15T16:48:49.790Z","2.1.1":"2014-01-05T01:25:47.025Z","2.1.0":"2013-12-26T18:34:50.222Z","0.0.1":"2013-12-22T00:01:57.403Z"},"users":{},"author":{"name":"Stephen Sugden","email":"me@stephensugden.com"},"repository":{"type":"git","url":"git://github.com/grncdr/node-any-db-transaction.git"},"versions":{"2.3.0":{"name":"any-db-transaction","version":"2.3.0","description":"Transaction object for Any-DB adapters","main":"transaction.js","dependencies":{"inherits":"~2.0.1","once":"~1.3.0","yafsm":"0.0.0"},"devDependencies":{"any-db":"^2.2.1","any-db-adapter-spec":"2.3.0","any-db-fake":"2.3.0","any-db-mysql":"2.3.0","any-db-postgres":"2.3.0","any-db-sqlite3":"2.3.0","assert-in-order":"0.0.1","covert":"~0.2.0","tape":"~2.3.2"},"scripts":{"test":"tape tests/*.js","covert":"covert tests/*.js"},"repository":{"type":"git","url":"git://github.com/grncdr/node-any-db-transaction.git"},"keywords":["any-db","transaction","sql"],"author":{"name":"Stephen Sugden","email":"me@stephensugden.com"},"license":"BSD-2-Clause","bugs":{"url":"https://github.com/grncdr/node-any-db-transaction/issues"},"homepage":"https://github.com/grncdr/node-any-db-transaction#readme","_id":"any-db-transaction@2.3.0","_nodeVersion":"14.13.0","_npmVersion":"6.14.8","dist":{"shasum":"bb2e37b89a2ce434b6f99e6f018b1f575fdb40af","size":9136,"noattachment":false,"key":"/any-db-transaction/-/any-db-transaction-2.3.0.tgz","tarball":"http://registry.cnpm.dingdandao.com/any-db-transaction/download/any-db-transaction-2.3.0.tgz"},"_npmUser":{"name":"grncdr","email":"glurgle@gmail.com"},"directories":{},"maintainers":[{"name":"grncdr","email":"glurgle@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/any-db-transaction_2.3.0_1606822891166_0.46447099390976"},"_hasShrinkwrap":false,"publish_time":1606822891302,"_cnpm_publish_time":1606822891302,"_cnpmcore_publish_time":"2021-12-18T22:43:34.851Z"},"2.2.2":{"name":"any-db-transaction","version":"2.2.2","description":"Transaction object for Any-DB adapters","main":"transaction.js","dependencies":{"inherits":"~2.0.1","yafsm":"0.0.0","once":"~1.3.0"},"devDependencies":{"any-db-postgres":"~2.1.0","any-db-sqlite3":"~2.1.0","any-db-mysql":"~2.1.0","tape":"~2.3.2","any-db-fake":"~0.0.3","any-db-adapter-spec":"~2.1.0","assert-in-order":"0.0.1","covert":"~0.2.0"},"scripts":{"test":"tape tests/*.js","covert":"covert tests/*.js"},"repository":{"type":"git","url":"git://github.com/grncdr/node-any-db-transaction.git"},"keywords":["any-db","transaction","sql"],"author":{"name":"Stephen Sugden","email":"me@stephensugden.com"},"license":"BSD-2-Clause","bugs":{"url":"https://github.com/grncdr/node-any-db-transaction/issues"},"gitHead":"d6fe8c58a0adb984e4330ae1668e00dba9657fab","homepage":"https://github.com/grncdr/node-any-db-transaction","_id":"any-db-transaction@2.2.2","_shasum":"6e3e14edf5faa65cbd11b7292da9d0d527c2af59","_from":".","_npmVersion":"1.4.28","_npmUser":{"name":"grncdr","email":"glurgle@gmail.com"},"maintainers":[{"name":"grncdr","email":"glurgle@gmail.com"}],"dist":{"shasum":"6e3e14edf5faa65cbd11b7292da9d0d527c2af59","size":9249,"noattachment":false,"key":"/any-db-transaction/-/any-db-transaction-2.2.2.tgz","tarball":"http://registry.cnpm.dingdandao.com/any-db-transaction/download/any-db-transaction-2.2.2.tgz"},"directories":{},"publish_time":1413557697887,"_hasShrinkwrap":false,"_cnpm_publish_time":1413557697887,"_cnpmcore_publish_time":"2021-12-18T22:43:35.051Z"},"2.2.1":{"name":"any-db-transaction","version":"2.2.1","description":"Transaction object for Any-DB adapters","main":"transaction.js","dependencies":{"inherits":"~2.0.1","yafsm":"0.0.0","once":"~1.3.0"},"devDependencies":{"any-db-postgres":"~2.1.0","any-db-sqlite3":"~2.1.0","any-db-mysql":"~2.1.0","tape":"~2.3.2","any-db-fake":"~0.0.3","any-db-adapter-spec":"~2.1.0","assert-in-order":"0.0.1","covert":"~0.2.0"},"scripts":{"test":"tape tests/*.js","covert":"covert tests/*.js"},"repository":{"type":"git","url":"git://github.com/grncdr/node-any-db-transaction.git"},"keywords":["any-db","transaction","sql"],"author":{"name":"Stephen Sugden","email":"me@stephensugden.com"},"license":"BSD-2-Clause","bugs":{"url":"https://github.com/grncdr/node-any-db-transaction/issues"},"homepage":"https://github.com/grncdr/node-any-db-transaction","_id":"any-db-transaction@2.2.1","_shasum":"757144588b67cbabde8e14e6f7d4534e6257b30d","_from":".","_npmVersion":"1.4.8","_npmUser":{"name":"grncdr","email":"glurgle@gmail.com"},"maintainers":[{"name":"grncdr","email":"glurgle@gmail.com"}],"dist":{"shasum":"757144588b67cbabde8e14e6f7d4534e6257b30d","size":9214,"noattachment":false,"key":"/any-db-transaction/-/any-db-transaction-2.2.1.tgz","tarball":"http://registry.cnpm.dingdandao.com/any-db-transaction/download/any-db-transaction-2.2.1.tgz"},"directories":{},"publish_time":1400673773899,"_hasShrinkwrap":false,"_cnpm_publish_time":1400673773899,"_cnpmcore_publish_time":"2021-12-18T22:43:35.261Z"},"2.2.0":{"name":"any-db-transaction","version":"2.2.0","description":"Transaction object for Any-DB adapters","main":"transaction.js","dependencies":{"inherits":"~2.0.1","yafsm":"0.0.0","once":"~1.3.0"},"devDependencies":{"any-db-postgres":"~2.1.0","any-db-sqlite3":"~2.1.0","any-db-mysql":"~2.1.0","tape":"~2.3.2","any-db-fake":"~0.0.3","any-db-adapter-spec":"~2.1.0","assert-in-order":"0.0.1","covert":"~0.2.0"},"scripts":{"test":"tape tests/*.js","covert":"covert tests/*.js"},"repository":{"type":"git","url":"git://github.com/grncdr/node-any-db-transaction.git"},"keywords":["any-db","transaction","sql"],"author":{"name":"Stephen Sugden","email":"me@stephensugden.com"},"license":"BSD-2-Clause","bugs":{"url":"https://github.com/grncdr/node-any-db-transaction/issues"},"_id":"any-db-transaction@2.2.0","dist":{"shasum":"c87e56abe75ff3fe3ea479608fa0205fe263dcbc","size":8646,"noattachment":false,"key":"/any-db-transaction/-/any-db-transaction-2.2.0.tgz","tarball":"http://registry.cnpm.dingdandao.com/any-db-transaction/download/any-db-transaction-2.2.0.tgz"},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"grncdr","email":"glurgle@gmail.com"},"maintainers":[{"name":"grncdr","email":"glurgle@gmail.com"}],"directories":{},"publish_time":1400619930213,"_hasShrinkwrap":false,"_cnpm_publish_time":1400619930213,"_cnpmcore_publish_time":"2021-12-18T22:43:35.493Z"},"2.1.3":{"name":"any-db-transaction","version":"2.1.3","description":"Transaction object for Any-DB adapters","main":"transaction.js","dependencies":{"inherits":"~2.0.1","yafsm":"0.0.0","once":"~1.3.0"},"devDependencies":{"any-db-postgres":"~2.1.0","any-db-sqlite3":"~2.1.0","any-db-mysql":"~2.1.0","tape":"~2.3.2","any-db-fake":"~0.0.3","any-db-adapter-spec":"~2.1.0","assert-in-order":"0.0.1","covert":"~0.2.0"},"scripts":{"test":"tape tests/*.js","covert":"covert tests/*.js"},"repository":{"type":"git","url":"git://github.com/grncdr/node-any-db-transaction.git"},"keywords":["any-db","transaction","sql"],"author":{"name":"Stephen Sugden","email":"me@stephensugden.com"},"license":"BSD-2-Clause","bugs":{"url":"https://github.com/grncdr/node-any-db-transaction/issues"},"_id":"any-db-transaction@2.1.3","dist":{"shasum":"652c0ef955c9480d3ef0c14292453924e1d490bd","size":8236,"noattachment":false,"key":"/any-db-transaction/-/any-db-transaction-2.1.3.tgz","tarball":"http://registry.cnpm.dingdandao.com/any-db-transaction/download/any-db-transaction-2.1.3.tgz"},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"grncdr","email":"glurgle@gmail.com"},"maintainers":[{"name":"grncdr","email":"glurgle@gmail.com"}],"directories":{},"publish_time":1394759844318,"_hasShrinkwrap":false,"_cnpm_publish_time":1394759844318,"_cnpmcore_publish_time":"2021-12-18T22:43:35.729Z"},"2.1.2":{"name":"any-db-transaction","version":"2.1.2","description":"Transaction object for Any-DB adapters","main":"transaction.js","dependencies":{"inherits":"~2.0.1","yafsm":"0.0.0","once":"~1.3.0"},"devDependencies":{"any-db-postgres":"~2.1.0","any-db-sqlite3":"~2.1.0","any-db-mysql":"~2.1.0","tape":"~2.3.2","any-db-fake":"~0.0.3","any-db-adapter-spec":"~2.1.0","assert-in-order":"0.0.1","covert":"~0.2.0"},"scripts":{"test":"tape tests/*.js","covert":"covert tests/*.js"},"repository":{"type":"git","url":"git://github.com/grncdr/node-any-db-transaction.git"},"keywords":["any-db","transaction","sql"],"author":{"name":"Stephen Sugden","email":"me@stephensugden.com"},"license":"BSD-2-Clause","bugs":{"url":"https://github.com/grncdr/node-any-db-transaction/issues"},"readmeFilename":"README.md","_id":"any-db-transaction@2.1.2","dist":{"shasum":"4b2cff3fe4bcada9ef40974b362f46de459dfa53","size":7919,"noattachment":false,"key":"/any-db-transaction/-/any-db-transaction-2.1.2.tgz","tarball":"http://registry.cnpm.dingdandao.com/any-db-transaction/download/any-db-transaction-2.1.2.tgz"},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"grncdr","email":"glurgle@gmail.com"},"maintainers":[{"name":"grncdr","email":"glurgle@gmail.com"}],"directories":{},"publish_time":1389804529790,"_hasShrinkwrap":false,"_cnpm_publish_time":1389804529790,"_cnpmcore_publish_time":"2021-12-18T22:43:35.945Z"},"2.1.1":{"name":"any-db-transaction","version":"2.1.1","description":"Transaction object for Any-DB adapters","main":"transaction.js","dependencies":{"inherits":"~2.0.1","yafsm":"0.0.0","once":"~1.3.0"},"devDependencies":{"any-db-postgres":"~2.1.0","any-db-sqlite3":"~2.1.0","any-db-mysql":"~2.1.0","tape":"~2.3.2","any-db-fake":"~0.0.3","any-db-adapter-spec":"~2.1.0","assert-in-order":"0.0.1","covert":"~0.2.0"},"scripts":{"test":"tape tests/*.js","covert":"covert tests/*.js"},"repository":{"type":"git","url":"git://github.com/grncdr/node-any-db-transaction.git"},"keywords":["any-db","transaction","sql"],"author":{"name":"Stephen Sugden","email":"me@stephensugden.com"},"license":"BSD-2-Clause","bugs":{"url":"https://github.com/grncdr/node-any-db-transaction/issues"},"readmeFilename":"README.md","_id":"any-db-transaction@2.1.1","dist":{"shasum":"a46ed948f08972b25005dc450681323db775ecc9","size":7910,"noattachment":false,"key":"/any-db-transaction/-/any-db-transaction-2.1.1.tgz","tarball":"http://registry.cnpm.dingdandao.com/any-db-transaction/download/any-db-transaction-2.1.1.tgz"},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"grncdr","email":"glurgle@gmail.com"},"maintainers":[{"name":"grncdr","email":"glurgle@gmail.com"}],"directories":{},"publish_time":1388885147025,"_hasShrinkwrap":false,"_cnpm_publish_time":1388885147025,"_cnpmcore_publish_time":"2021-12-18T22:43:36.159Z"},"2.1.0":{"name":"any-db-transaction","version":"2.1.0","description":"Transaction object for Any-DB adapters","main":"transaction.js","dependencies":{"inherits":"~2.0.1","yafsm":"0.0.0","once":"~1.3.0"},"devDependencies":{"any-db-postgres":"~2.1.0","any-db-sqlite3":"~2.1.0","any-db-mysql":"~2.1.0","tape":"~2.3.2","any-db-fake":"~0.0.3","any-db-adapter-spec":"~2.1.0","assert-in-order":"0.0.1","covert":"~0.2.0"},"scripts":{"test":"tape tests/*.js","covert":"covert tests/*.js"},"repository":{"type":"git","url":"git://github.com/grncdr/node-any-db-transaction.git"},"keywords":["any-db","transaction","sql"],"author":{"name":"Stephen Sugden","email":"me@stephensugden.com"},"license":"BSD-2-Clause","bugs":{"url":"https://github.com/grncdr/node-any-db-transaction/issues"},"readmeFilename":"README.md","_id":"any-db-transaction@2.1.0","dist":{"shasum":"be0ec1202ccfdad70704914e7a6afc930cb18056","size":7896,"noattachment":false,"key":"/any-db-transaction/-/any-db-transaction-2.1.0.tgz","tarball":"http://registry.cnpm.dingdandao.com/any-db-transaction/download/any-db-transaction-2.1.0.tgz"},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"grncdr","email":"glurgle@gmail.com"},"maintainers":[{"name":"grncdr","email":"glurgle@gmail.com"}],"directories":{},"publish_time":1388082890222,"_hasShrinkwrap":false,"_cnpm_publish_time":1388082890222,"_cnpmcore_publish_time":"2021-12-18T22:43:36.395Z"},"0.0.1":{"name":"any-db-transaction","version":"0.0.1","description":"Transaction object for Any-DB adapters","main":"transaction.js","dependencies":{"inherits":"~2.0.1","yafsm":"0.0.0"},"devDependencies":{},"scripts":{"test":"node test.js"},"repository":{"type":"git","url":"git://github.com/grncdr/node-any-db-transaction.git"},"keywords":["any-db","transaction","sql"],"author":{"name":"Stephen Sugden","email":"me@stephensugden.com"},"license":"BSD-2-Clause","bugs":{"url":"https://github.com/grncdr/node-any-db-transaction/issues"},"readmeFilename":"README.md","_id":"any-db-transaction@0.0.1","dist":{"shasum":"393df6a5642c7b40fd5aa24210d79b4ee7585c83","size":5186,"noattachment":false,"key":"/any-db-transaction/-/any-db-transaction-0.0.1.tgz","tarball":"http://registry.cnpm.dingdandao.com/any-db-transaction/download/any-db-transaction-0.0.1.tgz"},"_from":".","_npmVersion":"1.3.11","_npmUser":{"name":"grncdr","email":"glurgle@gmail.com"},"maintainers":[{"name":"grncdr","email":"glurgle@gmail.com"}],"directories":{},"publish_time":1387670517403,"_hasShrinkwrap":false,"_cnpm_publish_time":1387670517403,"_cnpmcore_publish_time":"2021-12-18T22:43:36.623Z"}},"readme":"# any-db-transaction\n\n[![Build Status](https://travis-ci.org/grncdr/node-any-db-transaction.png)](https://travis-ci.org/grncdr/node-any-db-transaction)\n\nA simple transaction helper for [any-db][] compliant database adapters.\n\n## Synopsis\n\n```javascript\nvar anyDB = require('any-db')\nvar begin = require('any-db-transaction')\n\nvar connection = anyDB.createConnection(...)\n\n// Callback-style\nbegin(connection, function (err, transaction) {\n  if (err) return console.error(err)\n  // Do work using transaction\n  transaction.query(...)\n  transaction.commit()\n})\n\n// Synchronous-style*\nvar transaction = begin(connection)\ntransaction.on('error', console.error)\ntransaction.query(...)\ntransaction.commit()\n\n// Or use a connection pool\nvar pool = anyDB.createPool(...)\nvar transaction = begin(pool)\n```\n\n## API\n\n```ocaml\nmodule.exports := begin(Queryable, statement: String?, Continuation<Transaction>?) => Transaction\n\nTransaction := FSM & Queryable & {\n  commit:   (Continuation?) => void\n  rollback: (Continuation?) => void\n}\n```\n\n### begin\n\n```ocaml\nmodule.exports := begin(Queryable, statement: String?, Continuation<Transaction>?) => Transaction\n```\n\nTransaction objects are are simple wrappers around a [Connection][] that also\nimplement the [Queryable][] API, but guarantee that all queries take place\nwithin a single database transaction or not at all. Note that `begin` also\nunderstands how to acquire (and release) a connection from a [ConnectionPool][]\nas well, so you can simply pass a pool to it: `var tx = begin(pool)`\n\nBy default, any queries that error during a transaction will cause an automatic rollback.\nIf a query has no callback, the transaction will also handle (and re-emit)\n`'error'` events for the [Query][] instance. This enables handling errors for\nan entire transaction in a single place.\n\nTransactions may also be nested by passing a `Transaction` to `begin` and these\nnested transactions can safely error and rollback without rolling back their\nparent transaction:\n\n```javascript\nvar parent = begin(connection)\nvar child = begin(parent)\nchild.query('some invalid sql')\nchild.on('error', function() {\n  parent.query('select 1') // parent still works\n})\n```\n\nThis feature relies on the `SAVEPOINT` support in your database. (In particular\nMySQL will doesn't have good support in very old versions). The use of\nsavepoints also means there is no option to replace the statement used to begin\nthe child transaction.\n\nWhile the child transaction is in progress the parent transaction will queue any\nqueries it receives until the child transaction either commits or rolls back, at\nwhich point it will process the queue. Be careful: it's quite possible to write\ncode that deadlocks by waiting for a query in the parent transaction before\ncommitting the child transaction. For example:\n\n```javascript\n// Do not do this! it will deadlock!\n\nvar parent = begin(connection) // starts the transaction\nvar child = begin(parent) // creates a savepoint\n\nparent.query('SELECT 1', function(err) {\n  child.commit()\n})\n```\n\n#### Automatic Rollback on Error\n\nAs stated previously, by default any queries that error during a transaction\nwill cause an automatic rollback. This is to support the common pattern in which\na transaction is a series of queries you either want to succeed or fail\natomically.\n\nThere is another common pattern for transactions where you either create or\nupdate a record. Many databases support an `INSERT OR REPLACE` statement, but\nquite often you'd like an `INSERT OR UPDATE` construct instead.\n\nIntuitively, a transaction can be used for this as well:\n\n1.  Start a transaction\n1.  Try an insert statement\n\n- If that succeeds, commit.\n- Otherwise, continue\n\n1.  Try an update statement.\n\n- If that succeeds, commit.\n- Otherwise, roll back the transaction.\n\nA transaction is unlikely to be the best choice here. The results of the first\nstatement need to make it back to the client before it can decide whether to\ncommit or try something else. Usually databases better support this kind of\nconstruct with nested queries, which avoid those roundtrips.\n\nTo facilitate this kind transaction use, automatic rollback of transactions\ncan be disabled.\n\n```javascript\nvar tx = begin(conn, { autoRollback: false })\ntx.query('Query that produces errors', function(err) {\n  tx.query('another query')\n})\n```\n\n**Note**: PostgreSQL does not allow you to use a transaction immediately after\nan error. However, you can get much the same behaviour by explicitly adding\n`SAVEPOINT` statements. A transaction with an error can be rolled back to a\nknown good savepoint, and can be used from there onwards. You can achieve the\nsame by using nested transactions.\n\n```javascript\nvar tx = begin(conn, { autoRollback: false })\nvar sp = begin(tx)\nsp.query('query that might fail', function(err) {\n  if (err) {\n    tx.query('alternate queries')\n  } else {\n    sp.commit()\n  }\n})\n```\n\nNote that the failing query is performed on the \"savepoint\" child transaction,\nbut the final query is perfomed on the outer/parent transaction.\n\n### Transaction states\n\nTransactions are [FSM][] instances with 4 states: `disconnected`,\n`connected`, `open`, and `closed`:\n\n    [disconnected]\n          ↓\n     [connected]\n       ↓  ↓  ↑\n       ↓ [open]\n       ↓   ↓\n      [closed]\n\nEvery transaction starts out in the `disconnected` state, in which it will queue\nall tasks (queries, child transactions, commits and rollbacks) in the order they\nare received.\n\nOnce the transaction acquires a connection\\* it will transition to the\n`connected` state and begin processing it's internal task queue. While in this\nstate any new tasks will still be added to the end of the queue. There are two\npossible transitions from the `connected` state:\n\n- `connected → open` - When all queued tasks have finished.\n- `connected → closed` - When a rollback or commit is encountered in the queue.\n  This includes automatic rollbacks caused by query errors.\n\n`closed` is a terminal state in which all further database operations result in\nerrors. (The errors will either be sent to any callback provided or emitted as\n`error` events on the next tick).\n\nIn the `open` state, all database operations will be performed immediately. If\na child transaction is started like `var child = begin(parentTxn)`, the parent\ntransaction will move back into the `connected` state (queueing any queries it\nreceives) until the child completes, at which point it will resume processing\nit's own internal queue.\n\nTransactions created from a [Connection][] transition to `connected` before\n[begin][] returns.\n\n### Transaction.adapter\n\nThe [Adapter](https://github.com/grncdr/node-any-db-adapter-spec/#adapter) instance used by the resource (connection or parent transaction) underlying this transaction.\n\n### Transaction.query\n\n```ocaml\n(text: String, params: Array?, Continuation<Result>?) => Query\n```\n\nMaintains the same contract as [Queryable.query][] but adds further guarantees\nthat queries will be performed within the transaction or not at all. If the\ntransaction has been committed or rolled back this method will fail by passing\nan error to the continuation (if provided) or emitting an `'error'` event.\n\n### Transaction.commit\n\n```ocaml\n(Continuation<void>) => void\n```\n\nIssue a `COMMIT` (or `RELEASE ...` in the case of nested transactions) statement\nto the database. If a continuation is provided it will be called (possibly with\nan error) after the `COMMIT` statement completes. The transaction object itself\nwill be unusable after calling `commit()`.\n\n### Transaction.rollback\n\n```ocaml\n(Continuation<void>) => void\n```\n\nThe same as [Transaction.commit](#transactioncommit) but issues a `ROLLBACK`.\nAgain, the transaction will be unusable after calling this method.\n\n### Transaction events\n\n- `'query', query` - emitted immediately after `.query` is called on a\n  connection via `tx.query`. The argument is a [query](#query) object.\n- `'commit:start'` - Emitted when `.commit()` is called.\n- `'commit:complete'` - Emitted after the transaction has committed.\n- `'rollback:start'` - Emitted when `.rollback()` is called.\n- `'rollback:complete'` - Emitted after the transaction has rolled back.\n- `'close'` - Emitted after `rollback` or `commit` completes.\n- `'error', err` - Emitted under three conditions:\n\n  1.  There was an error acquiring a connection.\n  2.  Any query performed in this transaction emits an error that would otherwise\n      go unhandled.\n  3.  Any of `query`, `begin`, `commit`, or `rollback` are called after the\n      connection has already been committed or rolled back.\n\n  Note that the `'error'` event **may be emitted multiple times!** depending on\n  the callback you are registering, you way want to wrap it using [once][].\n\n## Examples\n\n### Unit-of-work middleware\n\nA common pattern in web applications is start a transaction for each request and\ncommit it before sending a response. Here is a simplified [connect][] middleware\nthat encapsulates this pattern:\n\n```javascript\nmodule.exports = function unitOfWorkMiddleware(pool, errorHandler) {\n  return function(req, res, next) {\n    req.tx = pool.begin()\n    // intercept writeHead to ensure we have completed our transaction before\n    // responding to the user\n    var writeHead = res.writeHead\n    res.writeHead = function() {\n      var args = arguments\n\n      if (req.tx.state() != 'closed') {\n        req.tx.commit(function(err) {\n          if (err) {\n            errorHandler(req, res, err)\n          } else {\n            writeHead.apply(res, args)\n          }\n        })\n      } else {\n        writeHead.apply(res, args)\n      }\n    }\n    next()\n  }\n}\n```\n\n### Rolling back\n\nHere's an example where we stream all of our user ids, check them against an\nexternal abuse-monitoring service, and flag or delete users as necessary, if\nfor any reason we only get part way through, the entire transaction is rolled\nback and nobody is flagged or deleted:\n\n```javascript\nvar pool = require('any-db').createPool(...)\n\n// this is our external service\nvar abuseService = require('./services').abuseService()\n\nvar tx = begin(pool)\ntx.on('error', finished)\n\n/*\nWhy query with the pool and not the transaction?\nBecause it allows the transaction queries to begin executing immediately,\nrather than queueing them all up behind the initial SELECT.\n*/\npool.query('SELECT id FROM users')\n  .on('data', function (user) {\n    if (tx.state() == 'closed') {\n      // Do not make unneccessary requests\n      return\n    }\n    abuseService.checkUser(user.id, function (err, result) {\n      if (err) return tx.handleError(err)\n      // Errors from these queries will propagate up to the transaction object\n      if (result.flag) {\n        tx.query('UPDATE users SET abuse_flag = 1 WHERE id = $1', [user.id])\n      } else if (result.destroy) {\n        tx.query('DELETE FROM users WHERE id = $1', [user.id])\n      }\n    })\n  }).on('end', function () {\n    tx.commit(finished)\n  })\n\nfunction finished (err) {\n  if (err) console.error(err)\n  else console.log('All done!')\n}\n```\n\n# License\n\n2-clause BSD\n\n[any-db]: https://github.com/grncdr/node-any-db\n[begin]: #begin\n[connection]: https://github.com/grncdr/node-any-db-adapter-spec#connection\n[queryable]: https://github.com/grncdr/node-any-db-adapter-spec#queryable\n[queryable.query]: https://github.com/grncdr/node-any-db-adapter-spec#queryablequery\n[query]: https://github.com/grncdr/node-any-db-adapter-spec#query\n[connectionpool]: https://github.com/grncdr/node-any-db-pool#connectionpool\n[connect]: https://npm.im/connect\n","_attachments":{},"homepage":"https://github.com/grncdr/node-any-db-transaction#readme","bugs":{"url":"https://github.com/grncdr/node-any-db-transaction/issues"},"license":"BSD-2-Clause"}