simple-postgres
simple-postgres is a small and powerful PostgreSQL interface for Node.js
Replace all of your database boilerplate with import db from 'simple-postgres'
and never look back.
Getting started
npm install simple-postgres
let accountName = 'ACME\'; DELETE FROM accounts; --' // this is totally safeawait db // this is also totally safelet account = await dbrow` SELECT * FROM accounts WHERE name = ` console // => 'ACME\'; DELETE FROM accounts; --'
Why?
Many other postgres modules are bad. This one is good. Here's why:
simple-postgres has everything you need
- connects using the DATABASE_URL environment variable
- runs queries and returns the results
- automatic query parameterization
- escaping literals, identifiers, arrays
- transactions
- async/await ready
- sets application_name using package.json
- good test coverage
- trusted in production by my boss who trusts nothing
simple-postgres doesn't have anything you don't need
- no ORM
- no query builder
- no connect function
- no disconnect function
- no connection pool manager
- no configuration
- no initialization
- no callbacks
API
db.query(sql, params = [])
run a query
returns a promise, which resolves with a pg Result object
This is best for INSERT/UPDATE/DELETE/etc queries which will not return any rows. If you are doing a SELECT, you probably want one of the functions below.
let result = await dbconsole // => UPDATE 2
db.rows(sql, params = [])
run a query
returns a promise, which resolves with an array of row objects
let accounts = await dbrows'SELECT * FROM accounts'for let account of accounts console // => "1: ACME"
db.row(sql, params = [])
run a query
returns a promise, which resolves with the first row object
This will not automatically add LIMIT 1
to the end of the query.
let account = await dbconsole // => "ACME"
db.value(sql, params = [])
run a query
returns a promise, which resolves with the first column of the first row
This is useful for things like counts.
let accountName = await dbvalue'SELECT name FROM accounts WHERE id = 1'console // => "ACME"
db.column(sql, params = [])
run a query
returns a promise, which resolves with an array of the first values in each row
Example:
let oneThroughFive = await dbconsole // => [1, 2, 3, 4, 5]
template string mode
Any of the above functions can be used with template string literals to make
long queries more readable. Interpolated values will be moved to the params
array and replaced with $1, $2, etc.
Example:
let type = 'pancake'// the following two calls are identical:dbvalue` SELECT COUNT(*) FROM breakfast WHERE type = `dbvalue'SELECT COUNT(*) FROM breakfast WHERE type = $1' type
Do not use parentheses around your template string or you will open yourself up to SQL injection attacks and you will have a bad day.
let type = 'pancake \'; DELETE FROM accounts; --'// NOTE THE PARENTHESES AROUND THE BACKTICKS - DO NOT DO THISdbvalue` SELECT COUNT(*) FROM breakfast WHERE type = `
If you need to interpolate an identifier such as a table name, the normal
escaping will wrap your value in single quotes. Use the db.identifier
function
instead.
Example:
let table = 'breakfast'let type = 'pancake' dbvalue` SELECT COUNT(*) FROM WHERE type = `
db.template`SELECT ${a}...`
Prepare a statement for later execution. This is good for testing functions that dynamically generate SQL.
let accountName = 'ACME'let tableName = 'users' let subquery = dbtemplate` SELECT id FROM accounts WHERE name = `let query = dbtemplate` SELECT a, b FROM WHERE account_id IN ()` let results = await dbrowsquery// [{a: , b: }, {a: , b: }, ...] let rawSql = query// SELECT a, b FROM "users" WHERE account_id IN (SELECT id FROM accounts WHERE name='ACME')
db.transaction(block)
perform a database transaction
block: should be a function which will perform work inside the transaction and return a promise. If the promise rejects, the transaction will be rolled back.
returns a promise, which should resolve with the return value of block or reject if the transaction failed
Example:
// process one orderdb
db.connection(block)
perform multiple queries sequentially on a single connection
block: should be a function which will perform work inside the connection and return a promise. When the promise resolves or rejects, the connection will be returned to the pool.
Example:
let cookies = await db
Query cancellation
The promises returned by db.query
, db.rows
, etc all have a cancel
method
which will kill the query on the backend.
Example:
let query = db query q // STDOUT:// query cancelled// cancel resolved
An obscure note about cancellation: db.connection
and db.transaction
do not
have .cancel()
methods, although you can cancel individual queries you run
within them.
db.escape(value)
alias of db.escapeLiteral
escape a value for safe use in SQL queries, returns string
While this function is tested and probably secure, you should avoid using it. Instead, use bind vars, as they are much more difficult to mess up.
db.escapeIdentifier(value)
escape a value for safe use as an identifier in SQL queries, returns string
Same as the above function, except for things like table names, column names, etc.
db.escapeLiterals(values, separator = ', ')
escape an array of literals and join them with the given separator, returns string
db === "'a', 'b', 'c'"
db.escapeIdentifiers(values, separator = ', ')
escape an array of identifiers and join them with the given separator, returns string
db === '"a", "b", "c"'
db.identifier(value)
escapes an identifier in such a way that it can be passed safely into a template query, returns object
Below, note the lack of parentheses around the SQL, with db.query being called as a template function.
let tableName = 'potentially "dangerous" table name'dbquery` SELECT * FROM `
db.identifiers(values, separator = ', ')
escapes multiple identifiers in such a way that they can be passed safely into a template query, returns object
let columns = 'id' 'name'dbquery` SELECT FROM accounts`
db.literals(values, separator = ', ')
escapes multiple literals in such a way that they can be passed safely into a template query, returns object
let accounts = 1 2 3dbquery` SELECT id FROM accounts WHERE name IN()`
db.items(values, separator = ', ')
escapes multiple items in such a way that they can be passed safely into a template query, returns object. Escapes literals by default, but allows identifiers and templates.
let defaultTitle = 'untitled'let select = 'test string' db dbtemplate`COALESCE(title, ) AS title` let books = await dbrows` SELECT FROM books`/*SELECT 'test string', "id", COALESCE(title, 'untitled') AS titleFROM books*/
db.setErrorHandler(callback)
sets a callback for otherwise unhandled errors such as dropped connections and other mysteries
Contributing
Please send pull requests!