Codoloper

آموزش برنامه‌نویسی به فارسی

یاد بگیر، پیشرفت کن، بساز.

منبع آموزشی برنامه‌نویسی به زبان فارسی - مستندات، دوره‌ها و مطالب کاربردی برای همه سطوح.

داکیومنت ها:

JavascriptJetpack ComposeHTMLCSSبیشتر
1// برنامه‌نویسی به فارسی
2import { learn } from 'codoloper'
3
4const developer = learn({
5"lang": "فارسی"
6"level": "همه سطوح"
7free: true
8})
240+
صفحه مستندات
6+
زبان ها / فریمورک ها
رایگان
دسترسی کامل
چرا کدلپر؟

ارزش‌های ما

یادگیری برنامه‌نویسی نباید پیچیده باشد - ما مسیر را ساده می‌کنیم.

کیفیت بالا
مطالب آموزشی با بالاترین استانداردها تهیه می‌شوند.
همه سطوح
از مبتدی تا پیشرفته، آموزش برای همه.
نوآوری
روش‌های خلاقانه برای یادگیری بهتر و سریع‌تر.
جامعه پویا
یک جامعه حمایت‌کننده از برنامه‌نویسان فارسی‌زبان.
بروزرسانی روزانه
هر روز محتوای جدید اضافه می‌شود.
دسترسی آزاد
همه محتوا رایگان و در دسترس همه است.
بلاگ

مطالب جدید

تازه‌ترین مطالب آموزشی و خبرهای مرتبط.

Next.js 16.3: وقتی اپ سرور-محورت مثل SPA رفتار می‌کنه نوشته شده توسط عرفان دهقانی

Next.js 16.3: وقتی اپ سرور-محورت مثل SPA رفتار می‌کنه

یه انتقاد قدیمی به Next.js وجود داشته که هرچقدر هم منطقی باشه، همیشه کمی ناراحت‌کننده بود: وقتی روی یه لینک کلیک می‌کنی، چیزی نمی‌شه. صفحه فقط منتظر می‌مونه تا سرور جواب بده، بعد یهو همه چیز ظاهر می‌شه. این رفتار برای یه بلاگ یا سایت خبری کاملاً قابل قبوله، ولی برای یه اپ داشبورد یا پروداکتیو، حس می‌ده داری از سایت‌های دهه نود استفاده می‌کنی.

در حالی که SPAها از روز اول همین مشکل رو حل کرده بودن: کلیک می‌کنی، یه shell از صفحه‌ی بعدی بلافاصله نشون داده می‌شه — حتی اگه داده‌ها هنوز دارن لود می‌شن. این حس «instant» بودن چیزیه که خیلی از توسعه‌دهنده‌ها رو به سمت SPAها می‌کشه، حتی وقتی می‌دونن که مدل سرور-محور Next.js از نظر فنی برتری‌های زیادی داره.

۲۵ ژوئن ۲۰۲۶، تیم Next.js یه پیش‌نمایش از نسخه‌ی ۱۶.۳ منتشر کرد که مستقیماً همین مشکل رو هدف گرفته: Instant Navigations.

ایده‌ی اصلی چیه

وقتی یه route توی Next.js به داده‌ای از سرور await می‌کنه، سه گزینه داری:

Stream با <Suspense>: کاربر بلافاصله یه loading shell می‌بینه و داده‌ها بعداً stream می‌شن. navigation ایمدیت، داده‌ها دیرتر.

Cache با 'use cache': کاربر یه UI کش‌شده‌ی قبلی رو می‌بینه، تا زمانی که نسخه‌ی جدید آماده بشه. باز هم navigation ایمدیت.

Block با export const instant = false: بگی این route نباید instant باشه — مثلاً یه بلاگ که نمی‌خوای shell خالی صفحه‌ی پست رو نشون بده. navigation منتظر سرور می‌مونه.

دو گزینه‌ی اول navigation رو به حالت SPA-like در میارن. گزینه‌ی سوم اختیارى‌ه و بهت کنترل می‌ده.

