PostgreSQL / Node.js + PostgreSQL

Node.js + PostgreSQL

Та одоо PostgreSQL-н нарийн ойлголтуудыг бүгдийг эзэмшлээ. Ирээдүйд та эдгээрийг JavaScript application-аасаа ашиглах хэрэгтэй болно. Энэ хичээлд хамгийн нийтлэг pg (node-postgres) library-г ашиглан Node.js-аас PostgreSQL-тай холбогдох, аюулгүй асуулга ажиллуулах, transaction гүйцэтгэх аргуудыг сурна.

Суулгалт ба холболт

bash
# Шинэ Node.js төсөл үүсгэх
mkdir pg-demo && cd pg-demo
npm init -y
npm install pg
npm install dotenv   # нууц мэдээллийг .env-д хадгалах
javascript
// db.js — connection pool тохиргоо
import pg from 'pg';
import 'dotenv/config';

const { Pool } = pg;

// Pool нь олон холболтыг удирдаж, дахин ашигладаг
export const pool = new Pool({
  host:     process.env.DB_HOST,
  port:     Number(process.env.DB_PORT) || 5432,
  database: process.env.DB_NAME,
  user:     process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  max:      10,               // Нэгэн зэрэг хамгийн ихдээ 10 холболт
  idleTimeoutMillis: 30000,   // 30 секунд идэвхгүй байвал холболт хаах
  connectionTimeoutMillis: 2000,
});

// Холболт шалгах
pool.on('error', (err) => {
  console.error('PostgreSQL холболтын алдаа:', err);
});
bash
# .env файл
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp
DB_USER=postgres
DB_PASSWORD=нууцүг123

Үндсэн асуулгууд

$1, $2 гэсэн parameterized query ашиглах нь SQL injection халдлагаас хамгаалдаг. Хэзээ ч string concatenation ашиглаж SQL үүсгэж болохгүй.

javascript
// queries.js
import { pool } from './db.js';

// Бүх хэрэглэгчдийг авах
export async function getAllUsers() {
  const result = await pool.query(
    'SELECT id, username, email, created_at FROM users ORDER BY created_at DESC'
  );
  return result.rows;  // массив буцаана
}

// ID-аар нэг хэрэглэгч авах
export async function getUserById(id) {
  const result = await pool.query(
    'SELECT id, username, email FROM users WHERE id = $1',
    [id]   // параметрийг тусад нь дамжуулах — SQL injection болохгүй
  );
  return result.rows[0] ?? null;  // нэг мөр буюу null
}

// Шинэ хэрэглэгч үүсгэх
export async function createUser(username, email) {
  const result = await pool.query(
    `INSERT INTO users (username, email, created_at)
     VALUES ($1, $2, NOW())
     RETURNING id, username, email, created_at`,
    [username, email]
  );
  return result.rows[0];
}

// Хэрэглэгч шинэчлэх
export async function updateUser(id, username) {
  const result = await pool.query(
    'UPDATE users SET username = $1 WHERE id = $2 RETURNING *',
    [username, id]
  );
  return result.rows[0] ?? null;
}

// Хэрэглэгч устгах
export async function deleteUser(id) {
  const result = await pool.query(
    'DELETE FROM users WHERE id = $1 RETURNING id',
    [id]
  );
  return result.rowCount > 0;  // устгагдсан уу?
}

Transaction ажиллуулах

Node.js-аас transaction хийхдээ pool.connect() ашиглан нэг холболт авч, тухайн холболт дээр бүх үйлдлийг гүйцэтгэнэ:

javascript
// transaction.js
import { pool } from './db.js';

export async function transferFunds(fromId, toId, amount) {
  const client = await pool.connect();   // холболт авах

  try {
    await client.query('BEGIN');

    // Хасах
    const debit = await client.query(
      'UPDATE accounts SET balance = balance - $1 WHERE id = $2 RETURNING balance',
      [amount, fromId]
    );

    if (debit.rows[0].balance < 0) {
      throw new Error('Дансны үлдэгдэл хүрэлцэхгүй байна');
    }

    // Нэмэх
    await client.query(
      'UPDATE accounts SET balance = balance + $1 WHERE id = $2',
      [amount, toId]
    );

    await client.query('COMMIT');
    return { success: true };

  } catch (error) {
    await client.query('ROLLBACK');
    throw error;   // дээш дамжуулах

  } finally {
    client.release();   // заавал pool-д буцааж өгөх
  }
}

Pagination ба хайлт

javascript
// Pagination ба хайлттай жагсаалт
export async function searchProducts({ query = '', page = 1, limit = 20 }) {
  const offset = (page - 1) * limit;

  const result = await pool.query(
    `SELECT
        id, name, price, category,
        COUNT(*) OVER () AS нийт_тоо
     FROM products
     WHERE name ILIKE $1
     ORDER BY name
     LIMIT $2 OFFSET $3`,
    [`%${query}%`, limit, offset]
  );

  const total = result.rows[0]?.нийт_тоо ?? 0;

  return {
    items:      result.rows,
    total:      Number(total),
    page,
    totalPages: Math.ceil(total / limit),
  };
}

Холболтыг зөв хаах

javascript
// main.js — програм дуусах үед pool-г хаах
import { pool } from './db.js';
import { getAllUsers, createUser } from './queries.js';

async function main() {
  try {
    // Хэрэглэгч үүсгэх
    const user = await createUser('bold_mn', 'bold@example.mn');
    console.log('Үүсгэлээ:', user);

    // Жагсаалт авах
    const users = await getAllUsers();
    console.log('Нийт хэрэглэгч:', users.length);

  } finally {
    // Програм дуусахад pool-г заавал хаах
    await pool.end();
  }
}

main().catch(console.error);

pg library нь хялбар бөгөөд хяналттай. Илүү дэвшилтэт ORM хэрэгтэй бол Prisma эсвэл Drizzle ORM нь PostgreSQL-г маш сайн дэмждэг — гэхдээ тэдгээрийн ард pg л ажиллаж байдаг.

Дараагийн хичээлд:

Курсийн бүх мэдлэгийг нэгтгэн ашиглах эцсийн төсөл — Node.js REST API + PostgreSQL database бүтэн систем бариулна.