Skip to content

Chaining Queries

Vitaly Tomilov edited this page Nov 28, 2021 · 45 revisions

When executing a single query, you normally use query methods of the Database object directly:

db.oneOrNone('select * from users where id = $1', 123)
    .then(data => {
        // success;
    })
    .catch(error => {
        // error;
    });

All methods underneath use method query, which allocates and releases the connection, so chaining them directly will result in poor connection usage, compromising performance and scalability.

Instead, you should use method task, which allocates and releases the connection only once, providing a connection context well suited for executing multiple queries at once:

db.task(t => {
    return t.oneOrNone('SELECT id FROM Users WHERE name = $1', 'John')
        .then(user => {
            if(user) {
                return t.any('SELECT * from Events WHERE userId = $1', user.id);
            }
            return []; // user not found, so no events
        });
})
    .then(events => {
        // success
    })
    .catch(error => {
        // error
    });

And if the task requires a transaction, use method tx instead.

If you do not follow the advised approach, your application will perform better under a light load, due to more connections allocated in parallel, but under a heavy load it will quickly deplete the connection pool, crippling scalability and performance of your application.

EXTRAS

  • Tasks and transactions are much easier to use with the new ES7 syntax.
  • Tagged tasks and transactions are easier to read and monitor via pg-monitor, see tags.
ES7 example
db.task('my-es7-task', async t => {
    const user = await t.oneOrNone('SELECT id FROM Users WHERE name = $1', 'John');
    if(user) {
        return t.any('SELECT * from Events WHERE userId = $1', user.id);
    }
    return []; // user not found, so no events
})
    .then(events => {
        // success
    })
    .catch(error => {
        // error
    });

See also: SELECT ⇒ INSERT example.