برای فعال کردنش، یه flag توی next.config.ts اضافه می‌کنی:

const nextConfig: NextConfig = {
  cacheComponents: true,
};

این flag قراره در یه major version آینده به حالت default تبدیل بشه.

prefetching رو هم از اول طراحی کردن

یه مشکل دیگه هم وجود داشت که کمتر بهش توجه می‌شد: حتی اگه سرور سریع جواب بده، هنوز یه رفت‌وبرگشت شبکه بین client و سرور داری. Next.js قبلاً این مشکل رو با prefetch کردن لینک‌ها حل می‌کرد — برای هر لینکی که توی viewport بود، یه request جداگانه می‌زد. اگه یه sidebar با بیست لینک داشتی، بیست request. اگه به Network tab نگاه می‌کردی، یه طوفان از request‌ها می‌دیدی که به گفته‌ی خود تیم Next.js «مسخره به نظر می‌رسید».

توی ۱۶.۳ این رویکرد عوض شده: به جای prefetch کردن به‌ازای هر لینک، حالا یه shell به‌ازای هر route prefetch می‌شه. یعنی اگه بیست لینک داری که همه به /chat/[id] اشاره می‌کنن، فقط یه بار shell اون route دانلود می‌شه و کش می‌شه.

برای فعال کردن این رفتار:

const nextConfig: NextConfig = {
  cacheComponents: true,
  partialPrefetching: true,
};

البته اگه بخوای برای یه لینک خاص بیشتر از shell prefetch بشه — مثلاً header یه صفحه‌ی chat — می‌تونی با <Link prefetch={true}> opt-in کنی. توی اون حالت هم Next.js فقط تا جایی prefetch می‌کنه که synchronous یا 'use cache' باشه، نه کل route.

Instant Insights: slow navigation به‌عنوان error

چیزی که توی این آپدیت جالبه اینه که تیم Next.js تصمیم گرفته navigation‌های کند رو توی development mode به‌عنوان error نشون بده — نه warning، error. یه پنل جدید به اسم Instant Insights اضافه شده که به‌طور خودکار route‌هایی که instant نیستن رو پیدا می‌کنه و نشون می‌ده.

برای تست هم یه helper اضافه شده:

import { instant } from '@next/playwright';

test('navigation is instant', async ({ page }) => {
  await page.goto('/products/shoes');
  await instant(page, async () => {
    await page.click('a[href="/products/hats"]');
    await expect(page.locator('h1')).toContainText('Baseball Cap');
    await expect(page.getByText('Checking inventory...')).toBeVisible();
  });
});

این یعنی می‌تونی توی CI هم چک کنی که navigation‌های instant بودنشون رو از دست ندادن.

یه Navigation Inspector هم اضافه شده که بهت اجازه می‌ده هر navigation رو «pause» کنی و ببینی shell چه شکلیه، قبل از اینکه داده‌های واقعی بیان.

چقدر واقعاً فرق می‌کنه

تیم Next.js گفته که این feature رو روی v0 — اپ داخلی Vercel — قبل از release استفاده کردن. نتیجه‌ای که منتشر کردن نشون می‌ده که navigation time از چند صد میلی‌ثانیه به نزدیک صفر رسیده برای اکثر route‌ها، بعد از اینکه کار روی route‌های کند رو تموم کردن.

البته این هنوز preview هست و یه سری محدودیت‌های شناخته‌شده هم وجود داره: بعضی route‌های blocking توی Instant Insights گزارش نمی‌شن، و tooling توی Safari مشکل داره. تیم گفتن که این‌ها قبل از stable release برطرف می‌شن.

چطور امتحانش کنی

npm install next@preview

بعدش cacheComponents: true رو به config اضافه کن و ببین کدوم route‌هات blocking هستن. اگه می‌خوای Partial Prefetching هم باشه، partialPrefetching: true رو هم اضافه کن.

یه دمو هم آماده کردن به اسم Next Beats — یه موزیک‌پلیر که روی Next.js 16.3 ساخته شده و سورسش هم روی GitHub هست — که می‌تونی ببینی navigation‌های instant در عمل چطور به نظر می‌رسن.

