PostgreSQL

Port 5432 Relational data with ACID transactions, joins, and strict schemas.

When to Use

Connection

DATABASE_URL=postgresql://myapp_user:PASSWORD@db.bizfylabs.com:5432/myapp_db

Setup (Node.js / Python)

npm install pg          # Node.js
pip install psycopg2-binary   # Python

// Node.js
import pg from 'pg';
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL, max: 10 });
await pool.query('SELECT 1');

# Python
import os, psycopg2
conn = psycopg2.connect(os.environ["DATABASE_URL"])

Schema — Create Tables

CREATE TABLE users (
  id         UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email      TEXT NOT NULL UNIQUE,
  name       TEXT NOT NULL,
  status     TEXT NOT NULL DEFAULT 'active',
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE orders (
  id         UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id    UUID NOT NULL REFERENCES users(id),
  total      NUMERIC(10,2) NOT NULL,
  status     TEXT NOT NULL DEFAULT 'pending',
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_users_email ON users (email);
CREATE INDEX idx_orders_user_id ON orders (user_id);

CRUD — Node.js

Always use parameterized queries ($1, $2).

Create

const { rows } = await pool.query(
  'INSERT INTO users (email, name) VALUES ($1, $2) RETURNING *',
  [email, name]
);
const newUser = rows[0];

// Bulk insert
await pool.query(
  'INSERT INTO users (email, name) SELECT unnest($1::text[]), unnest($2::text[])',
  [['a@b.com','c@d.com'], ['Alice','Carol']]
);

Read

// Single row
const { rows } = await pool.query('SELECT * FROM users WHERE id = $1', [userId]);

// List with filter + pagination
const { rows: list } = await pool.query(
  'SELECT * FROM users WHERE status = $1 ORDER BY created_at DESC LIMIT $2 OFFSET $3',
  ['active', 20, 0]
);

// Join
const { rows: orders } = await pool.query(
  `SELECT o.*, u.email FROM orders o
   JOIN users u ON u.id = o.user_id WHERE o.user_id = $1`,
  [userId]
);

// Count
const { rows: [{ count }] } = await pool.query(
  'SELECT COUNT(*) FROM users WHERE status = $1', ['active']
);

Update

await pool.query(
  'UPDATE users SET name = $1, updated_at = NOW() WHERE id = $2',
  [newName, userId]
);

// Upsert
await pool.query(
  `INSERT INTO settings (user_id, key, value) VALUES ($1, $2, $3)
   ON CONFLICT (user_id, key) DO UPDATE SET value = EXCLUDED.value`,
  [userId, 'theme', 'dark']
);

Delete

await pool.query('DELETE FROM users WHERE id = $1', [userId]);

// Soft delete
await pool.query(
  "UPDATE users SET status = 'deleted', updated_at = NOW() WHERE id = $1",
  [userId]
);

CRUD — Python

cur = conn.cursor()

# Create
cur.execute('INSERT INTO users (email, name) VALUES (%s, %s) RETURNING id', (email, name))
user_id = cur.fetchone()[0]

# Read
cur.execute('SELECT * FROM users WHERE id = %s', (user_id,))
user = cur.fetchone()

# Update
cur.execute('UPDATE users SET name = %s WHERE id = %s', (new_name, user_id))

# Delete
cur.execute('DELETE FROM users WHERE id = %s', (user_id,))
conn.commit()

Transactions

const client = await pool.connect();
try {
  await client.query('BEGIN');
  await client.query('INSERT INTO orders (user_id, total) VALUES ($1, $2)', [userId, total]);
  await client.query('UPDATE users SET balance = balance - $1 WHERE id = $2', [total, userId]);
  await client.query('COMMIT');
} catch (e) {
  await client.query('ROLLBACK');
  throw e;
} finally {
  client.release();
}

Admin — Create Database & User

Platform admin only. Run on the DB server.
docker exec -it bizfy-postgres psql -U postgres -d bizfy

-- 1. Create database
CREATE DATABASE myapp_db;

-- 2. Create read/write user
CREATE USER myapp_user WITH PASSWORD 'strong-random-password';
GRANT CONNECT ON DATABASE myapp_db TO myapp_user;

-- 3. Grant schema permissions
\c myapp_db
GRANT USAGE ON SCHEMA public TO myapp_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO myapp_user;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO myapp_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
  GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO myapp_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
  GRANT USAGE, SELECT ON SEQUENCES TO myapp_user;

Read-only User

CREATE USER myapp_readonly WITH PASSWORD 'strong-random-password';
GRANT CONNECT ON DATABASE myapp_db TO myapp_readonly;
\c myapp_db
GRANT USAGE ON SCHEMA public TO myapp_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO myapp_readonly;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO myapp_readonly;

Delete User / Database

-- Revoke and drop user
REVOKE ALL PRIVILEGES ON DATABASE myapp_db FROM myapp_user;
\c myapp_db
DROP OWNED BY myapp_user;
DROP USER myapp_user;

-- Drop database (destructive)
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'myapp_db';
DROP DATABASE myapp_db;

List Users & Tables

\l                          -- list databases
\du                         -- list users
\dt                         -- list tables
SELECT * FROM pg_roles;     -- all roles

Send to Developer

DATABASE_URL=postgresql://myapp_user:PASSWORD@db.bizfylabs.com:5432/myapp_db