diff --git a/README.md b/README.md
index a69e384b..6a87dd89 100644
--- a/README.md
+++ b/README.md
@@ -14,10 +14,17 @@ $ npm install pg-promise
### 1. Load the library
```javascript
-var pgpLib = require('pg-promise');
+var pgpLib = require('pg-promise'); // loading the library;
```
-### 2. Configure database connection
-Use one of the two ways to specify connection:
+### 2. Initialize the library
+```javascript
+var pgp = pgpLib(/*options*/); // initializing the library, with optional global settings;
+```
+You can pass additional ```options``` parameter when initilizing the library (see chapter Advanced for details).
+
+NOTE: Only one instance of such ```pgp``` object should exist throughout the application.
+### 3. Configure database connection
+Use one of the two ways to specify connection details:
* Configuration object:
```javascript
var cn = {
@@ -35,19 +42,23 @@ var cn = "postgres://username:password@host:port/database";
This library doesn't use any of the connection's details, it simply passes them on to [PG] when opening a new connection.
For more details see [ConnectionParameters] class in [PG], such as additional connection properties supported.
-### 3. Initialize the library
+### 4. Instantiate your database
```javascript
-var pgp = pgpLib(cn);
+var db = new pgp(cn); // create a new database instance based on the connection details
```
-NOTE: Only one global instance should be used throughout the application.
+There can be multiple database objects instantiated in the application from different connection details.
-See also chapter Advanced for the use of parameter ```options``` during initialization. But for now you are ready to use the library.
+You are now ready to make queries against the database.
# Usage
### The basics
In order to eliminate the chances of unexpected query results and make code more robust, each request is parametrized with the expected/supported
Query Result Mask, using type ```queryResult``` as shown below:
```javascript
+///////////////////////////////////////////////////////
+// Query Result Mask flags;
+//
+// Any combination is supported, except for one + many.
queryResult = {
one: 1, // single-row result is expected;
many: 2, // multi-row result is expected;
@@ -56,18 +67,17 @@ queryResult = {
```
In the following generic-query example we indicate that the call can return any number of rows:
```javascript
-pgp.query("select * from users", queryResult.none | queryResult.many);
+db.query("select * from users", queryResult.none | queryResult.many);
```
which is equivalent to calling:
```javascript
-pgp.manyOrNone("select * from users");
+db.manyOrNone("select * from users");
```
-
This usage pattern is facilitated through result-specific methods that can be used instead of the generic query:
```javascript
-pgp.many("select * from users"); // one or more records are expected
-pgp.one("select * from users limit 1"); // one record is expected
-pgp.none("update users set active=TRUE where id=1"); // no records expected
+db.many("select * from users"); // one or more records are expected
+db.one("select * from users limit 1"); // one record is expected
+db.none("update users set active=TRUE where id=1"); // no records expected
```
The mixed-result methods are:
* ```oneOrNone``` - expects 1 or 0 rows to be returned;
@@ -76,7 +86,7 @@ The mixed-result methods are:
Each of the query calls returns a [Promise] object, as shown below, to be used in the standard way.
And when the expected and actual results do not match, the call will be rejected.
```javascript
-pgp.many("select * from users")
+db.many("select * from users")
.then(function(data){
console.log(data); // printing the data returned
}, function(reason){
@@ -89,7 +99,7 @@ In PostgreSQL stored procedures are just functions that usually do not return an
Suppose we want to call function ```findAudit``` to find audit records by user id and maximum timestamp.
We can make such call as shown below:
```javascript
-pgp.func('findAudit', [123, new Date()])
+db.func('findAudit', [123, new Date()])
.then(function(data){
console.log(data); // printing the data returned
}, function(reason){
@@ -99,7 +109,7 @@ pgp.func('findAudit', [123, new Date()])
We passed it user id = 123, plus current Date/Time as the timestamp. We assume that the function signature matches the parameters that we passed.
All values passed are serialized automatically to comply with PostgreSQL type formats.
-And when you are not expecting any return results, call ```pgp.proc``` instead. Both methods return a [Promise] object.
+And when you are not expecting any return results, call ```db.proc``` instead. Both methods return a [Promise] object.
### Transactions
Every call shown in chapters above would acquire a new connection from the pool and release it when done. In order to execute a transaction on the same
@@ -109,7 +119,7 @@ Example:
```javascript
var promise = require('promise');
-var tx = new pgp.tx(); // creating a new transaction object
+var tx = new db.tx(); // creating a new transaction object
tx.exec(function(/*client*/){
@@ -133,7 +143,7 @@ we want both queries inside the transaction to resolve before executing a COM
Notes
* While inside a transaction, we make calls to the same-named methods as outside of transactions, except we do it on the transaction object instance now,
-as opposed to the global ```pgp``` object, which gives us access to the shared connection object. The same goes for calling functions and procedures within
+as opposed to the database object ```db```, which gives us access to the shared connection object. The same goes for calling functions and procedures within
transactions, using ```tx.func``` and ```tx.proc``` accordingly.
* Just for flexibility, the transaction call-back function takes parameter ```client``` - the connection object.
@@ -149,7 +159,7 @@ pgp.as.text(value); // returns proper PostgreSQL text presentation,
pgp.as.date(value); // returns proper PostgreSQL date/time presentation,
// wrapped in quotes.
```
-As these helpers are not associated with a connection, and thus can be called from anywhere.
+As these helpers are not associated with a database, they can be called from anywhere.
# Advanced
@@ -166,7 +176,7 @@ var options = {
console.log("Disconnected from database '" + cp.database + "'");
}
};
-var pgp = pgpLib(cn, options);
+var pgp = pgpLib(options);
```
Two events supported at the moment - ```connect``` and ```disconnect```, to notify of virtual connections being established or released accordingly.
Each event takes parameter ```client```, which is the client connection object. These events are mostly for connection monitoring, while debugging your application.
@@ -185,13 +195,13 @@ doing it for you automatically.
Usage example:
```javascript
-pgp.connect().then(function(db){
+db.connect().then(function(info){
// connection was established successfully;
- // do stuff with the connection object (db.client) and/or queries;
+ // do stuff with the connection object (info.client) and/or queries;
// when done with all the queries, call done():
- db.done();
+ info.done();
}, function(reason){
// failed to connect;
@@ -201,6 +211,7 @@ pgp.connect().then(function(db){
NOTE: When using the direct connection, events ```connect``` and ```disconnect``` won't be fired.
# History
+* Version 0.2.0 introduced on March 6th, 2015, supporting multiple databases
* A refined version 0.1.4 released on March 5th, 2015.
* First solid Beta, 0.1.2 on March 4th, 2015.
* It reached first Beta version 0.1.0 on March 4th, 2015.
diff --git a/index.js b/index.js
index b41e4a95..3281f0f3 100644
--- a/index.js
+++ b/index.js
@@ -6,11 +6,10 @@ var npm = {
pg: require('pg')
};
-//////////////////////////////////////
-// Query result mask flags;
+///////////////////////////////////////////////////////
+// Query Result Mask flags;
//
-// NOTE: Cannot combine one + many, while the
-// rest of combinations are all supported.
+// Any combination is supported, except for one + many.
queryResult = {
one: 1, // single-row result is expected;
many: 2, // multi-row result is expected;
@@ -22,347 +21,391 @@ queryResult = {
//
// Parameters:
//
-// 1. cn (required) - either configuration object or connection string.
-// It is merely passed on to PG and not used by this library.
-// 2. options (optional) -
-// {
-// connect: function(client){
-// on-connect event;
-// client - pg connection object.
-// },
-// disconnect: function(client){
-// on-disconnect event;
-// client - pg connection object.
-// }
+// options (optional) -
+// {
+// connect: function(client){
+// on-connect event;
+// client - pg connection object.
+// },
+// disconnect: function(client){
+// on-disconnect event;
+// client - pg connection object.
// }
-module.exports = function (cn, options) {
+// }
+module.exports = function (options) {
+
+ var lib = function (cn) {
+ if(!$isEmptyObject(this)){
+ // This makes it easy to locate the most common mistake -
+ // skipping keyword 'new' when calling: var db = new pgp(cn);
+ throw new Error("Invalid database object instantiation.");
+ }
+ if (!cn) {
+ throw new Error("Invalid 'cn' parameter passed.");
+ }
+ return dbInit(this, cn, options);
+ };
- if (!cn) {
- throw new Error("Invalid 'cn' parameter passed.");
- }
+ // Exposing PG library instance, just for flexibility.
+ lib.pg = npm.pg;
- // simpler promise instantiation;
- var $p = function (func) {
- return new npm.promise(func);
+ // Terminates pg library; call it when exiting the application.
+ lib.end = function () {
+ npm.pg.end();
};
- var $self = {
+ // Namespace for type conversion helpers;
+ lib.as = {
+ bool: function (val) {
+ if ($isNull(val)) {
+ return 'null';
+ }
+ return val ? 'TRUE' : 'FALSE';
+ },
+ text: function (val) {
+ if ($isNull(val)) {
+ return 'null';
+ }
+ return $wrapText($fixQuotes(val));
+ },
+ date: function (val) {
+ if ($isNull(val)) {
+ return 'null';
+ }
+ if (val instanceof Date) {
+ return $wrapText(val.toUTCString());
+ } else {
+ throw new Error($wrapText(val) + " doesn't represent a valid Date object or value");
+ }
+ }
+ };
- /////////////////////////////////////////////////////////////
- // PG library instance;
- // Exposing it just for flexibility.
- pg: npm.pg,
+ return lib;
+};
- /////////////////////////////////////////////////////////////
- // Connects to the database;
- // The caller must invoke done() after requests are finished.
- connect: function () {
- return $p(function (resolve, reject) {
- npm.pg.connect(cn, function (err, client, done) {
- if (err) {
- reject(err);
- } else {
- resolve({
- client: client,
- done: done
- });
+function dbInit(dbInst, cn, options) {
+
+ // Handles database connection acquire/release
+ // events, notifying the client as needed.
+ function monitor(open, db) {
+ if (open) {
+ if (options) {
+ var func = options.connect;
+ if (func) {
+ if (typeof(func) !== 'function') {
+ throw new Error('Function was expected for options.connect');
}
- });
- });
- },
-
- ///////////////////////////////////////////////////////////////
- // Terminates pg library; call it when exiting the application.
- end: function () {
- npm.pg.end();
- },
+ func(db.client); // notify the client;
+ }
+ }
+ } else {
+ if (options) {
+ var func = options.disconnect;
+ if (func) {
+ if (typeof(func) !== 'function') {
+ throw new Error('Function was expected for options.disconnect');
+ }
+ func(db.client); // notify the client;
+ }
+ }
+ db.done(); // release database connection back to the pool;
+ }
+ }
- //////////////////////////////////////////////////////////////
- // Generic query request;
- // qrm is Query Result Mask, combination of queryResult flags.
- query: function (query, qrm) {
- return $p(function (resolve, reject) {
- $self.connect()
- .then(function (db) {
- _global.monitor(true, db);
- _global.query(db.client, query, qrm)
- .then(function (data) {
- _global.monitor(false, db);
- resolve(data);
- }, function (reason) {
- _global.monitor(false, db);
- reject(reason);
- });
- }, function (reason) {
- reject(reason); // connection failed;
+ /////////////////////////////////////////////////////////////////
+ // Connects to the database;
+ // The caller must invoke done() after all requests are finished.
+ dbInst.connect = function () {
+ return $p(function (resolve, reject) {
+ npm.pg.connect(cn, function (err, client, done) {
+ if (err) {
+ reject(err);
+ } else {
+ resolve({
+ client: client,
+ done: done
});
+ }
});
- },
+ });
+ };
- none: function (query) {
- return this.query(query, queryResult.none);
- },
+ //////////////////////////////////////////////////////////////
+ // Generic query request;
+ // qrm is Query Result Mask, combination of queryResult flags.
+ dbInst.query = function (query, qrm) {
+ return $p(function (resolve, reject) {
+ dbInst.connect()
+ .then(function (db) {
+ monitor(true, db);
+ $query(db.client, query, qrm)
+ .then(function (data) {
+ monitor(false, db);
+ resolve(data);
+ }, function (reason) {
+ monitor(false, db);
+ reject(reason);
+ });
+ }, function (reason) {
+ reject(reason); // connection failed;
+ });
+ });
+ };
- one: function (query) {
- return this.query(query, queryResult.one);
- },
+ dbInst.none = function (query) {
+ return dbInst.query(query, queryResult.none);
+ };
- many: function (query) {
- return this.query(query, queryResult.many);
- },
+ dbInst.one = function (query) {
+ return dbInst.query(query, queryResult.one);
+ };
- oneOrNone: function (query) {
- return this.query(query, queryResult.one | queryResult.none);
- },
+ dbInst.many = function (query) {
+ return dbInst.query(query, queryResult.many);
+ };
- manyOrNone: function (query) {
- return this.query(query, queryResult.many | queryResult.none);
- },
+ dbInst.oneOrNone = function (query) {
+ return dbInst.query(query, queryResult.one | queryResult.none);
+ };
- func: function (funcName, params) {
- return this.one(_global.createFuncQuery(funcName, params));
- },
+ dbInst.manyOrNone = function (query) {
+ return dbInst.query(query, queryResult.many | queryResult.none);
+ };
- proc: function (procName, params) {
- return this.oneOrNone(_global.createFuncQuery(procName, params));
- },
+ dbInst.func = function (funcName, params, qrm) {
+ var query = $createFuncQuery(funcName, params);
+ if (qrm) {
+ return dbInst.query(query);
+ } else {
+ return dbInst.one(query);
+ }
+ };
- // Namespace for type conversion helpers;
- as: {
- bool: function (val) {
- if (_global.isNull(val)) {
- return 'null';
- }
- return val ? 'TRUE' : 'FALSE';
+ dbInst.proc = function (procName, params) {
+ return dbInst.oneOrNone($createFuncQuery(procName, params));
+ };
+
+ //////////////////////////
+ // Transaction class;
+ dbInst.tx = function () {
+
+ var tx = this;
+
+ if(!$isEmptyObject(tx)){
+ // This makes it easy to locate the most common mistake -
+ // skipping keyword 'new' when calling: var tx = new db.tx(cn);
+ throw new Error("Invalid transaction object instantiation.");
+ }
+
+ var local = {
+ db: null,
+ start: function (db) {
+ this.db = db;
+ monitor(true, db);
},
- text: function (val) {
- if (_global.isNull(val)) {
- return 'null';
- }
- return _global.wrapText(_global.fixQuotes(val));
+ finish: function () {
+ monitor(false, this.db);
+ this.db = null;
},
- date: function (val) {
- if (_global.isNull(val)) {
- return 'null';
+ call: function (cb) {
+ if (typeof(cb) !== 'function') {
+ return npm.promise.reject("Cannot invoke tx.exec() without a callback function.");
}
- if (val instanceof Date) {
- return _global.wrapText(val.toUTCString());
+ var result = cb(this.db.client);
+ if (result && typeof(result.then) === 'function') {
+ return result;
} else {
- throw new Error(_global.wrapText(val) + " doesn't represent a valid Date object or value");
+ return npm.promise.reject("Callback function passed into tx.exec() didn't return a valid promise object.");
}
}
- },
+ };
- // Transaction class;
- tx: function () {
-
- var tx = this;
-
- var _local = {
- db: null,
- start: function (db) {
- this.db = db;
- _global.monitor(true, db);
- },
- finish: function () {
- _global.monitor(false, this.db);
- this.db = null;
- },
- call: function (cb) {
- if (typeof(cb) !== 'function') {
- return npm.promise.reject("Cannot invoke tx.exec() without a callback function.");
- }
- var result = cb(this.db.client);
- if (result && typeof(result.then) === 'function') {
- return result;
- } else {
- return npm.promise.reject("Callback function passed into tx.exec() didn't return a valid promise object.");
- }
- }
- };
-
- tx.exec = function (cb) {
- if (_local.db) {
- throw new Error("Previous call to tx.exec() hasn't finished.");
- }
- var t_data, t_reason, success;
- return $p(function (resolve, reject) {
- $self.connect()
- .then(function (db) {
- _local.start(db);
- return tx.query('begin', queryResult.none);
- }, function (reason) {
- reject(reason); // connection issue;
- })
- .then(function () {
- _local.call(cb)
- .then(function (data) {
- t_data = data;
- success = true;
- return tx.query('commit', queryResult.none);
- }, function (reason) {
- t_reason = reason;
- success = false;
- return tx.query('rollback', queryResult.none);
- })
- .then(function () {
- _local.finish();
- // either commit or rollback successfully executed;
- if (success) {
- resolve(t_data);
- } else {
- reject(t_reason);
- }
- }, function (reason) {
- // either commit or rollback failed;
- _local.finish();
- reject(reason);
- });
- }, function (reason) {
- _local.finish();
- reject(reason); // issue with 'begin' command;
- });
- });
- };
+ // Executes transaction;
+ tx.exec = function (cb) {
+ if (local.db) {
+ throw new Error("Previous call to tx.exec() hasn't finished.");
+ }
+ var t_data, t_reason, success;
+ return $p(function (resolve, reject) {
+ dbInst.connect()
+ .then(function (db) {
+ local.start(db);
+ return tx.query('begin', queryResult.none);
+ }, function (reason) {
+ reject(reason); // connection issue;
+ })
+ .then(function () {
+ local.call(cb)
+ .then(function (data) {
+ t_data = data;
+ success = true;
+ return tx.query('commit', queryResult.none);
+ }, function (reason) {
+ t_reason = reason;
+ success = false;
+ return tx.query('rollback', queryResult.none);
+ })
+ .then(function () {
+ local.finish();
+ // either commit or rollback successfully executed;
+ if (success) {
+ resolve(t_data);
+ } else {
+ reject(t_reason);
+ }
+ }, function (reason) {
+ // either commit or rollback failed;
+ local.finish();
+ reject(reason);
+ });
+ }, function (reason) {
+ local.finish();
+ reject(reason); // issue with 'begin' command;
+ });
+ });
+ };
- tx.query = function (query, qrm) {
- if (!_local.db) {
- throw new Error('Unexpected call outside of transaction');
- }
- return _global.query(_local.db.client, query, qrm);
- };
+ tx.query = function (query, qrm) {
+ if (!local.db) {
+ throw new Error('Unexpected call outside of transaction');
+ }
+ return $query(local.db.client, query, qrm);
+ };
- tx.none = function (query) {
- return tx.query(query, queryResult.none);
- };
+ tx.none = function (query) {
+ return tx.query(query, queryResult.none);
+ };
- tx.one = function (query) {
- return tx.query(query, queryResult.one);
- };
+ tx.one = function (query) {
+ return tx.query(query, queryResult.one);
+ };
- tx.many = function (query) {
- return tx.query(query, queryResult.many);
- };
+ tx.many = function (query) {
+ return tx.query(query, queryResult.many);
+ };
- tx.oneOrNone = function (query) {
- return tx.query(query, queryResult.one | queryResult.none);
- };
+ tx.oneOrNone = function (query) {
+ return tx.query(query, queryResult.one | queryResult.none);
+ };
- tx.manyOrNone = function (query) {
- return tx.query(query, queryResult.many | queryResult.none);
- };
+ tx.manyOrNone = function (query) {
+ return tx.query(query, queryResult.many | queryResult.none);
+ };
- tx.func = function (funcName, params) {
- return tx.one(_global.createFuncQuery(funcName, params));
- };
+ tx.func = function (funcName, params, qrm) {
+ var query = $createFuncQuery(funcName, params);
+ if (qrm) {
+ return tx.query(query);
+ } else {
+ return tx.one(query);
+ }
+ };
- tx.proc = function (procName, params) {
- return tx.oneOrNone(_global.createFuncQuery(procName, params));
- };
- }
+ tx.proc = function (procName, params) {
+ return tx.oneOrNone($createFuncQuery(procName, params));
+ };
};
+}
- var _global = {
- options: options,
- isNull: function (val) {
- return typeof(val) === 'undefined' || val === null;
- },
- fixQuotes: function (val) {
- return val.replace("'", "''");
- },
- wrapText: function (text) {
- return "'" + text + "'";
- },
- wrapValue: function (val) {
- if (this.isNull(val)) {
- return 'null';
- }
- switch (typeof(val)) {
- case 'string':
- return $self.as.text(val);
- case 'boolean':
- return $self.as.bool(val);
- default:
- if (val instanceof Date) {
- return $self.as.date(val);
- } else {
- return val;
- }
- }
- },
- monitor: function (open, db) {
- if (open) {
- if (this.options) {
- var func = this.options.connect;
- if (func) {
- if (typeof(func) !== 'function') {
- throw new Error('Function was expected for options.connect');
- }
- func(db.client);
- }
- }
+////////////////////////////////////////////////
+// Global, reusable functions, all start with $
+
+// Simpler promise instantiation;
+function $p(func) {
+ return new npm.promise(func);
+}
+
+// Null verification;
+function $isNull(val) {
+ return typeof(val) === 'undefined' || val === null;
+}
+
+// Checks if the object is empty (has no properties);
+function $isEmptyObject(obj){
+ return Object.keys(obj).length === 0;
+}
+
+// Fixes single-quote symbols in text fields;
+function $fixQuotes(val) {
+ return val.replace("'", "''");
+}
+
+// Wraps up text in single quotes;
+function $wrapText(text) {
+ return "'" + text + "'";
+}
+
+// Translates a javascript value into its text presentation,
+// according to the type, compatible with PostgreSQL format.
+function $wrapValue(val) {
+ if ($isNull(val)) {
+ return 'null';
+ }
+ switch (typeof(val)) {
+ case 'string':
+ return dbInst.as.text(val);
+ case 'boolean':
+ return dbInst.as.bool(val);
+ default:
+ if (val instanceof Date) {
+ return dbInst.as.date(val);
} else {
- if (this.options) {
- var func = this.options.disconnect;
- if (func) {
- if (typeof(func) !== 'function') {
- throw new Error('Function was expected for options.disconnect');
- }
- func(db.client);
- }
- }
- db.done();
+ return val;
}
- },
- formatValues: function (values) {
- var s = '';
- if (Array.isArray(values) && values.length > 0) {
- for (var i = 0; i < values.length; i++) {
- if (i > 0) {
- s += ',';
- }
- s += this.wrapValue(values[i]);
- }
+ }
+}
+
+// Formats array of javascript-type values into a list of
+// parameters for a function call, compatible with PostgreSQL.
+function $formatValues(values) {
+ var s = '';
+ if (Array.isArray(values) && values.length > 0) {
+ for (var i = 0; i < values.length; i++) {
+ if (i > 0) {
+ s += ',';
}
- return s;
- },
- createFuncQuery: function (funcName, params) {
- return 'select * from ' + funcName + '(' + this.formatValues(params) + ');';
- },
- query: function (client, query, qrm) {
- return $p(function (resolve, reject) {
- var badMask = queryResult.one | queryResult.many;
- if ((qrm & badMask) === badMask) {
- reject("Invalid query result mask: one + many");
+ s += $wrapValue(values[i]);
+ }
+ }
+ return s;
+}
+
+// Formats a proper function call from the parameters.
+function $createFuncQuery(funcName, params) {
+ return 'select * from ' + funcName + '(' + $formatValues(params) + ');';
+}
+
+// Generic, static query call for the specified connection + query + result.
+function $query(client, query, qrm) {
+ return $p(function (resolve, reject) {
+ var badMask = queryResult.one | queryResult.many;
+ if ((qrm & badMask) === badMask) {
+ reject("Invalid query result mask: one + many");
+ } else {
+ client.query(query, function (err, result) {
+ if (err) {
+ reject(err.message);
} else {
- client.query(query, function (err, result) {
- if (err) {
- reject(err.message);
+ var data = result.rows;
+ var l = result.rows.length;
+ if (l) {
+ if (l > 1 && !(qrm & queryResult.many)) {
+ reject("Single row was expected from query: '" + query + "'");
} else {
- var data = result.rows;
- var l = result.rows.length;
- if (l) {
- if (l > 1 && !(qrm & queryResult.many)) {
- reject("Single row was expected from query: '" + query + "'");
- } else {
- if (!(qrm & queryResult.many)) {
- data = result.rows[0];
- }
- }
- } else {
- if (qrm & queryResult.none) {
- data = null;
- } else {
- reject("No rows returned from query: '" + query + "'");
- }
+ if (!(qrm & queryResult.many)) {
+ data = result.rows[0];
}
- resolve(data);
}
- });
+ } else {
+ if (qrm & queryResult.none) {
+ data = null;
+ } else {
+ reject("No rows returned from query: '" + query + "'");
+ }
+ }
+ resolve(data);
}
});
}
- };
-
- return $self;
-};
+ });
+}
diff --git a/package.json b/package.json
index 07beb3bf..6b7cdc98 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "pg-promise",
- "version": "0.1.5",
+ "version": "0.2.0",
"description": "PG + Promise made easy, with transactions support.",
"main": "index.js",
"scripts": {
@@ -15,10 +15,10 @@
"url": "https://github.com/vitaly-t/pg-promise/issues"
},
"keywords": [
- "postgresql",
"pg",
+ "promise",
"transaction",
- "promise"
+ "postgresql"
],
"author": {
"name": "Vitaly Tomilov",