این مسیری که Next.js داره طی می‌کنه — گرفتن بهترین چیز SPA (navigation ایمدیت) بدون دادن مزایای server-centric — از نظر فنی جالبه. مشکل اصلی که همیشه بود این بود که مجبور بودی یا همه‌چیز رو client‌ side بکنی تا instant باشه، یا server‌ side بروی و سرعت navigation رو فدا کنی. حالا دیگه این trade‌off اجباری نیست.

متد QUERY در HTTP: وقتی GET و POST هیچ‌کدوم جواب نمی‌دن نوشته شده توسط عرفان دهقانی

متد QUERY در HTTP: وقتی GET و POST هیچ‌کدوم جواب نمی‌دن

یه سوال قدیمی توی طراحی API وجود داشت که همه باهاش کنار اومده بودن: وقتی می‌خوای یه query پیچیده بفرستی و چیزی رو بخوانی — نه تغییر بدی — چه متدی استفاده می‌کنی؟ GET بدنه نداره، POST هم از نظر پروتکل یعنی «دارم یه چیزی رو تغییر می‌دم». نتیجه؟ همه یه endpoint مثل POST /search می‌ساختن و ادامه می‌دادن.

ژوئن ۲۰۲۶، IETF این مشکل رو رسمی حل کرد. RFC 10008 تاریخ ۱۵ ژوئن ۲۰۲۶ منتشر شد و متد جدیدی به اسم QUERY رو برای HTTP تعریف می‌کنه. این اولین متد کاملاً جدید HTTP در حدود ۱۶ سال گذشته است.

مشکل واقعی چی بود

GET برای درخواست‌های read-only ایده‌آله، ولی یه محدودیت بزرگ دارد: همه چیز باید توی URL باشد. برای یه فیلتر ساده مثل ?city=Berlin&limit=50 کافیه، ولی وقتی query پیچیده می‌شود — مثلاً یه فیلتر تو در تو با چند شرط، یا یه کوئری SQL کامل — URL به سرعت به جایی می‌رسد که دیگر نمی‌توان باهاش کار کرد. RFC 9110 فقط توصیه می‌کند که پیاده‌سازی‌ها حداقل ۸۰۰۰ بایت URI را پشتیبانی کنند، و این یه کف است، نه یه تضمین. محدودیت واقعی را وقتی کشف می‌کنی که پشت یه پراکسی یا لود بالانسر قدیمی هستی — و test suite‌ات آن را نشان نمی‌دهد.

POST هم یه راه‌حل دیگر بود، ولی از نظر پروتکل HTTP مشکل داشت.POST نه safe هست و نه idempotent. وقتی connection وسط درخواست قطع می‌شود، نه client نه هیچ پراکسی‌ای نمی‌تواند بداند که آیا state روی سرور عوض شده یا نه. در نتیجه هیچ‌چیزی به‌طور خودکار retry نمی‌کنه. علاوه بر این، cache هم برای POST کار نمی‌کند — هر بار باید به origin برگردد.

یه مشکل ظریف‌تر هم بود که کمتر بهش توجه می‌شد: URL ها leak می‌کنند. request URI‌ها بیشتر از request body توی log‌ها ثبت می‌شوند. اگه query ات اطلاعات حساسی دارد، الان توی هر access log در طول مسیر است.

QUERY چطور این مشکل‌ها رو حل می‌کنه

QUERY دقیقاً همان چیزی است که GET و POST هیچ‌کدام نبودند: یه درخواست safe، idempotent و cacheable که بدنه هم دارد.

QUERY /contacts HTTP/1.1
Content-Type: application/json

{
  "filter": { "city": "Berlin" },
  "limit": 50
}

این یعنی body خود query است — نه یه resource که باید ذخیره شود، نه یه command. سرور آن را پردازش می‌کند و نتیجه را برمی‌گرداند.

مقایسه‌ی سه متد از نظر پروتکل:

