Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/fastify/fastify-postgres
Fastify PostgreSQL connection plugin
https://github.com/fastify/fastify-postgres
fastify fastify-plugin
Last synced: 27 days ago
JSON representation
Fastify PostgreSQL connection plugin
- Host: GitHub
- URL: https://github.com/fastify/fastify-postgres
- Owner: fastify
- License: mit
- Created: 2017-10-11T12:36:23.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2024-09-21T08:56:09.000Z (about 1 month ago)
- Last Synced: 2024-09-28T11:20:59.204Z (about 1 month ago)
- Topics: fastify, fastify-plugin
- Language: JavaScript
- Size: 188 KB
- Stars: 181
- Watchers: 19
- Forks: 33
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-fastify - `fastify-postgres`
README
# @fastify/postgres
![CI](https://github.com/fastify/fastify-postgres/workflows/CI/badge.svg)
[![NPM version](https://img.shields.io/npm/v/@fastify/postgres.svg?style=flat)](https://www.npmjs.com/package/@fastify/postgres)
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://standardjs.com/)Fastify PostgreSQL connection plugin; with this, you can share the same PostgreSQL connection pool in every part of your server.
Under the hood [node-postgres](https://github.com/brianc/node-postgres) is used, the options that you pass to `register` will be passed to the PostgreSQL pool builder.## Install
```
npm i pg @fastify/postgres
```
## Usage
Add it to your project with `register` and you are done!
This plugin will add the `pg` namespace to your Fastify instance, with the following properties:
```
connect: the function to get a connection from the pool
pool: the pool instance
Client: a client constructor for a single query
query: a utility to perform a query _without_ a transaction
transact: a utility to perform multiple queries _with_ a transaction
```Example:
```js
const fastify = require('fastify')()fastify.register(require('@fastify/postgres'), {
connectionString: 'postgres://postgres@localhost/postgres'
})fastify.get('/user/:id', (req, reply) => {
fastify.pg.connect(onConnect)function onConnect (err, client, release) {
if (err) return reply.send(err)client.query(
'SELECT id, username, hash, salt FROM users WHERE id=$1', [req.params.id],
function onResult (err, result) {
release()
reply.send(err || result)
}
)
}
})fastify.listen({ port: 3000 }, err => {
if (err) throw err
console.log(`server listening on ${fastify.server.address().port}`)
})
```Async await is supported as well!
```js
const fastify = require('fastify')()fastify.register(require('@fastify/postgres'), {
connectionString: 'postgres://postgres@localhost/postgres'
})fastify.get('/user/:id', async (req, reply) => {
const client = await fastify.pg.connect()
try {
const { rows } = await client.query(
'SELECT id, username, hash, salt FROM users WHERE id=$1', [req.params.id],
)
// Note: avoid doing expensive computation here, this will block releasing the client
return rows
} finally {
// Release the client immediately after query resolves, or upon error
client.release()
}
})fastify.listen({ port: 3000 }, err => {
if (err) throw err
console.log(`server listening on ${fastify.server.address().port}`)
})
```
Use of `pg.query`
```js
const fastify = require('fastify')()fastify.register(require('@fastify/postgres'), {
connectionString: 'postgres://postgres@localhost/postgres'
})fastify.get('/user/:id', (req, reply) => {
fastify.pg.query(
'SELECT id, username, hash, salt FROM users WHERE id=$1', [req.params.id],
function onResult (err, result) {
reply.send(err || result)
}
)
})fastify.listen({ port: 3000 }, err => {
if (err) throw err
console.log(`server listening on ${fastify.server.address().port}`)
})
```Use of `pg.transact`
```js
const fastify = require('fastify')()fastify.register(require('@fastify/postgres'), {
connectionString: 'postgres://postgres@localhost/postgres'
})fastify.post('/user/:username', (req, reply) => {
// will return a promise, fastify will send the result automatically
return fastify.pg.transact(async client => {
// will resolve to an id, or reject with an error
const id = await client.query('INSERT INTO users(username) VALUES($1) RETURNING id', [req.params.username])// potentially do something with id
return id
})
})/* or with a transaction callback
fastify.pg.transact(client => {
return client.query('INSERT INTO users(username) VALUES($1) RETURNING id', [req.params.username])
},
function onResult (err, result) {
reply.send(err || result)
}
})*/
/* or with a commit callback
fastify.pg.transact((client, commit) => {
client.query('INSERT INTO users(username) VALUES($1) RETURNING id', [req.params.username], (err, id) => {
commit(err, id)
});
})*/
fastify.listen({ port: 3000 }, err => {
if (err) throw err
console.log(`server listening on ${fastify.server.address().port}`)
})
```As you can see there is no need to close the client, since it is done internally. Promises and async await are supported as well.
### Name option
If you need to have multiple databases set up, then you can name each one of them by passing `name: 'foo'`. It will then be accessible as `fastify.pg.foo`.
You can use both unnamed and named postgres connections at once. There can be only one unnamed connection, and it will be accessible as `fastify.pg`.```js
const fastify = require('fastify')()fastify.register(require('@fastify/postgres'), {
connectionString: 'postgres://postgres@localhost/postgres',
name: 'foo'
})fastify.get('/user/:id', (req, reply) => {
fastify.pg.foo.query(
'SELECT id, username, hash, salt FROM users WHERE id=$1', [req.params.id],
function onResult (err, result) {
reply.send(err || result)
}
)
})fastify.listen({ port: 3000 }, err => {
if (err) throw err
console.log(`server listening on ${fastify.server.address().port}`)
})
```### Native option
If you want maximum performance you can install [pg-native](https://github.com/brianc/node-pg-native), and pass `native: true` to the plugin options.
*Note: it requires PostgreSQL client libraries & tools installed, see [instructions](https://github.com/brianc/node-pg-native#install).*
Note: trying to use native options without successfully installation of `pg-native` will return a warning and fallback to regular `pg` module.```js
const fastify = require('fastify')()fastify.register(require('@fastify/postgres'), {
connectionString: 'postgres://postgres@localhost/postgres',
native: true
})fastify.get('/user/:id', (req, reply) => {
fastify.pg.query(
'SELECT id, username, hash, salt FROM users WHERE id=$1', [req.params.id],
function onResult (err, result) {
reply.send(err || result)
}
)
})fastify.listen({ port: 3000 }, err => {
if (err) throw err
console.log(`server listening on ${fastify.server.address().port}`)
})
```### `pg` option
If you want to provide your own `pg` module, for example to support packages like [`pg-range`](https://www.npmjs.com/package/pg-range), you can provide an optional `pg` option with the patched library to use:```js
const fastify = require('fastify')()
const pg = require("pg");
require("pg-range").install(pg)fastify.register(require('@fastify/postgres'), {
connectionString: 'postgres://postgres@localhost/postgres',
pg: pg
})fastify.get('/user/:id', (req, reply) => {
fastify.pg.query(
'SELECT id, username, hash, salt FROM users WHERE id=$1', [req.params.id],
function onResult (err, result) {
reply.send(err || result)
}
)
})fastify.listen({ port: 3000 }, err => {
if (err) throw err
console.log(`server listening on ${fastify.server.address().port}`)
})
```### Transact route option
It is possible to automatically wrap a route handler in a transaction by using the `transact` option when registering a route with Fastify. Note that the option must be scoped within a `pg` options object to take effect.`query` commands can then be accessed at `request.pg` or `request.pg[name]` and `transact` can be set for either the root pg client with value `true` or for a pg client at a particular namespace with value `name`. Note that the namespace needs to be set when registering the plugin in order to be available on the request object.
```js
// transact set for the route pg client
fastify.get('/user/:id', { pg: { transact: true } }, (req, reply) => {
// transaction wrapped queries, NO error handling
req.pg.query('SELECT username FROM users WHERE id=1')
req.pg.query('SELECT username FROM users WHERE id=2')
req.pg.query('SELECT username FROM users WHERE id=3')
})// transact set for a pg client at name
fastify.get('/user/:id', { pg: { transact: 'foo' } }, (req, reply) => {
// transaction wrapped queries, NO error handling
req.pg.foo.query('SELECT username FROM users WHERE id=1')
req.pg.foo.query('SELECT username FROM users WHERE id=2')
req.pg.foo.query('SELECT username FROM users WHERE id=3')
})
```Important: rolling back a transaction relies on the handler failing and being caught by an `onError` hook. This means that the transaction wrapped route handler must not catch any errors internally.
In the plugin this works by using the `preHandler` hook to open the transaction, then the `onError` and `onSend` hooks to commit or rollback and release the client back to the pool.
## TypeScript Usage
Install the compiler and typings for pg module:
```shell script
npm i -D typescript @types/pg
```More examples in the [examples/typescript](./examples/typescript) directory.
## Development and Testing
### Docker approach
First, start postgres with:
```
$ npm run postgres
```Then you can, in another terminal, find the running docker, init the DB, then run the tests:
```
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
28341f85c4cd postgres:9.6-alpine "docker-entrypoint.s…" 3 minutes ago Up 3 minutes 0.0.0.0:5432->5432/tcp jovial_shockley$ docker exec -ti jovial_shockley /usr/local/bin/psql -d postgres -U postgres -c 'CREATE TABLE users(id serial PRIMARY KEY, username VARCHAR (50) NOT NULL);'
CREATE TABLE$ npm test
```### Custom Postgres approach
1. Set up a database of your choice in a postgres server of your choice
2. Create the required table using
```sql
CREATE TABLE users(id serial PRIMARY KEY, username VARCHAR (50) NOT NULL);
```
3. Specify a connection string to it in a `DATABASE_TEST_URL` environment variable when you run the tests
```bash
DATABASE_TEST_URL="postgres://username:password@localhost/something_thats_a_test_database" npm test
```## Acknowledgements
This project is kindly sponsored by:
- [nearForm](https://www.nearform.com)
- [LetzDoIt](https://www.letzdoitapp.com/)## License
Licensed under [MIT](./LICENSE).