Skip to content

Manipulating a Postgres database with Node.js

We’ll see how to interact with a Postgres database from Node.js. To achieve it, we’ll use the node-postgres package (pg on npm). It provices a simple JavaScript interface between Postgres and Node.js.

These same steps can be followed to connect Postgres and Express.js.

Set up and installation

In case you don’t have postgres installed, you can follow this tutorial.

If you don’t have a node project, initialize one by executing the following command from a new folder.

$ npm init -y

Then, install the node-postgres package.

$ npm install pg

Initializing the database in postgres

We’ll create a simple database for this tutorial. The command-line instructions for this section are specific to Ubuntu. In case you have a different OS, you might need to use slightly different commands.

Run psql as postgres user.

$ sudo -u postgres psql

Create a new user with a secure password.

CREATE USER my_user WITH PASSWORD '12356';

Create a new database and set the user above as the owner.

CREATE DATABASE db WITH OWNER='my_user';

Exit psql with the \q command and connect to the database as the newly created user. You will need to create an OS user with the same username if you don’t have one (see here).

$ sudo -u my_user psql db

Create a new table with some dummy data.

CREATE TABLE my_table(
  id int,
  name text,
  age int
); 
INSERT INTO my_table(id, name, age) VALUES 
  (1, 'foo', 25),
  (2, 'bar', 30),
  (3, 'john', 45);

We can verify the values were correctly inserted with:

table my_table;

Configure node-postgres

Now, we will start the JavaScritp manipulation in a file called db.js. The interaction with postgres is achieved via a Pool object from the node-postgres package. In simple terms, a Pool is a collection of Clients, where each Client is a single connection to the postgres database.

While a single Client would suffice for this simple tutorial, for both performance and ease of use, we’ll use Pool instead of Client.

To initialize our Pool, we pass it a configuration object with the following parameters to connect it to the database:

// db.js
const { Pool } = require('pg');

const pool = new Pool({
  user: "map",
  password: "map",
  database: "map",
  host: "localhost",
  port: 5432,
});

Since we’re connection to the database locally, we set the host to localhost. The port is the one used by postgres, which is 5432 by default. You can determine which port postgres uses by running pg_lsclusters from the command terminal.

Performing queries

At this stage, we’re ready to query our database.

Let’s insert a new row to our table. The corresponding SQL command is:

INSERT INTO my_table VALUES (4, 'Nick', 50);

We just need to pass that query as a string to the pool.query method.

pool.query("INSERT INTO my_table VALUES (4, 'Nick', 50)");

Since it’s an asynchronous process, we need to wrap it inside an async function.

// db.js
const insert = async () => {
  try {
    await pool.query("INSERT INTO my_table VALUES (4, 'Nick', 50)");
    console.log("success");
  } catch (err) {
    console.error(err);
  }
};

We can test it, by loading our script to node.

$ node db.js
> .load db.js
> insert()
> success

We can verify our table has been updated with psql:

$ sudo -u my_user psql db
> table my_table;

Parameterized queries

We can create parameterized queries by passing as a second argument an array with the query parameters.

const insertParam = async (id, name, age) => {
  try {
    await pool.query("INSERT INTO my_table VALUES ($1, $2, $3)", [
      id,
      name,
      age,
    ]);
    console.log("success");
  } catch (err) {
    console.error(err);
  }
};

The $x portions of the string will be substituted with the corresponding items of the parameters array.

Once again, we test it as follows.

$ node
> .load db.js
> insertParam(5, "Jamie", 21)
> success

We can pass the parameters directly using JavaScript data types. node-postgres will cast them to the corresponding postgres types. Additionally, node-postgres will automatically sanitize the parameters to avoid SQL injection.

If you are passing parameters to your queries you will want to avoid string concatenating parameters into the query text directly. This can (and often does) lead to sql injection vulnerabilities. node-postgres supports parameterized queries, passing your query text unaltered as well as your parameters to the PostgreSQL server where the parameters are safely substituted into the query with battle-tested parameter substitution code within the server itself.

node-postgres docs

SQL Injection test

As a fun experiment, let’s try sql injection with the function we’ve created.

First, let’s create insertUnsafe to compare the results.

// DO NOT USE
const insertUnsafe = async (id, name, age) => {
  try {
    await pool.query(`INSERT INTO my_table VALUES (${id}, ${name}, ${age})`);
    console.log("success");
  } catch (err) {
    console.error(err);
  }
};

It’s very similar to insertParam, except that it inserts the arguments directly into the query string.

Let’s reload db.js and execute the following:

> insertParam(5, "'Jamie', 1); TRUNCATE my_table; SELECT repeat('a'", 1)
> success

We can verify that the weird text passed as the second argument will be safely introduced to my_table.

Now, let’s try the same with insertUnsafe (CAUTION: my_table data will be wiped out).

> insertUnsafe(5, "'Jamie', 1); TRUNCATE my_table; SELECT repeat('a'", 1)
success

And let’s check the contents of my_table now…

Fetching data from the database

As a final application, let’s see how to recover data from the database.

We can do so using pool.query as before.

const getRow = async (id) => {
  try {
    const res = await pool.query("SELECT * FROM my_table WHERE id=$1", [id]);
    console.log(res.rows[0]);
  } catch (err) {
    console.error(err);
  }
};

This time, we save the results of the query in a variable res. The results of the query will be in the res.rows property that stores an array with the results. To each row of the result corresponds and object with a property for each column.

$ node
> .load db.js
> getRow(1)
> { id: 1, name: 'foo', age: 25 }

Finally, to gracefully terminate the connection with the database, we call pool.end().

Source code used for this tutorial can be found here.

Published inArticles
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments