နားလည်ထားရမယ့် အချက်
Application က SQL input ကို string concatenate မလုပ်ဘဲ parameters သုံးရပါတယ်။ Pool မှ client တစ်ခု checkout လုပ်ပြီး transaction အတွင်း query အားလုံးကို အဲဒီ client တစ်ခုတည်းဖြင့် run ရပါတယ်။ Unique/foreign-key violation စတဲ့ SQLSTATE codes ကို stable application errors အဖြစ် map လုပ်ပြီး raw database details ကို public response ထဲမဖော်ပါ။
လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
Enrollment endpoint က user/tutorial IDs ကို parameterized insert လုပ်ပြီး progress rows တစ်ပြိုင်နက်ဖန်တီးမယ်။ Error ဖြစ်လျှင် rollback၊ client ကို `finally` မှာ release လုပ်ပြီး retryable transaction errors ကိုသီးခြားကိုင်မယ်။
အတူတူ စမ်းရေးကြည့်မယ်
import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env.DATABASE_URL, max: 10 });
export async function enroll(userId: number, tutorialId: number) {
const client = await pool.connect();
try {
await client.query('BEGIN');
const { rows: [enrollment] } = await client.query(
`INSERT INTO app.enrollments (user_id, tutorial_id)
VALUES ($1, $2) RETURNING enrollment_id`,
[userId, tutorialId],
);
await client.query(
`INSERT INTO app.lesson_progress (enrollment_id, tutorial_id, lesson_id)
SELECT $1, $2, lesson_id FROM app.lessons WHERE tutorial_id = $2`,
[enrollment.enrollment_id, tutorialId],
);
await client.query('COMMIT');
return enrollment;
} catch (error) {
await client.query('ROLLBACK');
throw error;
} finally { client.release(); }
}SQL injection-safe၊ atomic enrollment service function ရမည်။၅ မိနစ် စမ်းကြည့်
Duplicate enrollment SQLSTATE `23505` ကို HTTP 409 response သို့ map လုပ်ပြီး raw constraint detail မဖော်အောင်ရေးပါ။
သတိလေးတစ်ချက်
`pool.query()` များစွာနဲ့ BEGIN/COMMIT ရေးရင် query တစ်ခုစီ connection မတူနိုင်ပြီး transaction မဖြစ်ပါ။
PostgreSQL — libpq Concepts — PostgreSQL Global Development Group