شرح ملفات .env: التحليل والتحويل إلى JSON وإدارة الإعدادات
ملف .env هو قائمة نصية صِرفة من أزواج KEY=VALUE تُبقي الإعدادات والأسرار خارج شيفرتك المصدرية. إنه التنسيق الذي تُحمّله Node وVite وNext.js وPython وRuby وDocker Compose إلى بيئة العملية. إن كنت تبحث عن env to json، فأنت عادةً تريد أحد أمرين: تحويل ملف .env إلى JSON منظَّم لأجل الأدوات، أو فهم القواعد فهماً يكفي لفعل ذلك بأمان.
ثلاثة أمور تُربك الناس، فلنُنحِّها جانباً أولاً:
- ملف .env مسطَّح. لا تداخل فيه. كل مفتاح يقع في المستوى الأعلى.
- كل قيمة سلسلة نصية. لا تُحوِّل dotenv الأنواع أبداً. تُحمَّل PORT=8080 بوصفها “8080”، لا 8080.
- القواعد النحوية غير رسمية. لا توجد مواصفة رسمية، لذا تختلف أدوات التحميل في معالجة الاقتباسات والتعليقات وعلامات الهروب عند الحواف.
يغطّي هذا الدليل قواعد تحليل dotenv، والتطابق بين .env وJSON (ولماذا قد تحوّل في أيٍّ من الاتجاهين)، ومصفوفة قرار لمتى تستخدم .env مقابل JSON، وكيف تتحقق من صحة إعداداتك قبل أن تُطلَق. كل ما يُوصف هنا يجري في محوّل ENV إلى JSON داخل متصفّحك بالكامل، حتى لو كان ملف .env مليئاً باعتمادات حقيقية، فهو لا يغادر الصفحة.
ما هو ملف .env؟
ملف .env هو المعيار الفعلي لإعداد البيئة. مكتبة dotenv، ومنافذها إلى كل لغة تقريباً، تقرأ الملف وتحقن كل زوج في العملية الجارية. حينها يقرأ تطبيقك process.env.DATABASE_URL بدلاً من تثبيت سلسلة الاتصال في الشيفرة. ولأن الملف يحمل كلمات مرور قواعد البيانات ومفاتيح API وأسرار OAuth ورموز الوصول، يكاد دائماً يُستثنى من git ويُعامَل بوصفه حسّاساً.
تشريح السطر
كل سطر ذي معنى هو زوج KEY=VALUE، يُقسَّم عند علامة = الأولى. تُتخطّى أسطر التعليق والأسطر الفارغة، وتُزال بادئة export الاختيارية:
# Database — this whole line is a comment
DATABASE_URL=postgres://user:pass@localhost:5432/mydb
DATABASE_POOL_SIZE=10
# A blank line above is ignored
export DEPLOY_ENV=production # the `export` prefix is removed
JWT_SECRET="super secret value"
المفتاح هو كل ما يسبق علامة = الأولى، مُقتطَعاً منه المسافات المحيطة. توجد بادئة export كي يُمكن استخدام الملف مباشرةً عبر source في الصدفة، وأدوات تحميل dotenv تُزيلها تلقائياً. والتقسيم عند علامة = الأولى مهمّ لأن قيماً مثل DATABASE_URL كثيراً ما تحوي علامات = خاصة بها داخل سلاسل الاستعلام.
لماذا تعيش الإعدادات في البيئة
يأتي المنطق من تطبيق العوامل الاثني عشر، الذي ينصّ على تخزين الإعدادات في البيئة. تتغيّر الإعدادات مع كل نشر، في التطوير والتجهيز والإنتاج، بينما تظل الشيفرة كما هي. إبقاؤها في البيئة يعني أنك تغيّر مضيف قاعدة بيانات دون تعديل الشيفرة المصدرية أو إعادة نشرها، وأن البناء نفسه يعمل في كل مكان.
ثمة سوء قراءة شائع: يقتبس الناس عبارة “ألّا تكون الإعدادات أبداً في ملف” فيستنتجون أن .env محظور. القاعدة الفعلية أضيق من ذلك. لا ينبغي أن تكون الإعدادات ملفاً مُودَعاً داخل التطبيق، ممزوجاً بالشيفرة ومُتتبَّعاً في نظام التحكم بالإصدارات. أما ملف .env محلي مُستثنى من git لأجل التطوير فلا بأس به وهو معيار. ما تتجنّبه هو إطلاق ملف .env حقيقي إلى الإنتاج بأسرار مخبوزة فيه.
قواعد تحليل .env (الحالات الحدّية التي تختلف عليها الأدوات)
لأنه لا توجد مواصفة رسمية يُقاس عليها parse .env file (تحليل ملف .env)، تتّخذ كل أداة تحميل قراراتها الخاصة عند الحدود. القواعد التالية هي أعراف dotenv المتّبَعة على نطاق واسع، أي تلك التي يُطبّقها المحوّل وتتفق عليها معظم أزمنة التشغيل.
الاقتباسات وعلامات الهروب
طريقة اقتباس القيمة تغيّر كل شيء:
- الاقتباس المزدوج يعالج تسلسلات الهروب. تصبح \n سطراً جديداً، و\t علامة جدولة، و\r رجوع المؤشر، و\ شرطة خلفية، و” علامة اقتباس مزدوجة حرفية. كما يمكن لقيمة محاطة باقتباس مزدوج أن تمتدّ على عدّة أسطر حتى علامة الإغلاق، ولهذا تتّسع مفاتيح PEM الخاصة داخل ملف .env.
- الاقتباس المفرد حرفي. لا تُعالَج علامات الهروب، تماماً مثل الصدفة. تُبقي ‘no \n escapes here’ على الشرطة الخلفية والحرف n حرفياً.
- القيم غير المُقتبَسة تمتدّ إلى نهاية السطر، مع اقتطاع المسافات اللاحقة. وعلامة # الداخلية (مسافة يتبعها رمز التجزئة) تبدأ تعليقاً يُحذف.
تلك القاعدة الأخيرة تُوقع الناس في فخّ ألوان النظام الست عشري. تفقد COLOR=#ff0000 كل ما يلي علامة #. ضعها بين اقتباسين (COLOR=“#ff0000”) فتبقى القيمة.
كل شيء سلسلة نصية
هذه أهمّ حقيقة مفردة عن تنسيق dotenv (dotenv format). لا تُحمَّل PORT=8080 بوصفها العدد 8080. تُحمَّل بوصفها السلسلة “8080”، لأن قيم process.env سلاسل نصية دائماً عند التشغيل. لا تُحوِّل dotenv الأنواع أبداً.
يسبّب هذا أخطاءً حقيقية. تكون if (process.env.DEBUG) صادقة حتى حين تكون DEBUG=false، لأن “false” سلسلة غير فارغة. وتفشل المقارنات العددية بصمت لأن “8080” ليست 8080. أي ميزة “استنتاج الأنواع”، بما فيها المفتاح الموجود في المحوّل، هي طبقة راحة مُضافة فوق dotenv، لا جزء من المعيار. استخدمها بقصد، عالِماً أن JSON سيختلف حينها عمّا يتلقّاه تطبيقك فعلاً.
المفاتيح المكرّرة والقيم متعدّدة الأسطر والاستيفاء
حين يظهر المفتاح ذاته مرتين، يفوز الظهور الأخير. تُسقَط القيمة الأسبق بصمت. هذا فخّ سوء إعداد متكرّر، إذ يحجب تكرار شارد قرب أسفل ملف طويل القيمةَ التي قصدت استخدامها بهدوء. والمحوّل الجيّد يُنبِّه على التكرارات بتحذير بدلاً من ابتلاعها.
تعمل القيم متعدّدة الأسطر داخل الاقتباس المزدوج فقط، ملتفّةً عبر الأسطر حتى علامة ” المُغلِقة. أما استيفاء ${VAR}، أي إحالة متغيّر إلى آخر، فموجود في بعض أدوات التحميل لكنه ليس عامّاً. لا تعتمد عليه عبر أزمنة التشغيل، فملف يستوفي على ما يرام في حزمة قد يُحمِّل السلسلة الحرفية ${VAR} في أخرى.
قواعد التحليل في لمحة
| سطر الإدخال | القيمة المُحلَّلة | القاعدة |
|---|---|---|
| PORT=8080 | ”8080” | غير مُقتبَسة، تُحفظ سلسلةً (لا تحويل أنواع) |
| APP_NAME=My App # title | ”My App” | غير مُقتبَسة: تُحذف علامة # الداخلية وتُقتطَع المسافة اللاحقة |
| GREETING=“Hello,\nWorld” | Hello,⏎World | الاقتباس المزدوج يعالج \n سطراً جديداً حقيقياً |
| LITERAL=‘no \n escapes’ | no \n escapes | الاقتباس المفرد حرفي، لا معالجة هروب |
| COLOR=#ff0000 | "" | علامة # غير المُقتبَسة تبدأ تعليقاً فتضيع القيمة؛ اقتبسها |
| export AWS_REGION=us-east-1 | us-east-1 | تُزال بادئة export |
| EMPTY= | "" | القيمة الفارغة سلسلة فارغة صالحة |
.env مقابل إعدادات JSON: متى تستخدم أيّاً منهما
لا يصحّ أن نقول إن “JSON أفضل”. إنهما يحلّان مشكلتين مختلفتين. ملف .env مسطَّح، نصّي فقط، صديق للتعليقات، وخاص بكل بيئة، فهو مصمَّم للأسرار وتجاوزات وقت النشر. أما JSON فمتداخل ومُحدَّد النوع ومنظَّم، لكنه بلا تعليقات ويَسهُل إيداعه بالخطأ. الاختيار بينهما هو قرار env vs json config الحقيقي.
ما لا يستطيع .env فعله
لا يستطيع .env التداخل، ولا حمل المصفوفات، ولا حمل أنواع حقيقية. إنه قائمة مسطّحة من السلاسل النصية. إن كانت إعداداتك مُجمَّعة بطبيعتها، فأنت تُسطّحها باصطلاح بادئة بدلاً من تداخلها، فتكتب DB_HOST وDB_PORT بدلاً من كائن db. تظلّ المفاتيح مسطّحة، وتعيد تجميع البنية في الشيفرة.
ما يتفوّق فيه JSON
يفوز JSON حين تكون البنية هي المقصد: كائنات متداخلة، ومصفوفات، وأعداد وقيم منطقية وnull حقيقية. إنه التنسيق الذي تتحقّق منه مقابل مخطَّط والذي تُولّد منه الأنواع. إن احتجت تسلسلاً هرمياً لا يستطيع ملف مسطّح التعبير عنه، فإن JSON هو الأداة الصحيحة، أو YAML المُغطّى أدناه.
مصفوفة القرار
| الحاجة | .env | JSON | لماذا |
|---|---|---|---|
| الأسرار / الاعتمادات | ✅ | ⚠️ | يُستثنى .env من git اصطلاحاً؛ بينما يَسهُل إيداع إعدادات JSON بالخطأ |
| تجاوزات لكل بيئة | ✅ | ⚠️ | ملف .env واحد لكل بيئة هو نمط النشر المعياري |
| البنية المتداخلة | ❌ | ✅ | .env مسطّح؛ بينما يتداخل JSON أصلاً |
| القيم المُحدَّدة النوع (عدد/منطقي/null) | ❌ | ✅ | قيم .env سلاسل دائماً؛ بينما لدى JSON أنواع حقيقية |
| التعليقات الداخلية | ✅ | ❌ | يدعم .env علامة #؛ بينما لا صياغة تعليق لدى JSON |
| التحقق بالمخطَّط | ⚠️ | ✅ | تحقّق بعد تحويل .env←JSON؛ بينما يتحقّق JSON مباشرةً |
التحويل من .env إلى JSON والعودة
التحويل في أيٍّ من الاتجاهين آليّ متى عرفت القواعد. التطابق واحد لواحد في المستوى الأعلى، فكل سطر KEY=VALUE خاصية JSON واحدة، والدقّة الوحيدة في الأنواع والتداخل.
.env ← JSON
كل زوج KEY=VALUE يصبح خاصية JSON. افتراضياً تكون كل قيمة سلسلة نصية، أمينةً لما تُحمّله dotenv عند التشغيل؛ ومفتاح استنتاج الأنواع الاختياري يُرقّي الأعداد والقيم المنطقية وnull غير المُقتبَسة. والنتيجة كائن مسطّح. تفعل هذا لتغذية إعداداتك لأدوات لا تقبل سوى JSON، أو لاستيراد جماعي إلى مدير أسرار، أو للتحقق مقابل مخطَّط، أو لمجرّد قراءة ملف .env متشعّب بوصفه بيانات منظَّمة. محوّل ENV إلى JSON يفعل هذا تماماً في المتصفّح، مع تحذير حين يرصد مفاتيح مكرّرة.
JSON ← .env
العكس يأخذ كائناً فقط، فمصفوفة على المستوى الأعلى أو قيمة قياسية مجرّدة لا تملك أسماء مفاتيح تُطابَق بالمتغيّرات. تُكتب الأعداد والقيم المنطقية مجرّدةً (PORT=8080)، وتصبح null مفتاحاً فارغاً KEY=، وأي سلسلة تحوي مسافة أو # أو سطراً جديداً أو علامة اقتباس تُحاط باقتباس مزدوج وتُهرَّب تلقائياً كي تَعبر ذهاباً وإياباً بأمان. لا تستطيع الكائنات والمصفوفات المتداخلة العيش في ملف مسطّح، لذا يُسلسَل كلٌّ منها إلى سلسلة JSON ويُعلَّم بتحذير. ومفاتيح اختيارية تُسوّي المفاتيح إلى UPPER_SNAKE_CASE وتُضيف بادئة export. محوّل JSON إلى ENV يتولّى كل هذا.
أمان الرحلة ذهاباً وإياباً وتحذير التداخل
يوجد الاقتباس التلقائي كي تَعبر القيمة .env ← JSON ← .env دون تغيير. وفيما يلي الرحلة الكاملة بوصفها شيفرة قابلة للتشغيل، مطابقةً لسلوك المحوّلَين. ولاحظ أن PORT تظل سلسلة نصية عبر الدورة، تماماً كما كانت dotenv ستُحمّلها:
import { parse } from 'dotenv';
// 1. Start with a .env file as text
const envText = `DATABASE_URL=postgres://user:pass@localhost:5432/mydb
PORT=8080
GREETING="Hello, World"
NOTE="value with # hash"`;
// 2. .env -> JSON (dotenv.parse returns string values only)
const config = parse(envText);
console.log(JSON.stringify(config, null, 2));
// {
// "DATABASE_URL": "postgres://user:pass@localhost:5432/mydb",
// "PORT": "8080",
// "GREETING": "Hello, World",
// "NOTE": "value with # hash"
// }
// 3. JSON -> .env (quote only strings that need it)
const needsQuotes = (s) => /[\s#"'\n]/.test(s);
const env = Object.entries(config)
.map(([key, value]) =>
needsQuotes(value) ? `${key}=${JSON.stringify(value)}` : `${key}=${value}`
)
.join('\n');
console.log(env);
// DATABASE_URL=postgres://user:pass@localhost:5432/mydb
// PORT=8080
// GREETING="Hello, World"
// NOTE="value with # hash"
العقبة هي التداخل. الرحلة ذهاباً وإياباً بلا فقد للإعدادات المسطّحة، لكن بنيةً عميقة التداخل لا يمكنها العبور عبر .env إلا بوصفها سلاسل JSON مُبهَمة لا يقرؤها أي تطبيق يتوقّع استرجاع البنية. إن كانت إعداداتك هرمية بحقّ، فالجأ إلى YAML بدلاً من ذلك. يغطّي محوّل YAML إلى JSON ودليل مشكلة Norway في YAML ذلك المسار وحوافّه الحادّة الخاصة.
التحقق من صحة إعدادات البيئة
ينبغي ألّا يظهر متغيّر إعداد مفقود أو مُشوَّه بوصفه خطأ undefined is not a function في الثالثة فجراً في الإنتاج. منهج العوامل الاثني عشر هو الفشل المبكر، أي التحقّق من الإعدادات قبل النشر لا بعده. تحويل .env إلى JSON يجعل ذلك عملياً، لأن لدى JSON أدوات تحقّق ناضجة لا تملكها متغيّرات البيئة الخام.
التحقق بالمخطَّط في CI
حوّل .env ← JSON، ثم تحقّق من JSON مقابل مخطَّط يُعلِن المفاتيح المطلوبة والتعدادات المسموحة وصيَغ القيم. بيئة مُساءة الإعداد، كأن يكون DATABASE_URL مفقوداً أو LOG_LEVEL غير صالح أو المنفذ ليس عدداً، تفشل في فحص CI بدل أن تفشل في النشر. يشرح دليل التحقق من JSON بـ JSON Schema كتابة المخطَّط، ومدقق JSON Schema يُشغّله في المتصفّح.
الإعدادات المُحدَّدة النوع
أبعد من فحوص الوجود، يمكنك اشتقاق كائن إعدادات مُحدَّد النوع كي لا تكون process.env.PORT سلسلةً غير مُحدَّدة النوع مبعثرةً في الشيفرة. تحقّق وحوّل عند البدء بمكتبة مخطَّط وقت تشغيل مثل Zod، أو ولّد واجهة TypeScript من JSON واقرأ الإعدادات من خلالها. يُغطّي دليل تحويل JSON إلى TypeScript ومحوّل JSON إلى TypeScript خطوة التوليد. نسِّق أو افحص JSON أولاً بـمنسق JSON كي تظهر أي مفاجأة بنيوية مبكراً.
نظافة الأسرار: التعامل مع .env بأمان
ملف .env هو، عملياً، قائمة اعتمادات. عامِله كذلك.
لا تُودِع .env أبداً. أضِفه إلى .gitignore. أودِع ملف .env.example يَسرُد كل مفتاح بقيم فارغة أو نائبة، مثل DATABASE_URL= بدلاً من سلسلة الاتصال الحقيقية. ذلك الملف هو عقد الفريق: يُوثّق المتغيّرات التي يحتاجها أي استنساخ جديد دون تسريب أيٍّ منها.
.env للمحلّي والتطوير؛ والإنتاج يستخدم مدير أسرار. أدوات مثل Vault وDoppler وAWS Secrets Manager تحقن الأسرار في البيئة وقت النشر. لا تُطلِق ملف .env حقيقياً بأسرار حيّة إلى مضيف إنتاج، بل اسحبها من المدير، كي لا يُسلّم ملف مُسرَّب أو حاوية مُساءة الإعداد مفاتيحك.
حوّل الأسرار في أداة تعمل في المتصفّح فقط. لصق ملف .env حقيقي في محوّل يعمل على الخادم يُرسل اعتماداتك عبر الشبكة إلى جهاز شخص آخر. كلا المحوّلَين هنا يعملان داخل متصفّحك بالكامل، فافتح علامة تبويب الشبكة في أدوات المطوّر وتأكّد أن اللصق يُطلق صفر طلبات. ذلك هو الفرق الذي يجعل تحويل ملف .env إنتاجيّ آمناً بدل عيّنة مُنقّاة.
الأسئلة الشائعة
كيف أُحوِّل ملف .env إلى JSON؟
ألصِق الملف في محوّل ENV إلى JSON فيُحلّله إلى JSON فوراً داخل متصفّحك. كل سطر KEY=VALUE يصبح خاصية. القيم سلاسل افتراضياً (مطابقةً لـ dotenv)؛ فعّل استنتاج الأنواع إن أردت أعداداً وقيماً منطقية. لا شيء يُرفَع، فتبقى الأسرار الحقيقية على جهازك.
هل قيم .env أعداد وقيم منطقية، أم سلاسل؟
سلاسل دائماً. لا تُحوِّل dotenv الأنواع أبداً، فعند التشغيل تكون كل قيمة process.env سلسلةً، فتكون PORT=8080 هي “8080” وتكون DEBUG=false هي السلسلة “false” (الصادقة). أي خيار “استنتاج الأنواع” طبقة راحة مُضافة فوق المعيار، لا جزء من dotenv ذاته.
ما الفرق بين ملف .env وملف إعدادات JSON؟
ملف .env مسطَّح، نصّي فقط، صديق للتعليقات، ومصمَّم للأسرار وتجاوزات كل بيئة. أما JSON فمتداخل ومُحدَّد النوع بأعداد وقيم منطقية وnull حقيقية، ويتحقّق مقابل مخطَّط، لكنه بلا تعليقات ويَسهُل إيداعه بالخطأ. استخدم .env للأسرار، وJSON للإعدادات المنظَّمة.
هل يمكن أن يحوي ملف .env قيماً متداخلة أو مُجمَّعة؟
لا. ملف .env قائمة مسطّحة من أزواج KEY=VALUE بلا تداخل وبلا مصفوفات. للتعبير عن التجميع، سطّحه باصطلاح بادئة، فتكتب DB_HOST وDB_PORT بدلاً من كائن db، ثم تعيد تجميع البنية في الشيفرة. إن احتجت تسلسلاً هرمياً بحقّ، فاستخدم JSON أو YAML.
كيف تُعالَج الاقتباسات و# والقيم متعدّدة الأسطر في .env؟
الاقتباس المزدوج يعالج علامات الهروب (\n، \t، \، ”) ويمكن أن يمتدّ على عدّة أسطر حتى علامة الإغلاق. الاقتباس المفرد حرفي بلا علامات هروب. القيم غير المُقتبَسة تمتدّ إلى نهاية السطر، وتُقتطَع المسافات اللاحقة، وتعامل مسافةً متبوعةً بـ # بوصفها تعليقاً داخلياً، فاقتبس أي قيمة تحوي # بصورة مشروعة.
هل ينبغي أن أُودِع ملف .env في Git؟
لا. أضِف .env إلى .gitignore وأودِع ملف .env.example يَسرُد المفاتيح بقيم فارغة بدلاً من ذلك. الملف الحقيقي يحمل كلمات مرور قواعد البيانات ومفاتيح API والرموز؛ وإيداعه يُسرّب الاعتمادات إلى تاريخك، حيث تبقى حتى بعد حذفك للملف.