Search code examples
node.jspostgresqlnode-postgres

Using Async/Await with node-postgres


I am using node-postgres to query my database and would like to know how to use async/await and handle errors correctly

An example of my use is here with a very simple query

const { Pool } = require('pg');

let config;
if (process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'staging') {
  config = { connectionString: process.env.DATABASE_URL, ssl: true };
} else {
  config = {
    host: 'localhost',
    user: 'myuser',
    database: 'mydatabase',
  };
}

const pool = new Pool(config);

async function getAllUsers() {
  let response;
  try {
    response = await pool.query('select * FROM users');
  } catch (error) {
    throw error;
  }
  return response.rows;
}

Then in my routes.js I have

app.get('/all_users', async (req, res) => {
  const users = await queries.getAllUsers();
  console.log(users); // returns all users fine
});

This is my understanding so far, but i don't think I am approaching this correctly as when it comes to errors my app will freeze and throw UnhandledPromiseRejectionWarning. So if I provide an incorrect table for example

async function getAllUsers() {
  let response;
  try {
    response = await pool.query('select * FROM notable');
  } catch (error) {
    throw error;
  }
  return response.rows;
}

UnhandledPromiseRejectionWarning: error: relation "notable" does not exist

The app will crash after 30 seconds and I have not handled this error gracefully. What am I missing?


Solution

  • When an async function or Promise throws an uncaught error, or when the catcher also throws, such as with your

    throw error;
    

    this means that the caller of the function will be faced with a rejected Promise to deal with. If you're using await in the caller, then you'll also have to use try/catch in the caller in order to catch the errors properly:

    app.get('/all_users', async (req, res) => {
      try {
        const users = await queries.getAllUsers();
        console.log(users);
      } catch(e) {
        // handle errors
      }
    });
    

    Another way to resolve the error without having to use try/catch in the consumer would be not to throw the error in your catch:

    async function getAllUsers() {
      let response;
      try {
        response = await pool.query('select * FROM users');
        return response.rows;
      } catch (error) {
        // handle error
        // do not throw anything
      }
    }
    

    but this will make it harder for the consumer to know when there was an error.

    In this particular case, the async/await/try/catch constructions add a lot of syntax noise without much benefit IMO - for the moment, you might consider using plain Promises instead:

    const getAllUsers = () => pool.query('select * FROM users')
      .then(response => response.rows);
    
    // and:
    app.get('/all_users', (req, res) => {
      queries.getAllUsers()
        .then((users) => {
          console.log(users);
        })
        .catch((err) => {
          // handle errors
        });
    });
    

    async and await shines when you have a few .thens that you'd like to look flatter in your code. If there's only one .then, IMO there's not much benefit to transforming it into async/await syntax. Of course, it's up to you.