ویژگیGETQUERYPOST
Safeآرهآره
Idempotentآرهآره
Cacheableآرهآرهمحدود
Request bodyندارددارددارد

یه مکانیزم discovery هم اضافه شده: سرور می‌تواند با هدر جدید Accept-Query اعلام کند که چه media typeهایی را برای QUERY قبول می‌کند:

Accept-Query: "application/json", application/sql

کش کردن چطور کار می‌کنه

QUERY قابل cache است، ولی کمی پیچیده‌تر از GET. cache key باید شامل محتوای request هم باشد، نه فقط URI. کش باید ابتدا کل body را بخواند و بعد تصمیم بگیرد آیا قبلاً این درخواست را دیده یا نه.

برای جلوگیری از miss‌های بی‌دلیل، cache‌ها اجازه دارند تفاوت‌های بی‌معنی را نرمالایز کنند — مثلاً ترتیب فیلدهای JSON. ولی اگه این نرمالایزیشن خیلی aggressive باشد، ممکن است cache جواب اشتباه را برگرداند. این edge case اصلی‌ایه که باید بهش توجه داشت.

چه کسی الان پشتیبانی می‌کنه

Node.Js از اوایل ۲۰۲۴ متد QUERY را به‌صورت native parse می‌کند، OpenAPI 3.2 می‌تواند آن را document کند، و Spring یه pull request باز دارد. وضعیت دقیق‌تر به این شکل است:

Stackوضعیت (جولای ۲۰۲۶)
Node.js✅ Native — از Node 21.7.2 و Node 22+
Expressکار می‌کند، ولی TypeScript type ندارد
Fastifyopt-in با addHttpMethod('QUERY', { hasBody: true })
Go net/httpبدون constant، ولی method string کار می‌کند
Spring❌ PR باز دارد، هنوز merge نشده
ASP.NET Core✅ در .NET 11 Preview 4 پشتیبانی می‌شود
OpenAPI✅ از نسخه ۳.۲.۰ (سپتامبر ۲۰۲۵)
curlبا -X QUERY --data و Content-Type
مرورگر (fetch)قبول می‌کند، ولی همیشه CORS preflight می‌زند

یه نکته مهم درباره مرورگر: QUERY یه CORS-safelisted method نیست، پس هر درخواست cross-origin یه preflight می‌زند. هزینه‌ی این OPTIONS اضافه را باید در نظر بگیری.

الان چطور امتحانش کنی

با curl و Node 22 همین الان می‌شود یه endpoint کاملاً کارکرد ساخت:

curl -X QUERY 'http://localhost:3000/contacts' \
  -H 'Content-Type: application/json' \
  -d '{"filter": {"city": "Berlin"}, "limit": 50}'

سمت سرور روی Node، req.method مقدار 'QUERY' را برمی‌گرداند:

import { createServer } from 'node:http';

const server = createServer((req, res) => {
  if (req.method !== 'QUERY') {
    res.writeHead(405, { Allow: 'QUERY' });
    res.end();
    return;
  }

  let body = '';
  req.on('data', chunk => (body += chunk));
  req.on('end', () => {
    const query = JSON.parse(body);
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ query, results: [] }));
  });
});

server.listen(3000);

از fetch هم همین‌طور کار می‌کند:

const res = await fetch('http://localhost:3000/contacts', {
  method: 'QUERY',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ filter: { city: 'Berlin' }, limit: 50 }),
});

اگه روی Spring هستی، فعلاً باید صبر کنی یا یه servlet filter دستی بنویسی که درخواست‌های QUERY را پیش از Spring MVC بگیرد — ولی این یه راه‌حل موقتی است.

قبل پایانم این واژه هارو هم گفتم توضیح بدم که چی هستند:

Preflight Request:
درخواست اولیه‌ای از نوع OPTIONS که مرورگر قبل از ارسال درخواست اصلی در CORS می‌فرستد تا بررسی کند سرور اجازه دسترسی می‌دهد یا نه.

