
Backend · Node.js · PostgreSQL
بکاند برای توسعهدهندگان فرانتاند؛ در سال ۲۰۲۶ چه چیزهایی باید بدانیم؟
۱۴۰۵ مهر · 12 min دقیقه مطالعه
توسعه مدرن فرانتاند دیگر از معماری بکاند جدا نیست. توسعهدهنده React یا Next.js که بداند پشت یک API چه اتفاقی میافتد، میتواند درباره دریافت داده، caching، loading state، امنیت، performance و مدیریت خطا تصمیمهای بسیار بهتری بگیرد.
لازم نیست Database Administrator یا متخصص بکاند شوید. اما اگر با React، Next.js، Node.js، PostgreSQL و Prisma کار میکنید، باید لایه بکاند را آنقدر عمیق بشناسید که بتوانید یک feature قابلاعتماد طراحی کنید.
۱. از چرخه عمر یک Request شروع کنید
قبل از یادگیری فریمورکها، مسیر یک request را بفهمید:
Browser
↓
Frontend / Next.js
↓
API Route / Backend Service
↓
Validation + Authentication
↓
Business Logic
↓
Prisma / SQL
↓
PostgreSQL
↓
Response
↓
UI State
وقتی مشکلی رخ میدهد، این مدل ذهنی کمک میکند بفهمید مشکل دقیقاً در کدام لایه است؛ بهجای اینکه فوراً کد فرانتاند را تغییر دهید.
۲. Node.js؛ محیط اجرای بکاند
Node.js اجازه میدهد JavaScript خارج از مرورگر اجرا شود. برای یک Frontend Developer این موضوع انتقال به بکاند را سادهتر میکند، چون زبان برنامهنویسی همچنان آشناست.
اما محیط کاملاً متفاوت است. خبری از DOM، window یا چرخه رندر مرورگر نیست. در عوض با HTTP request، فایل، دیتابیس، environment variable، stream، process و network I/O سروکار دارید.
یکی از مفاهیم مهم Node.js، I/O غیرهمزمان است. یک query کند نباید کل سرور را متوقف کند، اما کد بد میتواند با درخواستهای متوالی یا عملیات سنگین CPU همچنان latency ایجاد کند.
۳. طراحی API؛ قراردادی که فرانتاند به آن وابسته است
API فقط یک URL نیست که JSON برگرداند؛ API قرارداد بین کلاینت و سرور است.
یک API خوب باید این موارد را قابل پیشبینی کند:
- شکل پارامترها و body
- نیازهای authentication
- ساختار response
- HTTP status codeها
- خطاهای validation
- pagination و filtering
- خطاهای غیرمنتظره سرور
بهجای برگرداندن یک متن خطای تصادفی، بهتر است error codeهای پایدار داشته باشید تا فرانتاند بتواند آنها را به پیامهای فارسی و انگلیسی map کند.
{
"error": {
"code": "EMAIL_ALREADY_EXISTS",
"message": "An account already exists with this email."
}
}
۴. Validation باید در مرز Server انجام شود
Validation در فرانتاند برای UX ضروری است، اما مرز امنیتی نیست. کاربر میتواند تمام validationهای مرورگر را دور بزند و مستقیماً HTTP request بفرستد.
بنابراین ورودی خارجی را قبل از رسیدن به business logic یا database روی سرور validate کنید. ابزارهایی مثل Zod برای این کار مناسباند.
const CreateUser = z.object({
name: z.string().min(2),
email: z.string().email(),
});
const result = CreateUser.safeParse(body);
if (!result.success) {
return Response.json({ error: "INVALID_INPUT" }, { status: 400 });
}
قاعده ساده است: در مرز parse کن و داخل سیستم به داده validateشده اعتماد کن.
۵. PostgreSQL؛ قبل از ترفندهای SQL، مدل داده را یاد بگیرید
برای یک Frontend Developer که میخواهد بکاند را بهتر بفهمد، PostgreSQL یکی از ارزشمندترین مهارتهاست. ابتدا بهجای حفظ کردن queryهای پیچیده، مدلسازی رابطهای را یاد بگیرید.
- Table و Relationship
- Primary و Foreign Key
- رابطههای one-to-one، one-to-many و many-to-many
- Unique Constraint
- Index
- Transaction
- رفتار NULL
- Join و Aggregation
طراحی درست دیتابیس جلوی بسیاری از مشکلاتی را میگیرد که بعداً حل کردنشان در UI بسیار سخت است.
۶. Prisma مفید است، اما SQL همچنان مهم است
Prisma تجربه توسعهدهنده خوبی برای کار با دیتابیس ایجاد میکند. schema آن relationshipها و typeهای تولیدشده را قابلفهمتر میکند.
model User {
id String @id @default(cuid())
email String @unique
orders Order[]
createdAt DateTime @default(now())
}
model Order {
id String @id @default(cuid())
userId String
user User @relation(fields: [userId], references: [id])
total Decimal
createdAt DateTime @default(now())
}
اما Prisma نیاز به فهم SQL را حذف نمیکند. وقتی query کند میشود، باید Join، Index، Cardinality، Query Plan و حجم دادهای که اسکن میشود را بفهمید.
توسعهدهنده بکاند خوب میداند چه زمانی abstraction مربوط به ORM کمککننده است و چه زمانی یک تصمیم مهم دیتابیس را پنهان میکند.
۷. Index؛ ابزار Performance است، نه تزئین
فرض کنید جدول users میلیونها رکورد دارد و مرتب بر اساس email جستوجو میکنید. بدون index مناسب، PostgreSQL ممکن است مجبور شود بخش بزرگی از جدول را scan کند.
CREATE INDEX idx_users_email ON users(email);
Index خواندن را سریعتر میکند، اما هزینه دارد: فضای ذخیرهسازی مصرف میکند و writeها را کمی گرانتر میکند، چون index نیز باید بهروزرسانی شود.
هر ستون را index نکنید. بر اساس queryهای واقعی و اندازهگیری performance تصمیم بگیرید.
۸. Transaction؛ حفظ یکپارچگی چند عملیات
فرض کنید ایجاد سفارش شامل ایجاد order، رزرو موجودی و ثبت payment است. اگر دو عملیات اول موفق و سومی fail شود، ممکن است داده ناسازگار ایجاد شود.
Transaction اجازه میدهد مجموعهای از عملیات دیتابیس در صورت نیاز بهعنوان یک واحد موفق یا شکستخورده اجرا شوند.
await prisma.$transaction(async (tx) => {
const order = await tx.order.create({ data: orderData });
await tx.inventory.update({ ... });
await tx.payment.create({ data: paymentData });
return order;
});
سؤال درست این نیست که «کجا transaction بزنم؟»؛ سؤال این است که «کدام invariantهای کسبوکار باید حتی در صورت شکست وسط عملیات حفظ شوند؟»
۹. Authentication با Authorization فرق دارد
این تفاوت یکی از منابع رایج باگهای production است.
- Authentication: شما چه کسی هستید؟
- Authorization: اجازه انجام چه کاری را دارید؟
لاگین بودن کاربر به این معنی نیست که میتواند سفارشها، داشبورد یا endpointهای مدیریتی را مشاهده یا تغییر دهد.
در هر عملیات حساس باید هویت کاربر و سپس permission یا مالکیت resource در سمت سرور بررسی شود.
مخفی کردن یک button در React را هرگز جایگزین permission check سمت سرور نکنید.
۱۰. Authorization باید کنار داده enforce شود
یک اشتباه رایج این است که permission را فقط در سطح page بررسی کنیم و فرض کنیم عملیات داخلی نیز امن هستند. API و mutationها باید قوانین authorization خودشان را داشته باشند.
مثلاً این کافی نیست:
// UI دکمه حذف را برای کاربران عادی مخفی میکند
بکاند باید همچنان درخواست زیر را رد کند:
DELETE /api/users/123
اگر کاربر فعلی اجازه حذف آن user را ندارد. API را حتی در یک اپلیکیشن private نیز یک مرز ورودی واقعی در نظر بگیرید.
۱۱. Pagination جلوی فاجعههای Performance را میگیرد
ممکن است جدول فرانتاند فقط ۲۰ ردیف نشان دهد؛ این دلیل نمیشود بکاند ۲۰۰ هزار رکورد را برگرداند و React آنها را filter کند.
Pagination، filtering، sorting و limit باید تا حد ممکن نزدیک به منبع داده انجام شوند.
در دیتاستهای بزرگ، cursor-based pagination در بسیاری از سناریوها از offsetهای عمیق پایدارتر است، چون دیتابیس مجبور نیست تعداد زیادی رکورد را صرفاً برای رسیدن به صفحه موردنظر رد کند.
۱۲. Caching؛ بدانید چه چیزی را cache میکنید
Cache قدرتمند است چون کار تکراری و گران را حذف میکند؛ اما اگر داده قدیمی یا با scope اشتباه cache شود، میتواند به مشکل correctness یا حتی security تبدیل شود.
قبل از اضافه کردن cache این سؤالها را بپرسید:
- داده عمومی است یا مخصوص کاربر؟
- چقدر باید تازه باشد؟
- چه چیزی باعث invalidate شدن آن میشود؟
- آیا ممکن است response یک کاربر به کاربر دیگری برسد؟
۱۳. خطاهای Backend باید برای Frontend طراحی شوند
ممکن است بکاند برای همه خطاها 500 برگرداند، اما این کار تجربه محصول را خراب میکند.
خطاهای مورد انتظار دامنه را از خطاهای غیرمنتظره زیرساخت جدا کنید. فرانتاند باید بتواند مواردی مثل اینها را از هم تشخیص دهد:
- ورودی نامعتبر → 400
- احراز هویت نشده → 401
- احراز هویت شده اما بدون دسترسی → 403
- Resource پیدا نشد → 404
- تعارض business → 409
- خطای غیرمنتظره سرور → 500
جزئیات convention میتواند متفاوت باشد، اما اصل ثابت است: Failure Stateها را قابل پیشبینی طراحی کنید.
۱۴. Logging و Observability بخشی از Backend است
وقتی کاربر میگوید «Checkout کار نکرد»، بکاند باید بتواند توضیح دهد چه اتفاقی افتاده است.
- Request ID یا Correlation ID
- Structured Logging
- مدت زمان request
- زمان queryهای دیتابیس
- Error Rate
- خطاهای سرویسهای خارجی
برای Frontend Developer، این یعنی دیباگ کردن از «یک چیزی را تغییر بده و دوباره refresh کن» به trace کردن یک request در کل سیستم تبدیل میشود.
۱۵. یک معماری عملی برای Next.js
برای هر محصولی به microservice architecture نیاز ندارید. یک Next.js application ساختیافته میتواند سیستم را ساده نگه دارد:
app/
api/
dashboard/
lib/
auth/
db/
validation/
services/
prisma/
schema.prisma
features/
users/
orders/
payments/
نام پوشهها مهم نیست. چیزی که اهمیت دارد جداسازی مسئولیتهاست: route handling، validation، business logic، database access و UI نباید تبدیل به یک تابع عظیم شوند.
۱۶. Frontend Developer از کجا شروع کند؟
اگر هدف شما تبدیل شدن به یک Full-stack یا Senior Frontend Engineer قویتر است، همه تکنولوژیهای بکاند را همزمان یاد نگیرید. این ترتیب عملیتر است:
- HTTP: method، header، cookie، status code، CORS و caching
- Node.js: async I/O، module، environment variable، stream و error handling
- API Design: REST، validation، pagination و error contract
- PostgreSQL: relational modeling، join، index، transaction و query plan
- Prisma: schema، migration، relation، transaction و رفتار query
- Security: authentication، authorization، session، cookie، CSRF و input validation
- Observability: log، metric، tracing و production debugging
جمعبندی
هدف یک Frontend Developer این نیست که همه frameworkهای بکاند را بلد باشد. هدف این است که سیستم پشت UI را آنقدر خوب بفهمد که تصمیمهای مهندسی درستی بگیرد.
وقتی Node.js، API، PostgreSQL، Prisma، validation، transaction، authorization، caching و observability را میفهمید، خود فرانتاند شما هم بهتر میشود. کد data-fetching بهتری مینویسید، loading و error state بهتری طراحی میکنید، فرضیات ناامن را کنار میگذارید و ارتباط مؤثرتری با Backend Engineerها دارید.
بهترین Frontend Engineerها در مرز API متوقف نمیشوند؛ میفهمند پشت این مرز چه اتفاقی میافتد.