Node-postgres
1. Installation
npm install pg
2. Setting Up a Database Connection
The core of pg
involves creating a client and establishing a connection. There are two primary ways to manage connections in pg
:
- Using a Single Client
- Using a Connection Pool (recommended for most applications)
// Connecting with a Single Client
import { Client } from "pg";
const client = new Client({
user: "your_database_user",
host: "localhost",
database: "your_database_name",
password: "your_database_password",
port: 5432,
});
client
.connect()
.then(() => console.log("Connected to database"))
.catch((err) => console.error("Connection error", err.stack));
// Using a Connection Pool (Recommended)
import { Pool } from "pg";
const pool = new Pool({
user: "your_database_user",
host: "localhost",
database: "your_database_name",
password: "your_database_password",
port: 5432,
});
pool
.connect()
.then((client) => {
console.log("Connected to database");
client.release(); // Release the client back to the pool
})
.catch((err) => console.error("Connection error", err.stack));
3. CRUD Queries
With pg
, queries can be executed with either client.query
or pool.query
.
pool
.query("INSERT INTO users(name, age) VALUES($1, $2) RETURNING *", [
"John Doe",
30,
])
.then((result) => console.log("User added:", result.rows[0]))
.catch((error) => console.error("Insert error", error.stack));
pool
.query("SELECT * FROM users WHERE id = $1", [1])
.then((result) => console.log(result.rows))
.catch((error) => console.error("Query error", error.stack));
pool
.query("UPDATE users SET age = $1 WHERE id = $2 RETURNING *", [31, 1])
.then((result) => console.log("User updated:", result.rows[0]))
.catch((error) => console.error("Update error", error.stack));
pool
.query("DELETE FROM users WHERE id = $1 RETURNING *", [1])
.then((result) => console.log("User deleted:", result.rows[0]))
.catch((error) => console.error("Delete error", error.stack));
4. Advanced Queries
Table Joins
pool
.query(
`
SELECT users.name, orders.amount
FROM users
JOIN orders ON users.id = orders.user_id
WHERE users.id = $1`,
[1]
)
.then((result) => console.log(result.rows))
.catch((error) => console.error("Join query error", error.stack));
Aggregations
pool
.query("SELECT SUM(amount) as total_amount FROM orders")
.then((result) => console.log("Total amount:", result.rows[0].total_amount))
.catch((error) => console.error("Aggregation error", error.stack));
Complex Conditions
pool
.query("SELECT * FROM users WHERE age > $1 OR status = $2", [25, "active"])
.then((result) => console.log(result.rows))
.catch((error) => console.error("Conditional query error", error.stack));
5. Prepared Statements
Prepared statements are useful to prevent SQL injection and improve performance on repeated queries.
const queryText = "INSERT INTO users(name, age) VALUES($1, $2) RETURNING *";
const values = ["Alice", 24];
pool
.query(queryText, values)
.then((result) => console.log("User added:", result.rows[0]))
.catch((error) => console.error("Prepared statement error", error.stack));
6. Transactions
Transactions ensure multiple queries are executed as a single atomic operation. If one query fails, the whole transaction is rolled back.
async function transferFunds(fromUserId, toUserId, amount) {
const client = await pool.connect();
try {
await client.query("BEGIN");
const deduct =
"UPDATE accounts SET balance = balance - $1 WHERE user_id = $2";
const add = "UPDATE accounts SET balance = balance + $1 WHERE user_id = $2";
await client.query(deduct, [amount, fromUserId]);
await client.query(add, [amount, toUserId]);
await client.query("COMMIT");
console.log("Transaction successful");
} catch (error) {
await client.query("ROLLBACK");
console.error("Transaction failed:", error.stack);
} finally {
client.release();
}
}
7. Connection Pooling
Using a pool is typically more efficient, as it manages a set of reusable database connections.
const pool = new Pool({
user: "your_database_user",
host: "localhost",
database: "your_database_name",
password: "your_database_password",
port: 5432,
max: 10, // Maximum number of clients in the pool
idleTimeoutMillis: 30000, // Close idle clients after 30 seconds
connectionTimeoutMillis: 2000, // Timeout for acquiring new clients
});
pool.on("error", (err, client) => {
console.error("Unexpected error on idle client", err);
process.exit(-1);
});
8. Error Handling
Error handling is essential for managing database connection issues, query errors, and transaction rollbacks.
pool
.query("SELECT * FROM non_existing_table")
.then((result) => console.log(result.rows))
.catch((error) => {
if (error.code === "42P01") {
// Undefined table error code
console.error("Table does not exist.");
} else {
console.error("Query error:", error.stack);
}
});