Safe Methods:
متدهایی که فقط برای خواندن داده استفاده می‌شوند و نباید هیچ تغییری در state سرور ایجاد کنند؛ مثل GET, HEAD, OPTIONS.

Idempotent Methods:
متدهایی که اگر چند بار پشت سر هم اجرا شوند، نتیجه نهایی یکسانی روی سرور دارند؛ مثل GET, PUT, DELETE.

پس باید الان ازش استفاده کنی؟

برای سرویس‌های داخلی روی Node، من همین الان استفاده می‌کنم. parser support وجود دارد، semantics استاندارد شده، و ترافیک داخلی نیازی به نگرانی از CDN یا بلوغ مرورگر ندارد.

برای API عمومی، عجله نکن. endpoint‌های POST فعلی‌ات را نگه دار و Accept-Query را اضافه کن تا client‌ها بدانند که QUERY هم پشتیبانی می‌شود. tooling داره می‌آید — OpenAPI 3.2 و ASP.NET 11 هر دو آن را می‌شناسند.

ده یازده سال از اولین draft تا استاندارد شدن طول کشید. HTTP سریع حرکت نمی‌کند، و دقیقاً به همین خاطر وقتی حرکت می‌کند ارزش توجه دارد.سرچ کردن با متد POST احتمالاً ظرف چند سال آینده به همان اندازه‌ای که الان X- header استفاده کردن به نظر قدیمی می‌رسد، legacy به نظر خواهد رسید.

Fable 5 برگشت، Sonnet 5 اومد - و ما باید با یه پرامپت و نصفی کنار بیایم نوشته شده توسط عرفان دهقانی

Fable 5 برگشت، Sonnet 5 اومد - و ما باید با یه پرامپت و نصفی کنار بیایم

۹ ژوئن، آنتروپیک Fable 5 رو با سروصدا لانچ کرد. سه روز بعد، همه‌ی دسترسی‌ها قطع شد. اگه اون روزها پای Claude بودی و یهو دیدی مدل پیش‌فرضت دوباره همون Sonnet قدیمیه، تخیلات نبود — دولت آمریکا دخالت کرده بود.

۱۲ ژوئن، وزارت بازرگانی آمریکا یک دستورالعمل کنترل صادرات اعمال کرد که به‌خاطرش آنتروپیک مجبور شد دسترسی به Fable 5 و Mythos 5 رو برای همه قطع کند. دلیل رسمی این بود که شرکت نمی‌توانست در لحظه تشخیص دهد کاربر اهل کجاست، پس به‌جای محدودیت جغرافیایی، کل سرویس رو خواباند. بعداً مشخص شد محققی در آمازون روشی برای دور زدن safeguardهای Fable 5 پیدا کرده که مدل رو به پیدا کردن آسیب‌پذیری و نوشتن کد اکسپلویت وادار می‌کرده. آنتروپیک بلافاصله یه کلاسیفایر جدید آماده کرد تا این حفره رو ببندد، و بعد از ۱۸ روز توقف، ۳۰ ژوئن وزارت بازرگانی آمریکا محدودیت رو برداشت.

از اول جولای، Fable 5 برگشته — این‌بار با دسترسی جهانی و safeguardهای تقویت‌شده.

این ۱۸ روز برای کاربران معمولی یعنی چی بود

بذار صادق باشیم: اگه از نسخه‌ی رایگان یا Pro استفاده می‌کردی، احتمالاً اصلاً Fable 5 رو لمس نکردی. مدل از همان ابتدا فقط تا ۲۲ ژوئن روی پلن‌های Pro، Max و Team رایگان بود، و بعدش به کردیت نیاز داشت. یعنی پنجره‌ی دسترسی رایگان برای اکثر کاربران از اول هم کوتاه بود.

و همین‌جا بود که Sonnet 5 وارد شد — نه به‌عنوان جایگزین Fable، بلکه به‌عنوان چیزی که قرار بود برای بیشتر کاربران مدل روزمره باشد. آنتروپیک Sonnet 5 رو همزمان با اعلام بازگشت Fable معرفی کرد، و از همان روز اول به‌عنوان مدل پیش‌فرض برای پلن‌های Free و Pro تعریف شد.

