Comparing version 1.0.4 to 1.0.5
@@ -1,2 +0,2 @@ | ||
import pg from 'pg'; | ||
import { PgType } from './@types/pg'; | ||
import tx from './core'; | ||
@@ -6,2 +6,3 @@ export { Client, ClientBase, Connection, Events, Pool, Query, native, types } from 'pg'; | ||
export * from './core'; | ||
export default pg; | ||
declare const _default: PgType; | ||
export default _default; |
{ | ||
"name": "pgnode", | ||
"version": "1.0.4", | ||
"version": "1.0.5", | ||
"description": "PostgresSQL client to Nodejs servers", | ||
@@ -78,3 +78,3 @@ "main": "lib/index.js", | ||
"@types/node": "16.11.17", | ||
"@types/pg": "8.6.3", | ||
"@types/pg": "^8.6.3", | ||
"@typescript-eslint/eslint-plugin": "5.8.0", | ||
@@ -81,0 +81,0 @@ "@typescript-eslint/parser": "5.8.0", |
161
README.md
@@ -5,3 +5,2 @@ <p align="center"> | ||
<p align="center">PostgresSQL client to Nodejs servers</p> | ||
</p> | ||
@@ -21,2 +20,4 @@ <p align="center"> | ||
</a> | ||
<br /> | ||
<br /> | ||
<a href="https://github.com/hebertcisco/pgnode"> | ||
@@ -28,4 +29,15 @@ <img alt="GitHub release" src="https://img.shields.io/github/release/hebertcisco/pgnode.svg?style=flat&color=336791" /> | ||
</a> | ||
<br /> | ||
<br /> | ||
<a href="https://github.com/hebertcisco/pgnode/actions/workflows/node.js-macos.yml"> | ||
<img alt="Node.js CI on Darwin" src="https://github.com/hebertcisco/pgnode/actions/workflows/node.js-macos.yml/badge.svg" /> | ||
</a> | ||
<a href="https://github.com/hebertcisco/pgnode/actions/workflows/node.js-ubuntu.yml"> | ||
<img alt="Node.js CI on Ubuntu" src="https://github.com/hebertcisco/pgnode/actions/workflows/node.js-ubuntu.yml/badge.svg" /> | ||
</a> | ||
<a href="https://github.com/hebertcisco/pgnode/actions/workflows/node.js-windows.yml"> | ||
<img alt="Node.js CI on Windows" src="https://github.com/hebertcisco/pgnode/actions/workflows/node.js-windows.yml/badge.svg" /> | ||
</a> | ||
<br /> | ||
<br /> | ||
<a href="https://github.com/hebertcisco/musiko-app/issues/new/choose">Report Bug</a> | ||
@@ -42,2 +54,4 @@ <a href="https://github.com/hebertcisco/musiko-app/issues/new/choose">Request Feature</a> | ||
</a> | ||
<br /> | ||
<br /> | ||
<a href="https://www.debian.org/index.pt.html"> | ||
@@ -49,2 +63,10 @@ <img alt="Debian" src="https://img.shields.io/badge/Debian-A81D33?style=for-the-badge&logo=debian&logoColor=white&style=flat" /> | ||
</a> | ||
<br /> | ||
<br /> | ||
<a href="https://www.microsoft.com/pt-br/windows/"> | ||
<img alt="Windows" src="https://img.shields.io/badge/Windows-0078D6?style=for-the-badge&logo=windows&logoColor=white&style=flat" /> | ||
</a> | ||
<a href="https://www.apple.com/br/macos/"> | ||
<img alt="Macos" src="https://img.shields.io/badge/mac%20os-000000?style=for-the-badge&logo=apple&logoColor=white&style=flat" /> | ||
</a> | ||
</p> | ||
@@ -76,3 +98,3 @@ <p align="center">Did you like the project? Please, considerate <a href="https://www.buymeacoffee.com/hebertcisco">a donation</a> to help improve!</p> | ||
```js | ||
const pg = require('pgnode'); | ||
const pg = require("pgnode"); | ||
``` | ||
@@ -83,31 +105,34 @@ | ||
```js | ||
import pg from 'pgnode'; | ||
import pg from "pgnode"; | ||
``` | ||
## Use tx | ||
## Client connection | ||
This is the simplest possible way to connect, query, and disconnect with async/await: | ||
```js | ||
const { Client } = require('pgnode'); | ||
const client = new Client(); | ||
await client.connect(); | ||
const res = await client.query('SELECT $1::text as message', ['Hello world!']); | ||
console.log(res.rows[0].message); // Hello world! | ||
await client.end(); | ||
``` | ||
```ts | ||
import pg, { Client, Pool } from "pgnode"; | ||
And here's the same thing with callbacks: | ||
const config = { | ||
user: process.env.POSTGRES_USER, | ||
host: process.env.POSTGRES_HOST, | ||
database: process.env.POSTGRES_DATABASE, | ||
password: process.env.POSTGRES_PASSWORD, | ||
port: Number(process.env.POSTGRES_PORT), | ||
}; | ||
```js | ||
const { Client } = require('pgnode'); | ||
const client = new Client(); | ||
client.connect(); | ||
client.query('SELECT $1::text as message', ['Hello world!'], (err, res) => { | ||
console.log(err ? err.stack : res.rows[0].message); // Hello World! | ||
client.end(); | ||
}); | ||
const client = new pg.Client({ ...config }); | ||
function query(sql, params) { | ||
return client | ||
.connect() | ||
.then(() => client.query(sql, params)) | ||
.then((res) => { | ||
client.end(); | ||
return res; | ||
}); | ||
} | ||
``` | ||
Our real-world apps are almost always more complicated than that, and I urge you to read on! | ||
# Transactions (tx) | ||
@@ -117,62 +142,42 @@ ## Usage | ||
```Typescript | ||
import { tx } from `pgnode` | ||
import pg from `pgnode` | ||
import {tx, Client, Pool} from 'pgnode'; | ||
const pg = new Pool() | ||
const client = new Client({ | ||
user: process.env.POSTGRES_USER, | ||
host: process.env.POSTGRES_HOST, | ||
database: process.env.POSTGRES_DATABASE, | ||
password: process.env.POSTGRES_PASSWORD, | ||
port: Number(process.env.POSTGRES_PORT) | ||
}); | ||
await tx(pg, async (db) => { | ||
await db.query(`UPDATE accounts SET money = money - 50 WHERE name = 'bob'`) | ||
await db.query(`UPDATE accounts SET money = money + 50 WHERE name = 'alice'`) | ||
}) | ||
const pool = new Pool({...client}); | ||
await tx(pg, async (db) => { | ||
await db.query(`UPDATE accounts SET money = money - 50 WHERE name = 'bob'`) | ||
await db.query(`UPDATE accounts SET money = money + 50 WHERE name = 'debbie'`) | ||
export async function createTable(){ | ||
return await tx(pool, async (db) => { | ||
await db.query(` | ||
CREATE TABLE IF NOT EXISTS test | ||
( | ||
id SERIAL PRIMARY KEY, | ||
name TEXT NOT NULL | ||
);`); | ||
}); | ||
} | ||
// Any errors thrown inside the callback will terminate the transaction | ||
throw new Error(`screw Debbie`) | ||
}) | ||
// or use a generator function to create the transactions | ||
// You can also use it with other packages that use Pool or PoolClient, like pgtyped | ||
import { sql } from '@pgtyped/query' | ||
const updateAccount = sql<IUpdateAccountQuery>` | ||
UPDATE accounts | ||
SET money = momey + $delta | ||
WHERE name = $name | ||
` | ||
await tx(pg, async(db) => { | ||
await udpateAccount.run({ name: 'bob', delta: -50 }) | ||
await udpateAccount.run({ name: 'charlie', delta: 50 }) | ||
}) | ||
``` | ||
However, this approach contains a subtle bug, because the `client` it passes to the callback stays valid after transaction finishes (successfully or not), and can be unknowingly used. In essence, it's a variation of use-after-free bug, but with database clients instead of memory. | ||
Here's a demonstration of code that can trigger this condition: | ||
```Typescript | ||
async function failsQuickly(db: PoolClient) { | ||
await db.query(`This query has an error`) | ||
export function* createTableGenerator(){ | ||
yield tx(pool, async (db) => { | ||
await db.query(` | ||
CREATE TABLE IF NOT EXISTS test | ||
( | ||
id SERIAL PRIMARY KEY, | ||
name TEXT NOT NULL | ||
);`); | ||
}); | ||
// create another transaction | ||
yield tx(pool, async (db) => { | ||
await db.query(` | ||
INSERT INTO test (name) VALUES ('test');`); | ||
}); | ||
} | ||
async function executesSlowly(db: PoolClient) { | ||
// Takes a couple of seconds to complete | ||
await externalApiCall() | ||
// This operation will be executed OUTSIDE of transaction block! | ||
await db.query(` | ||
UPDATE external_api_calls | ||
SET amount = amount + 1 | ||
WHERE service = 'some_service' | ||
`) | ||
} | ||
await tx(pg, async (db) => { | ||
await Promise.all([ | ||
failsQuickly(db), | ||
executesSlowly(db) | ||
]) | ||
}) | ||
``` | ||
@@ -193,1 +198,5 @@ | ||
- make pg.Pool an es6 class | ||
- `pg.Client` and `pg.Pool` are ES6 classes | ||
- Support for `pg.Client.prototype.query` and `pg.Pool.prototype.query` | ||
- Support generator functions | ||
- Support for Nodejs `^v16x` |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 5 instances in 1 package
28514
15
432
194
5