diff --git a/docs/.gitignore b/docs/.gitignore index a680367e..2b3533c7 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1 +1,2 @@ .next +out diff --git a/docs/components/alert.tsx b/docs/components/alert.tsx new file mode 100644 index 00000000..7bf2237c --- /dev/null +++ b/docs/components/alert.tsx @@ -0,0 +1,10 @@ +import React from 'react' +import { Callout } from 'nextra-theme-docs' + +export const Alert = ({ children }) => { + return ( + + {children} + + ) +} diff --git a/docs/components/info.tsx b/docs/components/info.tsx new file mode 100644 index 00000000..a61e17fb --- /dev/null +++ b/docs/components/info.tsx @@ -0,0 +1,6 @@ +import React from 'react' +import { Callout } from 'nextra-theme-docs' + +export const Info = ({ children }) => { + return {children} +} diff --git a/docs/package.json b/docs/package.json index 907a3c42..dec5cceb 100644 --- a/docs/package.json +++ b/docs/package.json @@ -4,15 +4,16 @@ "description": "", "main": "next.config.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "start": "next dev", + "build": "next build && next export" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "next": "^12.3.1", - "nextra": "^1.1.0", - "nextra-theme-docs": "^1.2.6", + "nextra": "2.0.0-beta.29", + "nextra-theme-docs": "2.0.0-beta.29", "react": "^17.0.1", "react-dom": "^17.0.1" } diff --git a/docs/pages/_app.js b/docs/pages/_app.js index 3e7701bf..19532b06 100644 --- a/docs/pages/_app.js +++ b/docs/pages/_app.js @@ -1,5 +1,9 @@ import 'nextra-theme-docs/style.css' export default function Nextra({ Component, pageProps }) { - return + return ( + <> + + + ) } diff --git a/docs/pages/meta.json b/docs/pages/_meta.json similarity index 79% rename from docs/pages/meta.json rename to docs/pages/_meta.json index 5fc5a7ea..dd241d88 100644 --- a/docs/pages/meta.json +++ b/docs/pages/_meta.json @@ -1,5 +1,5 @@ { "index": "Welcome", "announcements": "Announcements", - "api": "API" + "apis": "API" } diff --git a/docs/pages/announcements.mdx b/docs/pages/announcements.mdx index 4ad22464..6fec81ca 100644 --- a/docs/pages/announcements.mdx +++ b/docs/pages/announcements.mdx @@ -1,81 +1,83 @@ +import { Alert } from '/components/alert.tsx' + ## 2020-02-25 ### pg@8.0 release `pg@8.0` is [being released](https://github.com/brianc/node-postgres/pull/2117) which contains a handful of breaking changes. -I will outline each breaking change here and try to give some historical context on them. Most of them are small and subtle and likely wont impact you; __however__, there is one larger breaking change you will likely run into: +I will outline each breaking change here and try to give some historical context on them. Most of them are small and subtle and likely wont impact you; **however**, there is one larger breaking change you will likely run into: -___ +--- -#### Support all `tls.connect` [options](https://nodejs.org/api/tls.html#tls_tls_connect_options_callback) being passed to the client/pool constructor under the `ssl` option. +- Support all `tls.connect` [options](https://nodejs.org/api/tls.html#tls_tls_connect_options_callback) being passed to the client/pool constructor under the `ssl` option. -Previously we white listed the parameters passed here and did slight massaging of some of them. The main __breaking__ change here is that now if you do this: +Previously we white listed the parameters passed here and did slight massaging of some of them. The main **breaking** change here is that now if you do this: -``` +```js const client = new Client({ ssl: true }) ``` -
-Now we will use the default ssl options to tls.connect which includes rejectUnauthorized being enabled. This means your connection attempt may fail if you are using a self-signed cert. To use the old behavior you should do this: -
+ + Now we will use the default ssl options to tls.connect which includes rejectUnauthorized being enabled. This means + your connection attempt may fail if you are using a self-signed cert. To use the old behavior you should do this: + -``` +```js const client = new Client({ ssl: { rejectUnauthorized: false } }) ``` This makes pg a bit more secure "out of the box" while still enabling you to opt in to the old behavior. -___ +--- The rest of the changes are relatively minor & you likely wont need to do anything, but good to be aware none the less! -#### change default database name +- change default database name If a database name is not specified, available in the environment at `PGDATABASE`, or available at `pg.defaults`, we used to use the username of the process user as the name of the database. Now we will use the `user` property supplied to the client as the database name, if it exists. What this means is this: -``` +```jsx new Client({ - user: 'foo' + user: 'foo', }) ``` `pg@7.x` will default the database name to the _process_ user. `pg@8.x` will use the `user` property supplied to the client. If you have not supplied `user` to the client, and it isn't available through any of its existing lookup mechanisms (environment variables, pg.defaults) then it will still use the process user for the database name. +- drop support for versions of node older than 8.0 -#### drop support for versions of node older than 8.0 +Node@6.0 has been out of LTS for quite some time now, and I've removed it from our test matrix. `pg@8.0` _may_ still work on older versions of node, but it isn't a goal of the project anymore. Node@8.0 is actually no longer in the LTS support line, but pg will continue to test against and support 8.0 until there is a compelling reason to drop support for it. Any security vulnerability issues which come up I will back-port fixes to the `pg@7.x` line and do a release, but any other fixes or improvments will not be back ported. -Node@6.0 has been out of LTS for quite some time now, and I've removed it from our test matrix. `pg@8.0` _may_ still work on older versions of node, but it isn't a goal of the project anymore. Node@8.0 is actually no longer in the LTS support line, but pg will continue to test against and support 8.0 until there is a compelling reason to drop support for it. Any security vulnerability issues which come up I will back-port fixes to the `pg@7.x` line and do a release, but any other fixes or improvments will not be back ported. +- prevent password from being logged accidentally -#### prevent password from being logged accidentally +`pg@8.0` makes the password field on the pool and client non-enumerable. This means when you do `console.log(client)` you wont have your database password printed out unintenionally. You can still do `console.log(client.password)` if you really want to see it! -`pg@8.0` makes the password field on the pool and client non-enumerable. This means when you do `console.log(client)` you wont have your database password printed out unintenionally. You can still do `console.log(client.password)` if you really want to see it! +- make `pg.native` non-enumerable -#### make `pg.native` non-enumerable +You can use `pg.native.Client` to access the native client. The first time you access the `pg.native` getter it imports the native bindings...which must be installed. In some cases (such as webpacking the pg code for lambda deployment) the `.native` property would be traversed and trigger an import of the native bindings as a side-effect. Making this property non-enumerable will fix this issue. An easy fix, but its technically a breaking change in cases where people _are_ relying on this side effect for any reason. -You can use `pg.native.Client` to access the native client. The first time you access the `pg.native` getter it imports the native bindings...which must be installed. In some cases (such as webpacking the pg code for lambda deployment) the `.native` property would be traversed and trigger an import of the native bindings as a side-effect. Making this property non-enumerable will fix this issue. An easy fix, but its technically a breaking change in cases where people _are_ relying on this side effect for any reason. +- make `pg.Pool` an es6 class -#### make `pg.Pool` an es6 class +This makes extending `pg.Pool` possible. Previously it was not a "proper" es6 class and `class MyPool extends pg.Pool` wouldn't work. -This makes extending `pg.Pool` possible. Previously it was not a "proper" es6 class and `class MyPool extends pg.Pool` wouldn't work. +- make `Notice` messages _not_ an instance of a JavaScript error -#### make `Notice` messages _not_ an instance of a JavaScript error +The code path for parsing `notice` and `error` messages from the postgres backend is the same. Previously created a JavaScript `Error` instance for _both_ of these message types. Now, only actual `errors` from the postgres backend will be an instance of an `Error`. The _shape_ and _properties_ of the two messages did not change outside of this. -The code path for parsing `notice` and `error` messages from the postgres backend is the same. Previously created a JavaScript `Error` instance for _both_ of these message types. Now, only actual `errors` from the postgres backend will be an instance of an `Error`. The _shape_ and _properties_ of the two messages did not change outside of this. +- monorepo -#### monorepo +While not technically a breaking change for the module itself, I have begun the process of [consolidating](https://github.com/brianc/node-pg-query-stream) [separate](https://github.com/brianc/node-pg-cursor/) [repos](https://github.com/brianc/node-pg-pool) into the main [repo](https://github.com/brianc/node-postgres) and converted it into a monorepo managed by lerna. This will help me stay on top of issues better (it was hard to bounce between 3-4 separate repos) and coordinate bug fixes and changes between dependant modules. -While not technically a breaking change for the module itself, I have begun the process of [consolidating](https://github.com/brianc/node-pg-query-stream) [separate](https://github.com/brianc/node-pg-cursor/) [repos](https://github.com/brianc/node-pg-pool) into the main [repo](https://github.com/brianc/node-postgres) and converted it into a monorepo managed by lerna. This will help me stay on top of issues better (it was hard to bounce between 3-4 separate repos) and coordinate bug fixes and changes between dependant modules. +Thanks for reading that! pg tries to be super pedantic about not breaking backwards-compatibility in non semver major releases....even for seemingly small things. If you ever notice a breaking change on a semver minor/patch release please stop by the [repo](https://github.com/brianc/node-postgres) and open an issue! -Thanks for reading that! pg tries to be super pedantic about not breaking backwards-compatibility in non semver major releases....even for seemingly small things. If you ever notice a breaking change on a semver minor/patch release please stop by the [repo](https://github.com/brianc/node-postgres) and open an issue! - -_If you find `pg` valuable to you or your business please consider [supporting](http://github.com/sponsors/brianc) it's continued development! Big performance improvements, typescript, better docs, query pipelining and more are all in the works!_ +_If you find `pg` valuable to you or your business please consider [supporting](http://github.com/sponsors/brianc) it's continued development! Big performance improvements, typescript, better docs, query pipelining and more are all in the works!_ ## 2019-07-18 ### New documentation -After a _very_ long time on my todo list I've ported the docs from my old hand-rolled webapp running on route53 + elb + ec2 + dokku (I know, I went overboard!) to [gatsby](https://www.gatsbyjs.org/) hosted on [netlify](https://www.netlify.com/) which is _so_ much easier to manage. I've released the code at [https://github.com/brianc/node-postgres-docs](https://github.com/brianc/node-postgres-docs) and invite your contributions! Let's make this documentation better together. Any time changes are merged to master on the documentation repo it will automatically deploy. +After a _very_ long time on my todo list I've ported the docs from my old hand-rolled webapp running on route53 + elb + ec2 + dokku (I know, I went overboard!) to [gatsby](https://www.gatsbyjs.org/) hosted on [netlify](https://www.netlify.com/) which is _so_ much easier to manage. I've released the code at [https://github.com/brianc/node-postgres-docs](https://github.com/brianc/node-postgres-docs) and invite your contributions! Let's make this documentation better together. Any time changes are merged to master on the documentation repo it will automatically deploy. If you see an error in the docs, big or small, use the "edit on github" button to edit the page & submit a pull request right there. I'll get a new version out ASAP with your changes! If you want to add new pages of documentation open an issue if you need guidance, and I'll help you get started. diff --git a/docs/pages/apis/_meta.json b/docs/pages/apis/_meta.json new file mode 100644 index 00000000..0b6a193c --- /dev/null +++ b/docs/pages/apis/_meta.json @@ -0,0 +1,7 @@ +{ + "client": "pg.Client", + "pool": "pg.Pool", + "result": "pg.Result", + "types": "pg.Types", + "cursor": "Cursor" +} diff --git a/docs/pages/api/2-client.mdx b/docs/pages/apis/client.mdx similarity index 91% rename from docs/pages/api/2-client.mdx rename to docs/pages/apis/client.mdx index e54241fe..c983859b 100644 --- a/docs/pages/api/2-client.mdx +++ b/docs/pages/apis/client.mdx @@ -1,16 +1,15 @@ --- title: pg.Client -slug: /api/client --- -## constructor +## new Client -### new Client([config: object]) +`new Client(config: Config)` Every field of the `config` object is entirely optional. A `Client` instance will use [environment variables](/features/connecting#environment-variables) for all missing values. -```flow -config = { +```ts +type Config = { user?: string, // default process.env.PGUSER || process.env.USER password?: string or function, //default process.env.PGPASSWORD host?: string, // default process.env.PGHOST @@ -49,7 +48,7 @@ Calling `client.connect` with a callback: ```js const { Client } = require('pg') const client = new Client() -client.connect(err => { +client.connect((err) => { if (err) { console.error('connection error', err.stack) } else { @@ -68,7 +67,7 @@ const client = new Client() client .connect() .then(() => console.log('connected')) - .catch(err => console.error('connection error', err.stack)) + .catch((err) => console.error('connection error', err.stack)) ``` _note: connect returning a promise only available in pg@7.0 or above_ @@ -79,10 +78,10 @@ _note: connect returning a promise only available in pg@7.0 or above_ Passing query text, optional query parameters, and a callback to `client.query` results in a type-signature of: -```flow +```ts client.query( text: string, - values?: Array, + values?: Array, callback: (err: Error, result: Result) => void ) => void ``` @@ -119,10 +118,10 @@ client.query('SELECT $1::text as name', ['brianc'], (err, res) => { If you call `client.query` with query text and optional parameters but **don't** pass a callback, then you will receive a `Promise` for a query result. -```flow +```ts client.query( text: string, - values?: Array + values?: Array ) => Promise ``` @@ -134,8 +133,8 @@ const client = new Client() client.connect() client .query('SELECT NOW()') - .then(result => console.log(result)) - .catch(e => console.error(e.stack)) + .then((result) => console.log(result)) + .catch((e) => console.error(e.stack)) .then(() => client.end()) ``` @@ -147,8 +146,8 @@ const client = new Client() client.connect() client .query('SELECT $1::text as name', ['brianc']) - .then(result => console.log(result)) - .catch(e => console.error(e.stack)) + .then((result) => console.log(result)) + .catch((e) => console.error(e.stack)) .then(() => client.end()) ``` @@ -158,13 +157,13 @@ client You can pass an object to `client.query` with the signature of: -```flow -interface QueryConfig { +```ts +type QueryConfig { // the raw query text text: string; // an array of query parameters - values?: Array; + values?: Array; // name of the query - used for prepared statements name?: string; @@ -212,10 +211,10 @@ const query = { // promise client .query(query) - .then(res => { + .then((res) => { console.log(res.rows) // ['brianc'] }) - .catch(e => { + .catch((e) => { console.error(e.stack) }) ``` @@ -232,13 +231,13 @@ const result = client.query(query) assert(query === result) // true -query.on('row', row => { +query.on('row', (row) => { console.log('row!', row) // { name: 'brianc' } }) query.on('end', () => { console.log('query done') }) -query.on('error', err => { +query.on('error', (err) => { console.error(err.stack) }) ``` @@ -252,7 +251,7 @@ query.on('error', err => { Disconnects the client from the PostgreSQL server. ```js -client.end(err => { +client.end((err) => { console.log('client has disconnected') if (err) { console.log('error during disconnection', err.stack) @@ -268,7 +267,7 @@ Calling end without a callback yields a promise: client .end() .then(() => console.log('client has disconnected')) - .catch(err => console.error('error during disconnection', err.stack)) + .catch((err) => console.error('error during disconnection', err.stack)) ``` _note: end returning a promise is only available in pg7.0 and above_ @@ -283,7 +282,7 @@ When the client is in the process of connecting, dispatching a query, or disconn const client = new pg.Client() client.connect() -client.on('error', err => { +client.on('error', (err) => { console.error('something bad has happened!', err.stack) }) @@ -300,9 +299,9 @@ When the client disconnects from the PostgreSQL server it will emit an end event Used for `listen/notify` events: -```flow +```ts type Notification { - processId: int, + processId: number, channel: string, payload?: string } @@ -314,7 +313,7 @@ client.connect() client.query('LISTEN foo') -client.on('notification', msg => { +client.on('notification', (msg) => { console.log(msg.channel) // foo console.log(msg.payload) // bar! }) @@ -327,5 +326,5 @@ client.query(`NOTIFY foo, 'bar!'`) Used to log out [notice messages](https://www.postgresql.org/docs/9.6/static/plpgsql-errors-and-messages.html) from the PostgreSQL server. ```js -client.on('notice', msg => console.warn('notice:', msg)) +client.on('notice', (msg) => console.warn('notice:', msg)) ``` diff --git a/docs/pages/api/5-cursor.mdx b/docs/pages/apis/cursor.mdx similarity index 98% rename from docs/pages/api/5-cursor.mdx rename to docs/pages/apis/cursor.mdx index aa4b7817..c4a6928c 100644 --- a/docs/pages/api/5-cursor.mdx +++ b/docs/pages/apis/cursor.mdx @@ -35,8 +35,8 @@ cursor.read(100, (err, rows) => { }) ``` -``` -interface CursorQueryConfig { +```ts +type CursorQueryConfig { // by default rows come out as a key/value pair for each row // pass the string 'array' here to receive rows as an array of values rowMode?: string; diff --git a/docs/pages/api/1-pool.mdx b/docs/pages/apis/pool.mdx similarity index 52% rename from docs/pages/api/1-pool.mdx rename to docs/pages/apis/pool.mdx index 26b492aa..6ebc1904 100644 --- a/docs/pages/api/1-pool.mdx +++ b/docs/pages/apis/pool.mdx @@ -1,31 +1,36 @@ --- title: pg.Pool -slug: /api/pool --- -## constructor +import { Alert } from '/components/alert.tsx' -### new Pool([config: object]) +## new Pool -Every field of the `config` object is entirely optional. The config passed to the pool is also passed to every client instance within the pool when the pool creates that client. +```ts +new Pool(config: Config) +``` -```flow -config = { +Constructs a new pool instance. + +The pool is initially created empty and will create new clients lazily as they are needed. Every field of the `config` object is entirely optional. The config passed to the pool is also passed to every client instance within the pool when the pool creates that client. + +```ts +type Config = { // all valid client config options are also valid here // in addition here are the pool specific configuration parameters: // number of milliseconds to wait before timing out when connecting a new client // by default this is 0 which means no timeout - connectionTimeoutMillis?: int, + connectionTimeoutMillis?: number // number of milliseconds a client must sit idle in the pool and not be checked out // before it is disconnected from the backend and discarded // default is 10000 (10 seconds) - set to 0 to disable auto-disconnection of idle clients - idleTimeoutMillis?: int, + idleTimeoutMillis?: number // maximum number of clients the pool should contain // by default this is set to 10. - max?: int, + max?: number // Default behavior is the pool will keep clients open & connected to the backend // until idleTimeoutMillis expire for each client and node will maintain a ref @@ -54,11 +59,62 @@ const pool = new Pool({ }) ``` +## pool.query + +Often we only need to run a single query on the database, so as convenience the pool has a method to run a query on the first available idle client and return its result. + +`pool.query() => Promise` + +```js +const { Pool } = require('pg') + +const pool = new Pool() + +pool + .query('SELECT $1::text as name', ['brianc']) + .then((res) => console.log(res.rows[0].name)) // brianc + .catch((err) => console.error('Error executing query', err.stack)) +``` + +Callbacks are also supported: + +`pool.query(callback: (err?: Error, result: pg.Result)) => void` + +```js +const { Pool } = require('pg') + +const pool = new Pool() + +pool.query('SELECT $1::text as name', ['brianc'], (err, result) => { + if (err) { + return console.error('Error executing query', err.stack) + } + console.log(result.rows[0].name) // brianc +}) +``` + +Notice in the example above there is no need to check out or release a client. The pool is doing the acquiring and releasing internally. I find `pool.query` to be a handy shortcut many situations and use it exclusively unless I need a transaction. + + +
+ Do not use pool.query if you are using a transaction. +
+ The pool will dispatch every query passed to pool.query on the first available idle client. Transactions within PostgreSQL + are scoped to a single client and so dispatching individual queries within a single transaction across multiple, random + clients will cause big problems in your app and not work. For more info please read + transactions + . +
+ ## pool.connect -### `pool.connect(callback: (err?: Error, client?: pg.Client, release?: releaseCallback) => void) => void` +`pool.connect(callback: (err?: Error, client?: pg.Client, release?: releaseCallback) => void) => void` -Acquires a client from the pool. If the pool is 'full' and all clients are currently checked out, this will wait in a FIFO queue until a client becomes available by it being released back to the pool. If there are idle clients in the pool it will be returned to the callback on `process.nextTick`. If the pool is not full a new client will be created & returned to this callback. +Acquires a client from the pool. + +- If there are idle clients in the pool one will be returned to the callback on `process.nextTick`. +- If the pool is not full but all current clients are checked out a new client will be created & returned to this callback. +- If the pool is 'full' and all clients are currently checked out will wait in a FIFO queue until a client becomes available by it being released back to the pool. ```js const { Pool } = require('pg') @@ -79,7 +135,7 @@ pool.connect((err, client, release) => { }) ``` -### `pool.connect() => Promise` +`pool.connect() => Promise` ```js const { Pool } = require('pg') @@ -93,19 +149,23 @@ const pool = new Pool() })() ``` -
- You must call the releaseCallback or{' '} - client.release (which points to the releaseCallback) when - you are finished with a client. If you forget to release the client then your application will quickly exhaust - available, idle clients in the pool and all further calls to pool.connect will timeout - with an error or hang indefinitely if you have connectionTimeoutMillis configured to 0. -
+### releasing clients -## releaseCallback +`release: (err?: Error)` -### release: (err?: Error) +Client instances returned from `pool.connect` will have a `release` method which will release them from the pool. -The `releaseCallback` releases an acquired client back to the pool. If you pass a truthy value in the `err` position to the callback, instead of releasing the client to the pool, the pool will be instructed to disconnect and destroy this client, leaving a space within itself for a new client. +The `release` method on an acquired client returns it back to the pool. If you pass a truthy value in the `err` position to the callback, instead of releasing the client to the pool, the pool will be instructed to disconnect and destroy this client, leaving a space within itself for a new client. + +```js +const { Pool } = require('pg') + +const pool = new Pool() +// check out a single client +const client = await pool.connect() +// release the client +client.release() +``` ```js const { Pool } = require('pg') @@ -113,72 +173,27 @@ const { Pool } = require('pg') const pool = new Pool() assert(pool.totalCount === 0) assert(pool.idleCount === 0) -;(async function () { - const client = await pool.connect() - await client.query('SELECT NOW()') - assert(pool.totalCount === 1) - assert(pool.idleCount === 0) - // tell the pool to destroy this client - client.release(true) - assert(pool.idleCount === 0) - assert(pool.totalCount === 0) -})() +const client = await pool.connect() +await client.query('SELECT NOW()') +assert(pool.totalCount === 1) +assert(pool.idleCount === 0) + +// tell the pool to destroy this client +client.release(true) +assert(pool.idleCount === 0) +assert(pool.totalCount === 0) ``` -Client instances returned from `pool.connect` will have a `release` method which will release them from the pool. This is the same method that is passed to the connect callback as the 3rd argument if you're using the pool with callbacks. - -```js -const { Pool } = require('pg') - -const pool = new Pool() -pool.connect((err, client, release) => { - assert(client.release === release) -}) -``` - -## pool.query - -Often we only need to run a single query on the database, so as convenience the pool has a method to run a query on the first available idle client and return its result. - -### pool.query(callback: (err?: Error, result: pg.Result)) => void - -```js -const { Pool } = require('pg') - -const pool = new Pool() - -pool.query('SELECT $1::text as name', ['brianc'], (err, result) => { - if (err) { - return console.error('Error executing query', err.stack) - } - console.log(result.rows[0].name) // brianc -}) -``` - -Promises are also supported: - -### `pool.query() => Promise` - -```js -const { Pool } = require('pg') - -const pool = new Pool() - -pool - .query('SELECT $1::text as name', ['brianc']) - .then((res) => console.log(res.rows[0].name)) // brianc - .catch((err) => console.error('Error executing query', err.stack)) -``` - -Notice in the example above no `releaseCallback` was necessary. The pool is doing the acquiring and releasing internally. I find `pool.query` to be a handy shortcut in a lot of situations. - -
- Do not use pool.query if you need transactional integrity: the pool will dispatch every query passed - to pool.query on the first available idle client. Transactions within PostgreSQL are scoped to a single client and so - dispatching individual queries within a single transaction across multiple, random clients will cause big problems in - your app and not work. For more info please read transactions. -
+ +
+ You must release a client when you are finished with it. +
+ If you forget to release the client then your application will quickly exhaust available, idle clients in the pool and + all further calls to pool.connect will timeout with an error or hang indefinitely if you have + connectionTimeoutMillis + configured to 0. +
## pool.end @@ -201,15 +216,15 @@ pool.end().then(() => console.log('pool has ended')) ## properties -### `pool.totalCount: int` +`pool.totalCount: number` The total number of clients existing within the pool. -### `pool.idleCount: int` +`pool.idleCount: number` The number of clients which are not checked out but are currently idle in the pool. -### `pool.waitingCount: int` +`pool.waitingCount: number` The number of queued requests waiting on a client when all clients are checked out. It can be helpful to monitor this number to see if you need to adjust the size of the pool. @@ -217,7 +232,9 @@ The number of queued requests waiting on a client when all clients are checked o `Pool` instances are also instances of [`EventEmitter`](https://nodejs.org/api/events.html). -### `pool.on('connect', (client: Client) => void) => void` +### connect + +`pool.on('connect', (client: Client) => void) => void` Whenever the pool establishes a new client connection to the PostgreSQL backend it will emit the `connect` event with the newly connected client. This presents an opportunity for you to run setup commands on a client. @@ -228,20 +245,30 @@ pool.on('connect', (client) => { }) ``` -### `pool.on('acquire', (client: Client) => void) => void` +### acquire + +`pool.on('acquire', (client: Client) => void) => void` Whenever a client is checked out from the pool the pool will emit the `acquire` event with the client that was acquired. -### `pool.on('error', (err: Error, client: Client) => void) => void` +### error -When a client is sitting idly in the pool it can still emit errors because it is connected to a live backend. If the backend goes down or a network partition is encountered all the idle, connected clients in your application will emit an error _through_ the pool's error event emitter. The error listener is passed the error as the first argument and the client upon which the error occurred as the 2nd argument. The client will be automatically terminated and removed from the pool, it is only passed to the error handler in case you want to inspect it. +`pool.on('error', (err: Error, client: Client) => void) => void` -
- It is important you add an event listener to the pool to catch errors. Just like other event emitters, if a pool emits - an error event and no listeners are added node will emit an uncaught error and - potentially exit. -
+When a client is sitting idly in the pool it can still emit errors because it is connected to a live backend. -### `pool.on('remove', (client: Client) => void) => void` +If the backend goes down or a network partition is encountered all the idle, connected clients in your application will emit an error _through_ the pool's error event emitter. + +The error listener is passed the error as the first argument and the client upon which the error occurred as the 2nd argument. The client will be automatically terminated and removed from the pool, it is only passed to the error handler in case you want to inspect it. + + +
You probably want to add an event listener to the pool to catch background errors errors!
+ Just like other event emitters, if a pool emits an error event and no listeners are added node will emit an + uncaught error and potentially crash your node process. +
+ +### remove + +`pool.on('remove', (client: Client) => void) => void` Whenever a client is closed & removed from the pool the pool will emit the `remove` event. diff --git a/docs/pages/api/3-result.mdx b/docs/pages/apis/result.mdx similarity index 100% rename from docs/pages/api/3-result.mdx rename to docs/pages/apis/result.mdx diff --git a/docs/pages/api/4-types.mdx b/docs/pages/apis/types.mdx similarity index 100% rename from docs/pages/api/4-types.mdx rename to docs/pages/apis/types.mdx diff --git a/docs/pages/features/_meta.json b/docs/pages/features/_meta.json new file mode 100644 index 00000000..a2f5e340 --- /dev/null +++ b/docs/pages/features/_meta.json @@ -0,0 +1,9 @@ +{ + "connecting": "Connecting", + "queries": "Queries", + "pooling": "Pooling", + "transactions": "Transactions", + "types": "Data Types", + "ssl": "SSL", + "native": "Native" +} diff --git a/docs/pages/features/1-connecting.mdx b/docs/pages/features/connecting.mdx similarity index 89% rename from docs/pages/features/1-connecting.mdx rename to docs/pages/features/connecting.mdx index ef9945a4..b3c5ecc4 100644 --- a/docs/pages/features/1-connecting.mdx +++ b/docs/pages/features/connecting.mdx @@ -1,11 +1,10 @@ --- title: Connecting -slug: /features/connecting --- ## Environment variables -node-postgres uses the same [environment variables](https://www.postgresql.org/docs/9.1/static/libpq-envars.html) as libpq to connect to a PostgreSQL server. Both individual clients & pools will use these environment variables. Here's a tiny program connecting node.js to the PostgreSQL server: +node-postgres uses the same [environment variables](https://www.postgresql.org/docs/9.1/static/libpq-envars.html) as libpq and psql to connect to a PostgreSQL server. Both individual clients & pools will use these environment variables. Here's a tiny program connecting node.js to the PostgreSQL server: ```js const { Pool, Client } = require('pg') @@ -48,7 +47,7 @@ This allows us to write our programs without having to specify connection inform The default values for the environment variables used are: ``` -PGHOST='localhost' +PGHOST=localhost PGUSER=process.env.USER PGDATABASE=process.env.USER PGPASSWORD=null @@ -120,18 +119,18 @@ const pool = new Pool({ }) ``` -### Programmatic Connection to Sockets +### Unix Domain Sockets Connections to unix sockets can also be made. This can be useful on distros like Ubuntu, where authentication is managed via the socket connection instead of a password. ```js -const { Client } = require('pg'); +const { Client } = require('pg') client = new Client({ - host: '/cloudsql/myproject:zone:mydb', - user: 'username', - password: 'password', - database: 'database_name', -}); + host: '/cloudsql/myproject:zone:mydb', + user: 'username', + password: 'password', + database: 'database_name', +}) ``` ## Connection URI diff --git a/docs/pages/features/7-native.mdx b/docs/pages/features/native.mdx similarity index 100% rename from docs/pages/features/7-native.mdx rename to docs/pages/features/native.mdx diff --git a/docs/pages/features/3-pooling.mdx b/docs/pages/features/pooling.mdx similarity index 73% rename from docs/pages/features/3-pooling.mdx rename to docs/pages/features/pooling.mdx index 99288485..4719150b 100644 --- a/docs/pages/features/3-pooling.mdx +++ b/docs/pages/features/pooling.mdx @@ -1,8 +1,10 @@ --- title: Pooling -slug: /features/pooling --- +import { Alert } from '/components/alert.tsx' +import { Info } from '/components/info.tsx' + If you're working on a web application or other software which makes frequent queries you'll want to use a connection pool. The easiest and by far most common way to use node-postgres is through a connection pool. @@ -52,20 +54,18 @@ pool.connect((err, client, done) => { }) // promise - checkout a client -pool - .connect() - .then(client => { - return client - .query('SELECT * FROM users WHERE id = $1', [1]) - .then(res => { - client.release() - console.log(res.rows[0]) - }) - .catch(err => { - client.release() - console.log(err.stack) - }) - }) +pool.connect().then((client) => { + return client + .query('SELECT * FROM users WHERE id = $1', [1]) + .then((res) => { + client.release() + console.log(res.rows[0]) + }) + .catch((err) => { + client.release() + console.log(err.stack) + }) +}) // async/await - check out a client ;(async () => { @@ -81,11 +81,14 @@ pool })() ``` -
-
- You must always return the client to the pool if you successfully check it out, regardless of whether or not there was an error with the queries you ran on the client. If you don't check in the client your application will leak them and eventually your pool will be empty forever and all future requests to check out a client from the pool will wait forever. + +
+ You must always return the client to the pool if you successfully check it out, regardless of whether or not + there was an error with the queries you ran on the client.
-
+ If you don't release the client your application will leak them and eventually your pool will be empty forever and all + future requests to check out a client from the pool will wait forever. + ### Single query @@ -114,8 +117,8 @@ const pool = new Pool() pool .query('SELECT * FROM users WHERE id = $1', [1]) - .then(res => console.log('user:', res.rows[0])) - .catch(err => + .then((res) => console.log('user:', res.rows[0])) + .catch((err) => setImmediate(() => { throw err }) @@ -128,14 +131,8 @@ Promises allow us to use `async`/`await` in node v8.0 and above (or earlier if y const { Pool } = require('pg') const pool = new Pool() -;(async () => { - const { rows } = await pool.query('SELECT * FROM users WHERE id = $1', [1]) - console.log('user:', rows[0]) -})().catch(err => - setImmediate(() => { - throw err - }) -) +const { rows } = await pool.query('SELECT * FROM users WHERE id = $1', [1]) +console.log('user:', rows[0]) ``` ### Shutdown @@ -146,20 +143,18 @@ To shut down a pool call `pool.end()` on the pool. This will wait for all checke const { Pool } = require('pg') const pool = new Pool() -;(async () => { - console.log('starting async query') - const result = await pool.query('SELECT NOW()') - console.log('async query finished') +console.log('starting async query') +const result = await pool.query('SELECT NOW()') +console.log('async query finished') - console.log('starting callback query') - pool.query('SELECT NOW()', (err, res) => { - console.log('callback query finished') - }) +console.log('starting callback query') +pool.query('SELECT NOW()', (err, res) => { + console.log('callback query finished') +}) - console.log('calling end') - await pool.end() - console.log('pool has drained') -})() +console.log('calling end') +await pool.end() +console.log('pool has drained') ``` The output of the above will be: @@ -173,8 +168,6 @@ callback query finished pool has drained ``` -
-
- The pool will return errors when attempting to check out a client after you've called `pool.end()` on the pool. -
-
+ + The pool will return errors when attempting to check out a client after you've called pool.end() on the pool. + diff --git a/docs/pages/features/2-queries.mdx b/docs/pages/features/queries.mdx similarity index 100% rename from docs/pages/features/2-queries.mdx rename to docs/pages/features/queries.mdx diff --git a/docs/pages/features/6-ssl.mdx b/docs/pages/features/ssl.mdx similarity index 100% rename from docs/pages/features/6-ssl.mdx rename to docs/pages/features/ssl.mdx diff --git a/docs/pages/features/4-transactions.mdx b/docs/pages/features/transactions.mdx similarity index 58% rename from docs/pages/features/4-transactions.mdx rename to docs/pages/features/transactions.mdx index 96ddc764..408db52f 100644 --- a/docs/pages/features/4-transactions.mdx +++ b/docs/pages/features/transactions.mdx @@ -1,30 +1,62 @@ --- title: Transactions -slug: /features/transactions --- +import { Alert } from '/components/alert.tsx' + To execute a transaction with node-postgres you simply execute `BEGIN / COMMIT / ROLLBACK` queries yourself through a client. Because node-postgres strives to be low level and un-opinionated, it doesn't provide any higher level abstractions specifically around transactions. -
+ You must use the same client instance for all statements within a transaction. PostgreSQL isolates a transaction to individual clients. This means if you initialize or use transactions with the{' '} pool.query method you will have problems. Do not use transactions with the pool.query method. -
+ ## Examples -### A pooled client with callbacks +### async/await + +Things are considerably more straightforward if you're using async/await: + +```js +const { Pool } = require('pg') +const pool = new Pool() + +// note: we don't try/catch this because if connecting throws an exception +// we don't need to dispose of the client (it will be undefined) +const client = await pool.connect() + +try { + await client.query('BEGIN') + const queryText = 'INSERT INTO users(name) VALUES($1) RETURNING id' + const res = await client.query(queryText, ['brianc']) + + const insertPhotoText = 'INSERT INTO photos(user_id, photo_url) VALUES ($1, $2)' + const insertPhotoValues = [res.rows[0].id, 's3.bucket.foo'] + await client.query(insertPhotoText, insertPhotoValues) + await client.query('COMMIT') +} catch (e) { + await client.query('ROLLBACK') + throw e +} finally { + client.release() +} +``` + +### callbacks + +node-postgres is a very old library, and still has an optional callback API. Here's an example of doing the same code above, but with callbacks: ```js const { Pool } = require('pg') const pool = new Pool() pool.connect((err, client, done) => { - const shouldAbort = err => { + const shouldAbort = (err) => { if (err) { console.error('Error in transaction', err.stack) - client.query('ROLLBACK', err => { + client.query('ROLLBACK', (err) => { if (err) { console.error('Error rolling back client', err.stack) } @@ -35,7 +67,7 @@ pool.connect((err, client, done) => { return !!err } - client.query('BEGIN', err => { + client.query('BEGIN', (err) => { if (shouldAbort(err)) return const queryText = 'INSERT INTO users(name) VALUES($1) RETURNING id' client.query(queryText, ['brianc'], (err, res) => { @@ -46,7 +78,7 @@ pool.connect((err, client, done) => { client.query(insertPhotoText, insertPhotoValues, (err, res) => { if (shouldAbort(err)) return - client.query('COMMIT', err => { + client.query('COMMIT', (err) => { if (err) { console.error('Error committing transaction', err.stack) } @@ -58,39 +90,4 @@ pool.connect((err, client, done) => { }) ``` -
-
- I omitted any additional libraries from the example for clarity, but if you're using callbacks you'd typically be - using a flow control library like async. -
-
- -### A pooled client with async/await - -Things are considerably more straightforward if you're using async/await: - -```js -const { Pool } = require('pg') -const pool = new Pool() -;(async () => { - // note: we don't try/catch this because if connecting throws an exception - // we don't need to dispose of the client (it will be undefined) - const client = await pool.connect() - - try { - await client.query('BEGIN') - const queryText = 'INSERT INTO users(name) VALUES($1) RETURNING id' - const res = await client.query(queryText, ['brianc']) - - const insertPhotoText = 'INSERT INTO photos(user_id, photo_url) VALUES ($1, $2)' - const insertPhotoValues = [res.rows[0].id, 's3.bucket.foo'] - await client.query(insertPhotoText, insertPhotoValues) - await client.query('COMMIT') - } catch (e) { - await client.query('ROLLBACK') - throw e - } finally { - client.release() - } -})().catch(e => console.error(e.stack)) -``` +..thank goodness for `async/await` yeah? diff --git a/docs/pages/features/5-types.mdx b/docs/pages/features/types.mdx similarity index 90% rename from docs/pages/features/5-types.mdx rename to docs/pages/features/types.mdx index 929b9718..65c814ba 100644 --- a/docs/pages/features/5-types.mdx +++ b/docs/pages/features/types.mdx @@ -1,8 +1,9 @@ --- title: Data Types -slug: /features/types --- +import { Alert } from '/components/alert.tsx' + PostgreSQL has a rich system of supported [data types](https://www.postgresql.org/docs/9.5/static/datatype.html). node-postgres does its best to support the most common data types out of the box and supplies an extensible type parser to allow for custom type serialization and parsing. ## strings by default @@ -83,7 +84,7 @@ console.log(result.rows) psql output: -```psql +``` bmc=# select * from dates; date_col | timestamp_col | timestamptz_col ------------+-------------------------+---------------------------- @@ -95,8 +96,11 @@ node-postgres converts `DATE` and `TIMESTAMP` columns into the **local** time of _note: I generally use `TIMESTAMPTZ` when storing dates; otherwise, inserting a time from a process in one timezone and reading it out in a process in another timezone can cause unexpected differences in the time._ -
-
- Although PostgreSQL supports microseconds in dates, JavaScript only supports dates to the millisecond precision. Keep this in mind when you send dates to and from PostgreSQL from node: your microseconds will be truncated when converting to a JavaScript date object even if they exist in the database. If you need to preserve them, I recommend using a custom type parser. + +
+ Although PostgreSQL supports microseconds in dates, JavaScript only supports dates to the millisecond precision. + Keep this in mind when you send dates to and from PostgreSQL from node: your microseconds will be truncated when + converting to a JavaScript date object even if they exist in the database. If you need to preserve them, I recommend + using a custom type parser.
-
+ diff --git a/docs/pages/guides/_meta.json b/docs/pages/guides/_meta.json new file mode 100644 index 00000000..3889a099 --- /dev/null +++ b/docs/pages/guides/_meta.json @@ -0,0 +1,5 @@ +{ + "project-structure": "Suggested Code Structure", + "async-express": "Express with Async/Await", + "upgrading": "Upgrading" +} diff --git a/docs/pages/guides/2-async-express.md b/docs/pages/guides/async-express.md similarity index 97% rename from docs/pages/guides/2-async-express.md rename to docs/pages/guides/async-express.md index 5e64a03e..3be6d955 100644 --- a/docs/pages/guides/2-async-express.md +++ b/docs/pages/guides/async-express.md @@ -1,6 +1,5 @@ --- title: Express with async/await -slug: /guides/async-express --- My preferred way to use node-postgres (and all async code in node.js) is with `async/await`. I find it makes reasoning about control-flow easier and allows me to write more concise and maintainable code. @@ -61,7 +60,7 @@ Then in my `routes/index.js` file I'll have something like this which mounts eac const users = require('./user') const photos = require('./photos') -module.exports = app => { +module.exports = (app) => { app.use('/users', users) app.use('/photos', photos) // etc.. diff --git a/docs/pages/guides/1-project-structure.md b/docs/pages/guides/project-structure.md similarity index 94% rename from docs/pages/guides/1-project-structure.md rename to docs/pages/guides/project-structure.md index 95d9ea97..742451da 100644 --- a/docs/pages/guides/1-project-structure.md +++ b/docs/pages/guides/project-structure.md @@ -1,6 +1,5 @@ --- title: Suggested Project Structure -slug: /guides/project-structure --- Whenever I am writing a project & using node-postgres I like to create a file within it and make all interactions with the database go through this file. This serves a few purposes: @@ -81,7 +80,7 @@ module.exports = { That was pretty quick! And now all of our queries everywhere in our application are being logged. -_note: I didn't log the query parameters. Depending on your application you might be storing encrypted passwords or other sensitive information in your database. If you log your query parameters you might accidentally log sensitive information. Every app is different though so do what suits you best!_ +_note: I didn't log the query parameters. Depending on your application you might be storing encrypted passwords or other sensitive information in your database. If you log your query parameters you might accidentally log sensitive information. Every app is different though so do what suits you best!_ Now what if we need to check out a client from the pool to run several queries in a row in a transaction? We can add another method to our `db/index.js` file when we need to do this: @@ -103,7 +102,7 @@ module.exports = { pool.connect((err, client, done) => { callback(err, client, done) }) - } + }, } ``` @@ -152,13 +151,13 @@ module.exports = { callback(err, client, release) }) - } + }, } ``` Using async/await: -``` +```js module.exports = { async query(text, params) { const start = Date.now() @@ -191,7 +190,8 @@ module.exports = { return release.apply(client) } return client - } + }, } ``` + That should hopefully give us enough diagnostic information to track down any leaks. diff --git a/docs/pages/guides/3-upgrading.md b/docs/pages/guides/upgrading.md similarity index 100% rename from docs/pages/guides/3-upgrading.md rename to docs/pages/guides/upgrading.md diff --git a/docs/pages/index.mdx b/docs/pages/index.mdx index a345900e..234cf11e 100644 --- a/docs/pages/index.mdx +++ b/docs/pages/index.mdx @@ -15,13 +15,13 @@ $ npm install pg node-postgres continued development and support is made possible by the many [supporters](https://github.com/brianc/node-postgres/blob/master/SPONSORS.md) with a special thanks to our featured supporters: -
-