ولی اگه روی نسخه‌ی رایگان یا حتی High توی پلن پولی امتحانش کردی، احتمالاً همون چیزی رو دیدی که من دیدم: با یه پرامپت و نصفه جواب برنمی‌گردوند. نه یه مکالمه‌ی طولانی، نه یه کار سنگین — یه سوال معمولی. محدودیت نرخ Sonnet 5 فعلاً خیلی سریع‌تر از نسخه‌های قبلی فعال می‌شود، مخصوصاً توی ساعت‌های شلوغ. آنتروپیک گفته که rate limitها رو برای پلن‌های مختلف افزایش داده، ولی در تستی که من انجام دادم در مدل رایگان جدید واقعا اینطور بنظر نیومد.

Sonnet 5 واقعاً چقدر فرق دارد

ادعای اصلی آنتروپیک این است که Sonnet 5 به عملکرد Opus 4.8 نزدیک است، ولی با قیمت خیلی پایین‌تر. در API، نرخ تعارفی تا پایان اوت ۲۰۲۶ برای هر یک‌میلیون توکن ورودی دو دلار و خروجی ده دلار است، بعدش به سه و پانزده دلار می‌رسد. Opus 4.8 روی همین مقیاس پنج و بیست‌وپنج دلار است. از نظر اعداد، Sonnet 5 روی بنچمارک‌های BrowseComp و OSWorld-Verified خیلی به Opus 4.8 نزدیک‌تر از Sonnet 4.6 است.

تفاوت اصلی در agentic قابلیت‌هاست: برنامه‌ریزی چندمرحله‌ای، کنترل مرورگر و ترمینال، و چک‌کردن خودکار خروجی بدون این‌که لازم باشد توی پرامپت بگویی «جوابت رو بررسی کن». اینا قابلیت‌هایی بودند که قبلاً بیشتر مختص مدل‌های Opus بود. از نظر امنیت سایبری هم آنتروپیک می‌گوید Sonnet 5 هیچ‌وقت روی داده‌های تخصصی امنیتی آموزش ندیده، و در تست‌هایی که با موزیلا انجام شده، روی آسیب‌پذیری‌های شناخته‌شده‌ی فایرفاکس هیچ اکسپلویت کاملی تولید نکرده. به همین خاطر هم دولت آمریکا سراغش نیامده.

Fable 5 الان چه وضعیتی دارد

Fable 5 برگشته، ولی نه به همان شکلی که اول لانچ شده بود. دسترسی رایگان روی پلن‌های Pro و Max تموم شده — حالا به کردیت نیاز دارد. Anthropic گفته هدفش این است که در آینده Fable رو دوباره به‌عنوان بخشی از پلن‌های اشتراکی برگرداند، ولی زمان مشخصی نداده.

Mythos 5 هنوز محدود است. آنتروپیک دسترسی رو برای یه سری سازمان‌های آمریکایی تاییدشده برقرار کرده و داره فهرست Project Glasswing رو گسترش می‌دهد، ولی برای عموم در دسترس نیست و ظاهراً تا مدتی هم نخواهد بود.

یه چیز جالب توی این ماجرا این بود که آنتروپیک، آمازون، مایکروسافت و گوگل با هم یه چارچوب مشترک برای رتبه‌بندی شدت jailbreakها پیشنهاد دادند. این نشان می‌دهد که وقتی مدل‌ها به این سطح از توانایی می‌رسند، بحث‌های امنیتی دیگر فقط داخلی نیست — صنعت داره سعی می‌کند یه زبان مشترک برای این مکالمات پیدا کند.

یه نکته‌ی مهم که باید بدانی: Fable 5 فعلاً فقط از طریق API در دسترس است. اگه انتظار داری توی claude.ai یا Claude Code بروی و از منوی مدل‌ها انتخابش کنی، ناامید می‌شوی. آنتروپیک گفته که قصد دارد Fable رو دوباره به پلن‌های اشتراکی برگرداند، ولی نه الان و نه با زمانبندی مشخص.

