PostgreSQL کے ساتھ # بوندا باندی ORM: مکمل گائیڈ
Drizzle ORM TypeScript ڈیٹابیس ایکو سسٹم میں ایک منفرد مقام رکھتا ہے: یہ وہ ORM ہے جو درحقیقت بھیس میں صرف ایک سوال بنانے والا ہے، اسکیما کے طور پر کوڈ کے ساتھ جو SQL بناتا ہے جسے آپ پڑھ سکتے ہیں، اور ایک مائیگریشن سسٹم جو آپ کے ڈیٹا بیس کو سچائی کے ماخذ کی طرح سمجھتا ہے۔ Prisma اور TypeORM کے سالوں کے بعد، بوندا باندی کو ایسا محسوس ہوتا ہے جیسے کچے SQL پر گھر آئے — لیکن مکمل TypeScript انفرنس اور صفر رن ٹائم اوور ہیڈ کے ساتھ۔
یہ گائیڈ ابتدائی سیٹ اپ سے لے کر پروڈکشن پیٹرن تک ہر چیز کا احاطہ کرتا ہے، 65+ Drizzle اسکیما فائلوں کے ساتھ کوڈ بیس سے ڈرائنگ، ملٹی کرایہ دار کے سوالات، اور ایک سست پراکسی کنکشن پیٹرن جو Next.js اور NestJS سیاق و سباق دونوں میں قابل اعتماد طریقے سے کام کرتا ہے۔
اہم ٹیک ویز
- db کنکشن کے لیے سست پراکسی کا استعمال کریں — ماڈیول لوڈ ٹائم پر کبھی بھی بے چین کنکشن نہ بنائیں
- مقامی پوسٹگری ایس کیو ایل کو نان ڈیفالٹ پورٹ (5433) پر چلنا چاہیے تاکہ سسٹم کی تنصیبات کے ساتھ تنازعات سے بچا جا سکے۔
- تمام ٹیبلز کو کثیر کرایہ داری کے لیے
organizationIdکی ضرورت ہے — اسکیما کی سطح پر نافذ کریں- بوندا باندی کے مقابلے کے لیے ٹائپ کو تنگ کرنے کے لیے واضح ٹائپ اسکرپٹ کاسٹنگ کی ضرورت ہے
- کبھی بھی
sql.raw()استعمال نہ کریں — ہمیشہ پیرامیٹرائزڈ اقدار کے ساتھsqlٹیمپلیٹ لفظی استعمال کریں- تعلقات غیر ملکی کلیدوں سے الگ ہیں — مکمل قسم کی حفاظت کے لیے دونوں کی وضاحت کریں۔
- لین دین ملٹی سٹیپ آپریشنز کو لپیٹتا ہے۔
txآبجیکٹ کو سروسز تک منتقل کریں۔- اسکیما انڈیکس (
schema/index.ts) کو کام کرنے کے لیے منتقلی کے لیے تمام جدولوں کو برآمد کرنا چاہیے۔
انسٹالیشن اور سیٹ اپ
بوندا باندی کے لیے دو پیکجز کی ضرورت ہوتی ہے: بنیادی ORM اور ڈیٹا بیس ڈرائیور۔
pnpm add drizzle-orm pg
pnpm add -D drizzle-kit @types/pg
منتقلی اور اسٹوڈیو سمیت مکمل اسٹیک کے لیے:
pnpm add drizzle-orm postgres
pnpm add -D drizzle-kit
postgres پیکیج (pg سے مختلف) جدید TypeScript پروجیکٹس میں Drizzle کے لئے تجویز کردہ ڈرائیور ہے — یہ کنکشن پولنگ، تیار کردہ بیانات، اور TypeScript کی بہتر اقسام کی حمایت کرتا ہے۔
سست پراکسی پیٹرن
Next.js یا NestJS monorepo میں بوندا باندی کے ساتھ سب سے اہم آرکیٹیکچرل فیصلہ کبھی بھی ڈیٹابیس کا بے چین کنکشن نہ بنانا ہے۔ ماڈیول لوڈ ٹائم پر بے چین کنکشن مسائل کا سبب بنتے ہیں:
- Next.js: تعمیراتی عمل کے دوران ماڈیول درآمد کیے جاتے ہیں، جہاں کوئی DB دستیاب نہیں ہے۔
- NestJS: پیکیج کی درآمدات ماحولیاتی متغیرات کے لوڈ ہونے سے پہلے ہوتی ہیں۔
- سرور لیس: سرد شروع ہونے میں کنکشن اوور ہیڈ کی وجہ سے تاخیر ہوتی ہے۔
سست پراکسی پیٹرن ان سب کو حل کرتا ہے:
// packages/db/src/index.ts
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import * as schema from './schema';
let _db: ReturnType<typeof drizzle> | null = null;
function getDb() {
if (!_db) {
const connectionString = process.env.DATABASE_URL;
if (!connectionString) {
throw new Error('DATABASE_URL environment variable is not set');
}
const client = postgres(connectionString, {
max: 10, // connection pool size
idle_timeout: 20,
connect_timeout: 10,
});
_db = drizzle(client, { schema });
}
return _db;
}
// Export a Proxy that initializes the connection on first use
export const db = new Proxy({} as ReturnType<typeof drizzle>, {
get(_, prop) {
return getDb()[prop as keyof ReturnType<typeof drizzle>];
},
});
export * from './schema';
اس پیٹرن کا مطلب ہے @ecosire/db کو درآمد کرنا کبھی بھی ڈیٹا بیس کنکشن نہیں کھولتا ہے۔ کنکشن صرف اس وقت بنتا ہے جب پہلا سوال چلتا ہے۔
اسکیما ڈیزائن
بوندا باندی سکیما سادہ TypeScript فائلیں ہیں جو ٹیبل کی تعریفیں برآمد کرتی ہیں۔ یہاں کا نظم و ضبط یہ ہے کہ متعلقہ ٹیبلز کو ایک ہی فائل میں رکھیں اور ہر چیز کو مرکزی انڈیکس کے ذریعے برآمد کریں۔
// packages/db/src/schema/contacts.ts
import {
pgTable,
uuid,
text,
varchar,
timestamp,
pgEnum,
boolean,
index,
} from 'drizzle-orm/pg-core';
export const contactTypeEnum = pgEnum('contact_type', ['individual', 'company', 'partner']);
export const contacts = pgTable(
'contacts',
{
id: uuid('id').defaultRandom().primaryKey(),
organizationId: uuid('organization_id').notNull(), // Multi-tenancy
name: varchar('name', { length: 255 }).notNull(),
email: varchar('email', { length: 255 }),
phone: varchar('phone', { length: 50 }),
type: contactTypeEnum('type').default('individual').notNull(),
isActive: boolean('is_active').default(true).notNull(),
notes: text('notes'),
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
},
(table) => ({
organizationIdx: index('contacts_organization_idx').on(table.organizationId),
emailIdx: index('contacts_email_idx').on(table.email),
})
);
export type Contact = typeof contacts.$inferSelect;
export type NewContact = typeof contacts.$inferInsert;
$inferSelect اور $inferInsert قسمیں آپ کو مکمل TypeScript کا اندازہ دیتی ہیں — بوندا باندی جانتی ہے کہ کون سے فیلڈز درکار ہیں بمقابلہ داخل کرنے کے وقت آپ کے اسکیما کی رکاوٹوں کی بنیاد پر اختیاری۔
// packages/db/src/schema/index.ts
export * from './contacts';
export * from './orders';
export * from './licenses';
export * from './products';
// ... all tables
انڈیکس فائل کو ہر ٹیبل کو drizzle-kit کے لیے ایکسپورٹ کرنا چاہیے تاکہ انہیں منتقلی کے دوران اٹھا سکے۔
تعلقات
بوندا باندی غیر ملکی کلیدی رکاوٹوں (پوسٹگری ایس کیو ایل کے ذریعے نافذ کردہ) کو ریلیشن ڈیفینیشنز سے الگ کرتی ہے (Drizzle's Relational query API کے ذریعے استعمال کیا جاتا ہے)۔ آپ کو دونوں کی ضرورت ہے:
// packages/db/src/schema/relations.ts
import { relations } from 'drizzle-orm';
import { contacts } from './contacts';
import { orders } from './orders';
import { orderItems } from './order-items';
import { products } from './products';
export const contactsRelations = relations(contacts, ({ many }) => ({
orders: many(orders),
}));
export const ordersRelations = relations(orders, ({ one, many }) => ({
contact: one(contacts, {
fields: [orders.contactId],
references: [contacts.id],
}),
items: many(orderItems),
}));
export const orderItemsRelations = relations(orderItems, ({ one }) => ({
order: one(orders, {
fields: [orderItems.orderId],
references: [orders.id],
}),
product: one(products, {
fields: [orderItems.productId],
references: [products.id],
}),
}));
تعلقات کی وضاحت کے ساتھ، آپ Drizzle کے متعلقہ استفسار API کا استعمال کر سکتے ہیں:
// Fetch order with contact and items in one query
const order = await db.query.orders.findFirst({
where: eq(orders.id, orderId),
with: {
contact: true,
items: {
with: {
product: true,
},
},
},
});
بوندا باندی ایک واحد جوائن سوال پیدا کرتی ہے — کوئی N+1 مسئلہ نہیں۔
ٹائپ سیف سوالات
بوندا باندی کا استفسار API مکمل طور پر ٹائپ سیف ہے۔ منتخب کردہ قسم اس بنیاد پر تنگ ہوتی ہے کہ آپ کون سے کالم شامل کرتے ہیں:
import { db } from '@ecosire/db';
import { contacts } from '@ecosire/db/schema';
import { eq, and, like, sql, count } from 'drizzle-orm';
// Full select — returns Contact[]
const allContacts = await db
.select()
.from(contacts)
.where(eq(contacts.organizationId, orgId))
.limit(50);
// Partial select — returns only specified columns
const contactNames = await db
.select({
id: contacts.id,
name: contacts.name,
email: contacts.email,
})
.from(contacts)
.where(
and(
eq(contacts.organizationId, orgId),
eq(contacts.isActive, true)
)
);
// Count query
const [{ total }] = await db
.select({ total: count() })
.from(contacts)
.where(eq(contacts.organizationId, orgId));
// Search with ILIKE (case-insensitive)
const searchResults = await db
.select()
.from(contacts)
.where(
and(
eq(contacts.organizationId, orgId),
like(contacts.name, `%${searchTerm}%`)
)
)
.limit(20);
ڈائنامک کے لیے جہاں شقیں (UI سے فلٹرز)، حالات کو مشروط طور پر بنائیں:
import { SQL } from 'drizzle-orm';
async function searchContacts(orgId: string, filters: {
search?: string;
type?: 'individual' | 'company';
isActive?: boolean;
}) {
const conditions: SQL[] = [eq(contacts.organizationId, orgId)];
if (filters.search) {
conditions.push(like(contacts.name, `%${filters.search}%`));
}
if (filters.type) {
// Enum comparisons need explicit casting
conditions.push(eq(contacts.type, filters.type as 'individual' | 'company' | 'partner'));
}
if (filters.isActive !== undefined) {
conditions.push(eq(contacts.isActive, filters.isActive));
}
return db
.select()
.from(contacts)
.where(and(...conditions))
.limit(100);
}
enum اقدار پر واضح کاسٹ نوٹ کریں: filters.type as 'individual' | 'company' | 'partner'. بوندا باندی کی اینوم قسم خود بخود تنگ سٹرنگ ذیلی قسموں کو کاسٹ کے بغیر قبول نہیں کرتی ہے۔
داخل کریں، اپ ڈیٹ کریں اور حذف کریں۔
import { db } from '@ecosire/db';
import { contacts, NewContact } from '@ecosire/db/schema';
import { eq, and } from 'drizzle-orm';
// Insert with RETURNING
async function createContact(data: NewContact) {
const [created] = await db
.insert(contacts)
.values(data)
.returning();
return created; // Fully typed as Contact
}
// Upsert (insert or update on conflict)
async function upsertContact(data: NewContact) {
const [result] = await db
.insert(contacts)
.values(data)
.onConflictDoUpdate({
target: [contacts.organizationId, contacts.email],
set: {
name: data.name,
updatedAt: new Date(),
},
})
.returning();
return result;
}
// Update with RETURNING
async function updateContact(
id: string,
orgId: string,
updates: Partial<NewContact>
) {
const [updated] = await db
.update(contacts)
.set({ ...updates, updatedAt: new Date() })
.where(
and(
eq(contacts.id, id),
eq(contacts.organizationId, orgId) // Always filter by org
)
)
.returning();
return updated;
}
// Soft delete (recommended over hard delete)
async function deleteContact(id: string, orgId: string) {
return db
.update(contacts)
.set({ isActive: false, updatedAt: new Date() })
.where(
and(
eq(contacts.id, id),
eq(contacts.organizationId, orgId)
)
);
}
INSERT اور UPDATE اسٹیٹمنٹس پر ہمیشہ .returning() استعمال کریں — یہ تخلیق شدہ/اپ ڈیٹ شدہ ریکارڈ حاصل کرنے کے لیے دوسرے SELECT راؤنڈ ٹرپ سے گریز کرتا ہے۔
لین دین
ملٹی سٹیپ آپریشنز جن کو ایک ساتھ کامیاب یا ناکام ہونا چاہیے ان کے لیے لین دین کی ضرورت ہے:
async function createOrderWithItems(
orderData: NewOrder,
items: NewOrderItem[]
) {
return db.transaction(async (tx) => {
// Create the order
const [order] = await tx
.insert(orders)
.values(orderData)
.returning();
// Create order items
const createdItems = await tx
.insert(orderItems)
.values(items.map((item) => ({ ...item, orderId: order.id })))
.returning();
// Update inventory (must succeed or entire transaction rolls back)
for (const item of items) {
await tx
.update(products)
.set({
stock: sql`${products.stock} - ${item.quantity}`,
})
.where(eq(products.id, item.productId));
}
return { order, items: createdItems };
});
}
لین دین کے سیاق و سباق کو سروس کے طریقوں میں منتقل کرتے وقت:
// Service accepts optional transaction for composability
async function activateLicense(
licenseId: string,
tx?: typeof db | Parameters<Parameters<typeof db.transaction>[0]>[0]
) {
const queryRunner = tx || db;
return queryRunner
.update(licenses)
.set({ status: 'active', activatedAt: new Date() })
.where(eq(licenses.id, licenseId))
.returning();
}
بوندا باندی کٹ کے ساتھ ہجرت
drizzle-kit CLI اسکیما تبدیلیوں سے منتقلی پیدا کرتا ہے:
// drizzle.config.ts
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './src/schema/index.ts',
out: './src/migrations',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
verbose: true,
strict: true,
});
ورک فلو:
# Generate migration SQL from schema changes
npx drizzle-kit generate
# Apply migrations to database
npx drizzle-kit migrate
# Push schema directly (dev only — skips migration files)
npx drizzle-kit push
# Open Drizzle Studio (visual database browser)
npx drizzle-kit studio
پیداوار کے لیے، ہمیشہ migrate استعمال کریں (push نہیں)۔ push کمانڈ مقامی ترقیاتی تکرار کے لیے ہے — یہ منتقلی کی فائلیں نہیں بناتا، اس لیے تبدیلیوں کا پتہ نہیں لگایا جاتا۔
صفحہ بندی کا پیٹرن
موثر صفحہ بندی کے لیے ڈیٹا کے استفسار اور شمار کے استفسار دونوں کی ضرورت ہوتی ہے:
interface PaginationParams {
page: number;
pageSize: number;
}
async function getContactsPaginated(orgId: string, { page, pageSize }: PaginationParams) {
const offset = (page - 1) * pageSize;
const [data, [{ total }]] = await Promise.all([
db
.select()
.from(contacts)
.where(eq(contacts.organizationId, orgId))
.limit(pageSize)
.offset(offset)
.orderBy(contacts.createdAt),
db
.select({ total: count() })
.from(contacts)
.where(eq(contacts.organizationId, orgId)),
]);
return {
data,
pagination: {
page,
pageSize,
total,
totalPages: Math.ceil(total / pageSize),
},
};
}
دونوں استفسارات کو Promise.all کے ساتھ متوازی طور پر چلانے سے ترتیب وار عمل کے مقابلے میں جوابی وقت آدھا رہ جاتا ہے۔
مشترکہ نقصانات اور حل
خطرہ 1: متحرک سوالات کے لیے sql.raw() کا استعمال
// Dangerous — SQL injection vulnerability
const results = await db.execute(
sql.raw(`SELECT * FROM contacts WHERE name = '${userInput}'`)
);
// Safe — parameterized
const results = await db.execute(
sql`SELECT * FROM contacts WHERE name = ${userInput}`
);
خطرہ 2: اکثر پوچھے جانے والے کالموں پر گمشدہ اشاریہ جات
ان کالموں میں ہمیشہ اشاریہ جات شامل کریں جس کے ذریعے آپ فلٹر کرتے ہیں۔ سب سے عام یاد شدہ اشاریہ جات:
(table) => ({
organizationIdx: index('contacts_org_idx').on(table.organizationId),
emailIdx: index('contacts_email_idx').on(table.email),
compositeIdx: index('contacts_org_active_idx').on(
table.organizationId,
table.isActive
),
})
پِٹفال 3: مینوئل ریلیشن لوڈنگ سے N+1 سوالات
// N+1 problem — one query per order
const orders = await db.select().from(ordersTable);
for (const order of orders) {
order.contact = await db
.select()
.from(contacts)
.where(eq(contacts.id, order.contactId));
}
// Solution — use Drizzle's relational query API
const orders = await db.query.orders.findMany({
with: { contact: true },
});
خطرہ 4: schema/index.ts سے نئی میزیں برآمد کرنا بھول جانا
اگر آپ ایک نئی اسکیما فائل بناتے ہیں لیکن schema/index.ts سے دوبارہ برآمد کرنا بھول جاتے ہیں، drizzle-kit کو نیا ٹیبل نظر نہیں آئے گا اور منتقلی پیدا نہیں ہوگی۔
اکثر پوچھے گئے سوالات
TypeScript پروجیکٹس کے لیے بوندا باندی کا موازنہ Prisma سے کیسے ہوتا ہے؟
بوندا باندی TypeScript کی اقسام کے ساتھ خام SQL کے قریب ہے — آپ ایسے سوالات لکھتے ہیں جو 1:1 کو SQL پر نقشہ بناتے ہیں۔ Prisma حسب ضرورت استفسار کی زبان اور علیحدہ اسکیما فائل کے ساتھ مزید خلاصہ کرتا ہے۔ بوندا باندی میں پیچیدہ سوالات، چھوٹے رن ٹائم اوور ہیڈ میں TypeScript کا بہتر اندازہ ہوتا ہے، اور کوڈ جنریشن کے مرحلے کی ضرورت نہیں ہوتی ہے۔ Prisma میں ایک بڑا ماحولیاتی نظام ہے اور نرم ڈیلیٹ اور آڈٹ لاگنگ جیسی مزید بلٹ ان خصوصیات ہیں۔ بڑی ٹیموں کے لیے جو SQL کے ساتھ آرام دہ ہیں، عام طور پر بوندا باندی کو ترجیح دی جاتی ہے۔
کیا مجھے drizzle-kit push استعمال کرنا چاہیے یا CI/CD میں منتقل کرنا چاہیے؟
CI/CD میں ہمیشہ drizzle-kit migrate استعمال کریں۔ push کمانڈ تیز رفتار مقامی ترقی کے لیے ہے — یہ مائیگریشن فائلیں بنائے بغیر اسکیما تبدیلیوں کو براہ راست لاگو کرتا ہے، اس لیے کوئی آڈٹ ٹریل نہیں ہے اور رول بیک مشکل ہے۔ اپنی تعیناتی پائپ لائن میں، ایپلیکیشن شروع کرنے سے پہلے drizzle-kit migrate چلائیں تاکہ یہ یقینی بنایا جا سکے کہ سکیما مطابقت پذیر ہے۔
میں بوندا باندی کے ساتھ ڈیٹا بیس سیڈنگ کو کیسے ہینڈل کروں؟
ایک الگ سیڈ فائل بنائیں اور اپنا سکیما براہ راست درآمد کریں۔ کمزور بیجوں کے لیے onConflictDoNothing() استعمال کریں جو دوبارہ چلانے پر ناکام نہیں ہوں گے۔ آسان اپ ڈیٹس کے لیے سیڈ ڈیٹا کو مستقل میں اسٹور کریں، اور اپنے دیو سیٹ اپ اسکرپٹ میں منتقلی کے بعد بیج چلائیں۔
میں بوندا باندی اور PostgreSQL کے ساتھ مکمل متن کی تلاش کیسے کروں؟
PostgreSQL کے to_tsvector اور to_tsquery فنکشنز کے ساتھ sql ٹیمپلیٹ لفظی استعمال کریں۔ بوندا باندی مکمل متن کی تلاش کو خلاصہ نہیں کرتی ہے، لیکن آپ اسے براہ راست لکھ سکتے ہیں۔ کارکردگی کے لیے tsvector کالم پر ایک GIN انڈیکس شامل کریں، اور قدرتی زبان کے سوالات کو محفوظ طریقے سے پارس کرنے کے لیے صارف کی فراہم کردہ تلاش کی اصطلاحات کے لیے websearch_to_tsquery استعمال کریں۔
میں بغیر ٹائم ٹائم کے اسکیما تبدیلیوں کو کیسے ہینڈل کروں؟
اضافی منتقلی کا استعمال کریں - پہلے کالم کو کالعدم کے طور پر شامل کریں، بیک فل ڈیٹا، پھر NOT NULL رکاوٹ شامل کریں۔ کالم کو کبھی بھی اسی منتقلی میں نہ چھوڑیں جو انہیں کوڈ سے ہٹاتا ہے۔ ترتیب یہ ہے: 1) پرانے کالم کو نظر انداز کرنے والے کوڈ کو تعینات کریں، 2) اسے چھوڑنے کے لیے منتقلی تعینات کریں۔ یہ یقینی بناتا ہے کہ ڈیٹا ضائع کیے بغیر رول بیک ہمیشہ ممکن ہے۔
اگلے اقدامات
ایک اچھی طرح سے ڈیزائن کردہ ڈیٹا بیس پرت وہ بنیاد ہے جس پر ہر پروڈکشن ایپلیکیشن بنائی گئی ہے۔ ECOSIRE کی انجینئرنگ ٹیم روزانہ Drizzle ORM اور PostgreSQL کے ساتھ کام کرتی ہے، ایک پیچیدہ کثیر کرایہ دار نظام میں 65+ اسکیما فائلوں کا انتظام کرتی ہے۔
چاہے آپ کو ڈیٹا بیس فن تعمیر سے متعلق مشاورت، Odoo ERP انضمام، یا جدید TypeScript ٹولنگ کے ساتھ بنایا گیا ایک مکمل بیک اینڈ سسٹم کی ضرورت ہو، یہ جاننے کے لیے کہ ہم کس طرح مدد کر سکتے ہیں۔
تحریر
ECOSIRE TeamTechnical Writing
The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.
متعلقہ مضامین
Zero-Downtime Database Migrations with Drizzle ORM
Run database migrations without downtime using Drizzle ORM. Covers expand-contract pattern, backward-compatible schema changes, rollback strategies, and CI/CD integration for PostgreSQL.
Odoo Performance Tuning: PostgreSQL and Server Optimization
Expert guide to Odoo 19 performance tuning. Covers PostgreSQL configuration, indexing, query optimization, Nginx caching, and server sizing for enterprise deployments.
Natural Language Database Queries with OpenClaw
How OpenClaw enables natural language database queries, translating plain English business questions into accurate SQL without exposing database credentials or query complexity.