Node.js وExpress: خادم بـ JavaScript
درس 3 من 6
3Node.js وExpress: خادم بـ JavaScript
الصورة الكبيرةمع Node تكتب الواجهة والخادم بنفس اللغة. Express أبسط إطار خادم وأكثرها انتشارًا، وفكرته الواحدة (سلسلة وسائط) موجودة في كل إطار خادم تقريبًا.
ماذا تشاهد في الرسم: كل وسيط يقرأ الطلب ويقرّر: يمرّره بـ next() أو يرد بنفسه. الترتيب في الكود هو الترتيب هنا.
خارطة هذا الجزء
المفاهيم بالترتيب، اضغط أيًّا منها للقفز إليه:
طريقان إلى نفس الفكرة
الخادم مثل مطعم: الطلب يمرّ على الاستقبال (يفكّ الظرف ويقرأ الطلب)، ثم من يسجّل الزيارة، ثم الطاهي المختص بهذا الطبق، ولو احترق شيء هناك مدير يعتذر للزبون بأدب. كل محطة «وسيط» في Express.
Node بيئة تشغيل V8 بحلقة أحداث أحادية الخيط وإدخال/إخراج غير محظور (libuv)، فالانتظار على الشبكة أو القرص لا يوقف الخادم، لكن الحساب الثقيل يوقفه. Express يبني تطبيقًا كسلسلة دوال (req, res, next)، والموجّه Router يجمع المسارات، ومعالج الأخطاء دالة بأربعة معاملات (err, req, res, next). في Express 5 تُمرَّر أخطاء الدوال غير المتزامنة تلقائيًا لمعالج الأخطاء.
قبل أن تبدأ
🧰ما هو إطار العمل
من الجزأين الأولين: الإطار ينادي دوالك، وExpress إطار خادم صغير مرن.
⚡JavaScript
تعرف الدوال السهمية والكائنات.
📨HTTP وREST
تعرف الأفعال والرموز من درس REST.
الفكرة الجوهرية
1حلقة الأحداث: انتظار بلا توقّف
Node يبدأ العمليات البطيئة (ملف، شبكة) ويكمل غيرها، ثم يعود حين تجهز. هذا المثال يُظهر الترتيب: الكود المتزامن أولًا، ثم الوعود، ثم المؤقّتات.
console.log("1 start");
setTimeout(() => console.log("4 timer"), 0);
Promise.resolve().then(() => console.log("3 promise"));
console.log("2 end of script");2المسارات وRouter
app.get("/orders", handler) يربط فعلًا ومسارًا بدالة. req.params للمقاطع (/orders/:id)، وreq.query لما بعد ?، وreq.body للجسم بعد express.json().
3الوسائط: سلسلة بالترتيب
الوسيط دالة (req, res, next). تقرأ أو تعدّل الطلب ثم تنادي next()، أو ترد وتنهي السلسلة. التسجيل والمصادقة وتحليل JSON كلها وسائط.
// a tiny middleware chain without Express, to see the idea run
function run(chain, req) {
let i = 0;
const next = (err) => {
const fn = chain[i++];
if (!fn) return;
if (err) return fn.length === 4 ? fn(err, req, next) : next(err);
if (fn.length === 4) return next();
try { fn(req, next); } catch (e) { next(e); }
};
next();
}
run([
(req, next) => { console.log("log:", req.method, req.path); next(); },
(req, next) => { if (!req.user) throw new Error("401 not logged in"); next(); },
(req, next) => console.log("handler: list orders"),
(err, req, next) => console.log("error handler ->", err.message),
], { method: "GET", path: "/orders", user: null });4التحقّق ومعالج الأخطاء
لا تثق بأي مدخل: تحقّق من النوع والطول والقيم المسموحة وارجع 400 برسالة واضحة. واجعل آخر وسيط معالج أخطاء يسجّل الخطأ ويرد 500 دون كشف التفاصيل للمستخدم.
مثال محلول
واجهة طلبات مقهى: عرض، إنشاء بتحقّق، جلب بالمعرّف مع 404، ومعالج أخطاء أخير. احفظه server.js وشغّله بـ node server.js.
import express from "express";
const app = express();
app.use(express.json());
app.use((req, res, next) => {
const start = Date.now();
res.on("finish", () => console.log(req.method, req.path, res.statusCode, Date.now() - start + "ms"));
next();
});
const MENU = new Set(["latte", "mocha", "v60"]);
const orders = [];
app.get("/orders", (req, res) => res.json(orders));
app.post("/orders", (req, res) => {
const { item, cups } = req.body ?? {};
if (!MENU.has(item)) return res.status(400).json({ error: `item must be one of ${[...MENU]}` });
if (!Number.isInteger(cups) || cups < 1 || cups > 10) return res.status(400).json({ error: "cups must be 1-10" });
const order = { id: orders.length + 1, item, cups };
orders.push(order);
res.status(201).location(`/orders/${order.id}`).json(order);
});
app.get("/orders/:id", (req, res) => {
const order = orders.find((o) => o.id === Number(req.params.id));
if (!order) return res.status(404).json({ error: "no such order" });
res.json(order);
});
app.use((err, req, res, next) => { // four arguments: the error handler
console.error(err);
res.status(500).json({ error: "something went wrong" });
});
app.listen(3000, () => console.log("http://localhost:3000"));curl -s localhost:3000/orders -H 'content-type: application/json' -d '{"item":"latte","cups":2}'
curl -s localhost:3000/orders/1
curl -s -o /dev/null -w "%{http_code}\n" localhost:3000/orders/99خرافة مقابل حقيقة
| الخرافة | الحقيقة |
|---|---|
| Node لا يتحمّل خوادم حقيقية لأنه خيط واحد. | ممتاز للإدخال والإخراج الكثير (APIs، دردشة)، ويضعف فقط مع الحساب الثقيل داخل الطلب. |
| Express قديم ولا أحد يستخدمه. | ما زال من أكثر حزم npm تحميلًا، وصدرت نسخته 5. وFastify (المستخدم في درب) بديل أسرع بنفس الفكرة. |
اختبر فهمك
ما ترتيب الطباعة: console.log، ثم setTimeout(0)، ثم Promise.then؟
الوعود (microtasks) تُنفَّذ قبل المؤقّتات بعد انتهاء الكود المتزامن.
كيف يعرف Express أن الوسيط معالج أخطاء؟
الدالة (err, req, res, next) بأربعة معاملات تُعامل كمعالج أخطاء.
طلب جاء بـ cups: "lots". ماذا ترد؟
المدخل خاطئ من العميل، فالرد 4xx ورسالة تقول ما المطلوب.
بطاقات مراجعة سريعة
تحقق من فهمك
لماذا يُكتب معالج الأخطاء في آخر الملف؟
لأن الوسائط تُنفَّذ بالترتيب، والخطأ يقفز للأمام حتى أول وسيط بأربعة معاملات.
متى ترد 201 ومعها Location؟
بعد إنشاء مورد جديد، والترويسة تقول أين تجده.
ماذا يحدث لو نسيت next() في وسيط لا يرد؟
يعلق الطلب بلا رد حتى تنتهي مهلته عند العميل.
مصادر هذا الدرس
مرتّبة بالنوع. ابدأ بالتوثيق الرسمي إذا تبي المرجع الدقيق، وبالمقال أو الفيديو إذا تبي الشرح أولًا.
خلّصت الدرس؟
سجّل الدخول ليُحفظ تقدّمك.