یه نظر شخصی که شاید ارزش داشته باشه بشنوی

وسط همه‌ی این سروصداها، یه چیزی هست که کمتر کسی می‌گوید: Sonnet 4.6 هنوز خیلی خوبه. اگه کارت کد نوشتن، توضیح مفاهیم، خلاصه‌سازی، یا همین کارهای روزمره‌ی برنامه‌نویسیه، Sonnet 4.6 تقریباً همه‌شان رو راه می‌اندازد — و مهم‌تر از اون، رایگانه یا خیلی ارزون‌تر.

Sonnet 5 و Fable 5 قابلیت‌های واقعی دارند، مخصوصاً توی کارهای agentic طولانی‌مدت که مدل باید خودش چندین مرحله رو مدیریت کند. ولی برای اکثر کارهای روزانه‌ای که یه توسعه‌دهنده‌ی معمولی باهاشون طرفه، این فاصله‌ای که بین نسخه‌ها هست توی عمل خیلی کمتر از اعداد بنچمارک‌هاست. قبل از اینکه بروی کردیت بخری یا پلن رو ارتقا بدی، ارزش داره یه هفته با همان Sonnet 4.6 کار کنی و ببینی کجا واقعاً به دیوار می‌خوری — اگه اصلاً بخوری.

مدیریت متغیرهای محیطی در Next.js نوشته شده توسط Haleh Nakisa

مدیریت متغیرهای محیطی در Next.js

وقتی روی یک پروژه بزرگ یا حتی یک وب‌سایت شخصی کار می‌کنید، اطلاعات حساسی مثل کلیدهای API (API Keys)، آدرس دیتابیس‌ها و رمزهای عبور وجود دارن که به هیچ وجه نباید به صورت مستقیم (Hard-code) داخل کدهای شما قرار بگیرن. قرار دادن این اطلاعات داخل کد، نه تنها امنیت پروژه رو به خطر می‌اندازه، بلکه فرآیند تغییر اون‌ها را در محیط‌های مختلف (مثل محیط توسعه و محیط پروداکشن) به یک چالش بزرگ تبدیل می‌کنه.
اینجاست که فایل‌های .env به کار میان. در این مقاله می‌خوایم بررسی کنیم که فریم‌ورک Next.js چطور کار با متغیرهای محیطی رو برای ما راحت کرده، این فایل‌ها چه اولویت‌هایی دارن و چطور می‌تونیم به بهینه‌ترین شکل ممکن از اون‌ها استفاده کنیم.
چطور فایل‌های .env زندگی ما رو راحت‌تر می‌کنن؟
به زبان ساده، متغیرهای محیطی به شما اجازه میدن تنظیمات پروژه رو از خودِ کد جدا کنید. Next.js به صورت پیش‌فرض و بدون نیاز به نصب هیچ پکیج اضافی (مثل dotenv)، از این فایل‌ها پشتیبانی می‌کنه.
شما می‌تونید چندین فایل .env برای سناریوهای مختلف داشته باشید، اما نکته مهم اینه که بدونید Next.js با چه ترتیبی و بر اساس چه اولویتی این فایل‌ها رو می‌خونه.
ترتیب اولویت فایل‌های .env در Next.js (از بیشترین به کمترین):
نکست‌جی‌اس برای جابجایی بین محیط‌های مختلف (توسعه، پروداکشن و تست) بسیار هوشمند عمل می‌کنه. ترتیب خونده شدن فایل‌ها به این صورت هستش:
فایل‌های اختصاصی محیط با پسوند .local: مثل .env.development.local یا .env.production.local. این فایل‌ها بالاترین اولویت رو دارن و معمولاً در سیستم هر برنامه‌نویس به صورت محلی ذخیره می‌شن و نباید به گیت‌هاب push بشن.
فایل .env.local: این فایل در تمامی محیط‌ها (به جز محیط تست) اولویت بسیار بالایی داره و برای تعریف متغیرهای محلی عمومی استفاده میشه.
فایل‌های اختصاصی محیط: شامل .env.development (وقتی دستور next dev رو می‌زنید)، .env.production (وقتی پروژه با next start بالا میاد) و .env.test. نکست‌جی‌اس بر اساس وضعیت پروژه، فقط یکی از این فایل‌ها رو انتخاب می‌کنه و بقیه رو کاملاً نادیده می‌گیره.
فایل پایه .env: پایین‌ترین اولویت رو داره. این فایل به عنوان یک نسخه‌ی پشتیبان (Fallback) عمل می‌کنه؛ یعنی اگر متغیری در فایل‌های بالایی پیدا نشه، نکست‌جی‌اس این فایل رو چک می‌کنه.


دسترسی به متغیرها: مرز بین سرور و کلانیت
به صورت پیش‌فرض، تمام متغیرهایی که در فایل‌های .env تعریف می‌کنید فقط و فقط در سمت سرور (Node.js) قابل دسترسی هستن (مثلاً در API روت‌ها یا Server Components). این یک قابلیت امنیتی هستش تا اطلاعات حساس شما به مرورگر کاربر ارسال نشه.
اما اگر بخواید از یک متغیر در کامپوننت‌های سمت کلایت (Client Components) استفاده کنید، باید این کار رو انجام بدید:
کافیه اسم متغیر رو با عبارت NEXT_PUBLIC_ شروع کنید. با این کار، Next.js متوجه می‌شه که این متغیر حساس نیست و اجازه میده که در سمت کلایت هم بهش دسترسی داشته باشید:

# فقط در سمت سرور دسترسی دارد
ANALYTICS_SECRET_KEY=secret_123 

# هم در سرور و هم در کامپوننت‌های کلایت دسترسی دارد
NEXT_PUBLIC_API_URL=https://api.codoloper.com
ساخت پوشه Configs برای دسترسی بهتر:
صدا زدن مداوم process.env.NEXT_PUBLIC_API_URL در سراسر پروژه، علاوه بر شلوغ کردن کد، دو مشکل اساسی داره: اول اینکه هیچ پیشنهادی (Auto-complete) از سمت ادیتور دریافت نمی‌کنید و دوم اینکه احتمال غلط املایی بالا میره.
برای حل این مشکل مراحل زیر رو انجام بدید:
۱. در ریشه (Root) پروژه یک پوشه به نام configs ایجاد کنید.
۲. داخل اون یک فایل به نام global.ts بسازید.
۳. متغیرهای محیطی رو به صورت جدا در این فایل تعریف و اکسپورت کنید:
export const apiUrl = process.env.NEXT_PUBLIC_API_URL;

export const isDevelopment = process.env.NODE_ENV === "development";

export const isProduction = process.env.NODE_ENV === "production";
حالا هر کجای پروژه که به این متغیرها نیاز داشتید، به جای کار با process.env، خیلی راحت متغیر مورد نظرتون رو ایمپورت می‌کنید:
import { apiUrl, isProduction} from "@/configs/global";
با این کار کدهای تمیزتری هم دارید.
جمع‌بندی
مدیریت هوشمندانه متغیرهای محیطی در Next.js به ما این امکان رو میده که پروژه‌های امن‌تر و منعطف‌تری بسازیم. با درک درست از اولویت‌بندی فایل‌های .env و استفاده از قابلیت‌هایی مثل پیشوند NEXT_PUBLIC_ و رویکرد Centralized Config، فرآیند توسعه و دیپلوی پروژه آسونتر میشه.
نکته مهمِ اینکه: حتماً مطمئن شید که فایل‌های با پسوند .local در فایل .gitignore شما قرار دارن تا به اشتباه روی مخازن گیت آپلود نشن!
شما در پروژه‌هاتون چطور متغیرها رو مدیریت می‌کنید؟ آیا تا به حال با چالش لود نشدن متغیرها در سمت کلایت مواجه شدید؟ نظرات و تجربیات خودتون رو در بخش کامنت‌ها با ما در میون بذارید!