Express والـ Routing: الـ params والـ query والترتيب والـ Router

جارٍ التحميل…

01 / ملخص الدرس

Express والـ Routing: الـ params والـ query والترتيب والـ Router

app.get و req.params و req.query (كلهم strings)، وليه ترتيب الـ routes بيفرق (/users/me قبل /users/:id). صيغة Express 5 للـ paths: الأجزاء الاختيارية بـ {} والـ wildcard اللي ليه اسم. validation للـ params، و 404 في الآخر، و express.Router بـ prefix زي /api/v1، و router.param علشان تحمّل الحاجة مرة واحدة.

02 / شرح الدرس مكتوبًا

نص شرح المعلم والكود الذي كتبه ونتيجة تشغيله، بنفس ترتيب الدرس.

أهلًا بيك. في الدرس اللي فات عملنا الـ routing بإيدينا بـ regex و if. Express بتعمل ده بدالك: app dot get بياخد path و function. slash users: بيقرا req dot query، يعني اللي بعد علامة الاستفهام في الـ URL: role و limit. slash users slash colon id: الـ colon معناها: الجزء ده متغير، وقيمته في req dot params. و slash users slash me: المستخدم الحالي. نشغّل ونجرّب.

server.js
import express from "express";
import { users } from "./data.js";

const app = express();

app.get("/users", (req, res) => {
  const { role, limit = "10" } = req.query;
  let list = role ? users.filter((u) => u.role === role) : users;
  res.json(list.slice(0, Number(limit)));
});

app.get("/users/:id", (req, res) => {
  const user = users.find((u) => u.id === Number(req.params.id));
  if (!user) return res.status(404).json({ error: "مش موجود" });
  res.json(user);
});

app.get("/users/me", (req, res) => {
  res.json(users[0]);
});

app.listen(3000, () => console.log("http://localhost:3000"));

المخرجات

$ node server.js
http://localhost:3000

→ GET /users?role=student&limit=1
← 200 OK
[
  {
    "id": 2,
    "name": "عمر",
    "role": "student"
  }
]

→ GET /users/2
← 200 OK
{
  "id": 2,
  "name": "عمر",
  "role": "student"
}

→ GET /users/me
← 404 Not Found
{
  "error": "مش موجود"
}

الـ query اشتغلت: student واحد بس. slash users slash two: عمر. بس slash users slash me رجّعت four oh four! ليه؟ Express بيمشي على الـ routes بالترتيب، وأول route يطابق بياخد الـ request. و slash users slash colon id طابقت me، على إن الـ id قيمته me، و Number بتاعها NaN، فمالقاش حد. والـ route بتاع me عمره ما اتوصل له. القاعدة: الأخص الأول، فـ me قبل colon id. وكمان validation للـ id: لو مش رقم صحيح موجب، four hundred بدل ما ندوّر بـ NaN. وفي Express خمسة، فيه صيغة جديدة: جزء اختياري بين curly braces، و wildcard لازم يبقى ليه اسم بعد الـ star. وفي الآخر app dot use من غير path: أي حاجة ما طابقتش، four oh four بالـ JSON. نشغّل.

server.js
import express from "express";
import { users } from "./data.js";

const app = express();

// الأخص الأول: /users/me قبل /users/:id
app.get("/users/me", (req, res) => res.json(users[0]));

app.get("/users/:id", (req, res) => {
  const id = Number(req.params.id);
  if (!Number.isInteger(id) || id < 1) {
    return res.status(400).json({ error: "id لازم رقم صحيح موجب" });
  }
  const user = users.find((u) => u.id === id);
  if (!user) return res.status(404).json({ error: "مش موجود" });
  res.json(user);
});

// Express 5: جزء اختياري بالـ {} ، و wildcard ليه اسم
app.get("/files{/:format}", (req, res) => res.json({ format: req.params.format ?? "json" }));
app.get("/assets/*path", (req, res) => res.json({ path: req.params.path }));

app.use((req, res) => res.status(404).json({ error: `${req.method} ${req.path} مش موجود` }));

app.listen(3000, () => console.log("http://localhost:3000"));

المخرجات

$ node server.js
http://localhost:3000

→ GET /users/me
← 200 OK
{
  "id": 1,
  "name": "مُنى",
  "role": "admin"
}

→ GET /users/abc
← 400 Bad Request
{
  "error": "id لازم رقم صحيح موجب"
}

→ GET /users/9
← 404 Not Found
{
  "error": "مش موجود"
}

→ GET /files
← 200 OK
{
  "format": "json"
}

→ GET /files/csv
← 200 OK
{
  "format": "csv"
}

→ GET /assets/img/logo.png
← 200 OK
{
  "path": [
    "img",
    "logo.png"
  ]
}

→ GET /nope
← 404 Not Found
{
  "error": "GET /nope مش موجود"
}

me رجعت مُنى. abc: four hundred. تسعة: four oh four. files من غير format: الـ default json، وبـ csv: csv. الـ wildcard رجّع الـ path كـ array من الأجزاء: img و logo dot png. و nope: الـ four oh four بتاعنا. وخد بالك: كل حاجة في params و query strings دايمًا، حتى لو شكلها رقم، زي ما اتعلمنا في تحت الصفر. لما الـ routes تكتر، بنقسّمها على ملفات بـ express dot Router. الـ Router زي app صغير: ليه routes، وبيتركّب على prefix. courses dot js فيه coursesRouter. router dot param بيتنادي مرة واحدة لأي route فيه colon slug: بيدوّر على الكورس، ولو مش موجود four oh four، ولو موجود يحطه في req dot course ويكمّل بـ next. وفي server dot js: app dot use بـ slash api slash v one slash courses. نشغّل.

server.js
import express from "express";
import { coursesRouter } from "./courses.js";

const app = express();
app.use("/api/v1/courses", coursesRouter);

app.use((req, res) => res.status(404).json({ error: `${req.method} ${req.path} مش موجود` }));

app.listen(3000, () => console.log("http://localhost:3000"));

المخرجات

$ node server.js
http://localhost:3000

→ GET /api/v1/courses
← 200 OK
[
  {
    "slug": "node",
    "title": "Node.js"
  },
  {
    "slug": "sql",
    "title": "PostgreSQL"
  }
]

→ GET /api/v1/courses/node
← 200 OK
{
  "slug": "node",
  "title": "Node.js",
  "lessons": [
    "intro",
    "modules",
    "streams"
  ]
}

→ GET /api/v1/courses/php
← 404 Not Found
{
  "error": "الكورس مش موجود"
}

الـ list. node بكل تفاصيله. و php: four oh four من الـ param. والـ routes جُوّه الـ router مكتوبة من غير الـ prefix: slash و slash colon slug. فلو غيرت v one لـ v two، بتغيّر سطر واحد. التحدي: routes الدروس. route لكل دروس الكورس. route لدرس برقمه من واحد. validation: لو الرقم مش صحيح أو أقل من واحد four hundred، ولو أكبر من عدد الدروس four oh four. و format short ترجّع اسم الدرس بس كنص. وكله جُوّه courses dot js، وتستخدم req dot course. سبع حالات اختبار في الكومنت.

تمرين

في courses.js: GET /:slug/lessons و GET /:slug/lessons/:n (400 لو مش رقم من 1، 404 لو أكبر من العدد، ?format=short ← الاسم كنص).

الحل. lessons بترجّع req dot course. و colon n: نحوّل لرقم، ونتأكد إنه صحيح وموجب. والدرس من الـ array بـ n minus one، لأن الـ array بتبدأ من zero والمستخدم بيعدّ من واحد. ولو format short: res dot type text و send. نشغّل بالسبع حالات.

courses.js
import express from "express";
import { courses } from "./data.js";

export const coursesRouter = express.Router();

// router.param: بيتنادي مرة لأي route فيه :slug
coursesRouter.param("slug", (req, res, next, slug) => {
  const course = courses.find((c) => c.slug === slug);
  if (!course) return res.status(404).json({ error: "الكورس مش موجود" });
  req.course = course;
  next();
});

coursesRouter.get("/", (req, res) => res.json(courses.map(({ slug, title }) => ({ slug, title }))));
coursesRouter.get("/:slug", (req, res) => res.json(req.course));


coursesRouter.get("/:slug/lessons", (req, res) => {
  res.json({ course: req.course.slug, lessons: req.course.lessons });
});

coursesRouter.get("/:slug/lessons/:n", (req, res) => {
  const n = Number(req.params.n);
  if (!Number.isInteger(n) || n < 1) {
    return res.status(400).json({ error: "n لازم رقم من 1" });
  }
  const lesson = req.course.lessons[n - 1];
  if (!lesson) return res.status(404).json({ error: "الدرس مش موجود" });
  if (req.query.format === "short") return res.type("text").send(lesson);
  res.json({ course: req.course.slug, n, lesson });
});

المخرجات

$ node server.js
http://localhost:3000

→ GET /api/v1/courses/node/lessons
← 200 OK
{
  "course": "node",
  "lessons": [
    "intro",
    "modules",
    "streams"
  ]
}

→ GET /api/v1/courses/node/lessons/2
← 200 OK
{
  "course": "node",
  "n": 2,
  "lesson": "modules"
}

→ GET /api/v1/courses/node/lessons/0
← 400 Bad Request
{
  "error": "n لازم رقم من 1"
}

→ GET /api/v1/courses/node/lessons/abc
← 400 Bad Request
{
  "error": "n لازم رقم من 1"
}

→ GET /api/v1/courses/sql/lessons/9
← 404 Not Found
{
  "error": "الدرس مش موجود"
}

→ GET /api/v1/courses/php/lessons/1
← 404 Not Found
{
  "error": "الكورس مش موجود"
}

→ GET /api/v1/courses/node/lessons/3?format=short
← 200 OK
streams

السبعة زي الاختبار. الدرس الجاي: إيه اللي اتغيّر في Express خمسة، وإزاي تنقل مشروع قديم من أربعة من غير ما تكسره.

03 / دروس المسار (122)
  1. 01أهلًا بيك: يعني إيه backend، والكورس ده ماشي إزاي
  2. 02الملفات والفولدرات والـ terminal: تتحرك على جهازك زي المبرمجين
  3. 03Node وأول برنامج: تشغّل وتقرا رسالة الخطأ
  4. 04القيم والمتغيرات والأنواع: const و let و typeof
  5. 05القرارات والتكرار: if و === والـ truthy والـ loops
  6. 06الـ functions: parameters و return والـ arrow functions والـ scope
  7. 07الـ objects والـ arrays: destructuring و spread و map و filter و reduce
  8. 08JSON والنصوص والتواريخ: اللغة اللي الـ backend بيتكلم بيها
  9. 09الـ callbacks والـ closures وأول لمسة async: setTimeout
  10. 10الـ classes والـ errors: throw و try/catch و stack trace
  11. 11git: تحفَظ كل خطوة وترجعلها، وما تسرّبش أسرارك
  12. 12ESM و CommonJS و package exports: Node بيلاقي الـ modules إزاي
  13. 13الـ Promises و async/await: sequential و parallel والأخطاء والإلغاء
  14. 14الـ event loop والـ microtasks: Node بيعمل كل حاجة بـ thread واحد إزاي
  15. 15الـ Streams والـ Backpressure: ملفات أكبر من الـ memory
  16. 16الـ File System والـ paths: قراية وكتابة صح، ومن غير path traversal
  17. 17الـ Buffers والبيانات الـ binary: الـ bytes من جُوّه
  18. 18Crypto APIs والأسرار: random و hash و HMAC و AES-GCM
  19. 19الـ Worker Threads: شغل CPU تقيل من غير ما توقف الـ server
  20. 20الـ Errors على مستوى الـ process: operational و programmer و crash-only
  21. 21الـ Node test runner والـ debugging: tests من غير مكتبات
  22. 22HTTP من جُوّه: الـ methods والـ status codes والـ headers
  23. 23Express والـ Routing: الـ params والـ query والترتيب والـ Routerانت هنا
  24. 24Express 5: إيه اللي اتغيّر، وإزاي تنقل مشروع قديم
  25. 25الـ Middleware ودورة حياة الـ request جُوّه Express
  26. 26Validation للمدخلات بـ Zod: ما تصدّقش أي حاجة جاية من برة
  27. 27تصنيف الأخطاء و Problem Details: شكل واحد لكل الـ errors
  28. 28الـ CORS والـ Cookies: المتصفح بيحمي المستخدم إزاي
  29. 29رفع الملفات وحدود الحجم: multer من غير ثغرات
  30. 30الـ Realtime: Server-Sent Events و WebSockets
  31. 31OpenAPI والـ API Versioning: عقد مكتوب، وتغيير من غير ما تكسر حد
  32. 32PostgreSQL: الـ DDL وأنواع البيانات الصح لكل عمود
  33. 33الـ Constraints: PK و FK و UNIQUE و CHECK — الـ database بتحمي نفسها
  34. 34الـ Normalization والـ ERD: تقسّم البيانات صح من أول يوم
  35. 35الـ JOINs والتجميع: INNER و LEFT و GROUP BY و HAVING و window functions
  36. 36JSONB في PostgreSQL: إمتى تستخدمه، وإزاي تسأله وتعمله index
  37. 37الـ Indexes من جُوّه: B-tree و GIN و BRIN
  38. 38Partial و Covering indexes: indexes أصغر وأسرع
  39. 39الـ Pagination بالـ Keyset: ليه OFFSET بيبطّأ وبيكرر
  40. 40Full Text Search في Postgres: بحث بالكلمات، بالعربي والإنجليزي
  41. 41EXPLAIN ANALYZE والـ Planner: اقرا الـ plan وصلّح الـ query
  42. 42الـ Transactions بـ node-postgres: كله أو ولا حاجة
  43. 43الـ MVCC ومستويات الـ Isolation: transactions كتير في نفس الوقت
  44. 44الـ Locks والـ Deadlocks: مين مستني مين
  45. 45SKIP LOCKED و Job Leasing: Postgres كـ job queue
  46. 46Row Level Security والـ Multi-tenancy: كل عميل يشوف بياناته بس
  47. 47Migrations من غير downtime: expand و backfill و contract
  48. 48الـ Replication والـ Read Replicas: نسخ حية من الـ database
  49. 49الـ Connection Pooling و PgBouncer: connections أقل، requests أكتر
  50. 50الـ Partitioning والـ VACUUM والمراقبة: Postgres على المدى الطويل
  51. 51Redis وأنواع البيانات: strings و hashes و lists و sets و sorted sets
  52. 52Cache-aside والـ TTL: Redis قدام الـ database
  53. 53الـ Cache invalidation والـ Stampede: لما الـ key الشهير يموت
  54. 54Lua والـ Atomicity في Redis: كام أمر كأنهم أمر واحد
  55. 55الـ Rate Limiting في Redis: fixed window و sliding window و token bucket
  56. 56الـ Sessions في Redis: تسجيل دخول تقدر تلغيه في أي وقت
  57. 57Redis Streams والـ Consumer Groups: log بيتوزع على workers
  58. 58Pub/Sub و BullMQ: إشارات بين الـ instances، وطوابير شغل محترمة
  59. 59Redis لما يقع: الـ persistence (RDB و AOF) والـ replication
  60. 60Redis في الإنتاج: الـ ACLs والـ Cluster والمراقبة
  61. 61تخزين الـ Passwords بـ Argon2id: صح من أول مرة
  62. 62Session Fixation والـ Rotation: id جديد مع كل صلاحية جديدة
  63. 63الـ Cookie Flags والـ CSRF: المتصفح بيبعت الـ cookie، فلازم تتأكد مين اللي طلب
  64. 64الـ XSS والـ Content Security Policy: ما تخليش النص يتحوّل لـ code
  65. 65الـ OAuth و OpenID Connect و PKCE: login بـ Google من غير ما تشوف الـ password
  66. 66التأكد من الـ JWT صح: التوقيع والـ claims والـ scopes
  67. 67الـ BOLA و BFLA: الـ token السليم مش معناه إن الطلب مسموح
  68. 68الـ RBAC و ABAC والملكية: policy واحدة بدل ifs متفرقة
  69. 69الـ MFA والـ recovery: التاني factor اللي بجد
  70. 70الـ Passkeys و WebAuthn: login من غير password وضد الـ phishing
  71. 71الـ Threat Modeling بـ STRIDE: فكّر زي المهاجم قبل ما تكتب code
  72. 72الـ SQL Injection والـ Parameterization: الـ data ما تبقاش code
  73. 73الـ SSRF والـ Egress: ما تخليش الـ server بتاعك يزور الشبكة الداخلية
  74. 74الـ Security Headers والـ TLS: كل response يقول للـ browser يحمي المستخدم إزاي
  75. 75توقيع الـ Webhooks والـ Replay: الـ webhook ده جاي فعلًا من Stripe؟
  76. 76تغيير الأسرار والـ HMAC: إزاي تغيّر مفتاح من غير ما كل حاجة تقع
  77. 77سلسلة التوريد والـ SBOM: إنت واثق في ألف package ما تعرفهمش؟
  78. 78الـ Audit والـ Privacy Logs: سجّل مين عمل إيه، من غير ما تسجّل الأسرار
  79. 79اختبار الأمان و OWASP ASVS: الأمن جُوّه الـ CI، مش audit آخر السنة
  80. 80الـ Prototype Pollution والـ Resource DoS: request واحد يخلّي الكل admin أو يوقّع الـ server
  81. 81الـ SLI والـ SLO والـ Error Budget: إمتى تصحّي الـ on-call وإمتى تسيبه نايم
  82. 82الـ Event Loop Delay والـ ELU: لما Node نفسه يتخنق
  83. 83الـ Worker Threads والـ CPU Profiling: الشغل التقيل بره الـ event loop، والوقت راح فين
  84. 84الـ Memory والـ GC والـ Leaks: الـ process اللي بيكبر لحد ما يقع
  85. 85الـ N+1 Queries والـ DataLoader: صفحة واحدة بمِيّتين query
  86. 86الـ HTTP Caching والـ Compression: أسرع request هو اللي ما بيوصلش
  87. 87الـ Load Testing والـ Soak Testing: الـ server بيستحمل قد إيه بجد
  88. 88الـ Concurrency Limits والـ Bulkheads: dependency بطيئة ما توقعش الـ app كله
  89. 89الـ Timeouts والـ Retries والـ Circuit Breakers: إزاي تتعامل مع dependency بطيئة أو واقعة
  90. 90OpenTelemetry والـ Distributed Tracing: الـ request راح فين وضاع وقته فين
  91. 91Docker Layers والـ Build Context: ليه الـ build بياخد دقيقة وفيه الـ .env بتاعك
  92. 92Multi-stage Builds و Non-root: image صغيرة ومن غير root
  93. 93Docker Compose والـ Networks: الـ services بتلاقي بعض إزاي
  94. 94Docker Volumes: الـ data بتتحفَظ فين
  95. 95Healthcheck و Restart و Resource Limits: running مش معناها شغّال
  96. 96BuildKit والـ Build Secrets: الـ token ما يدخلش الـ image
  97. 97Reverse Proxy و TLS: اللي واقف قدام الـ app
  98. 98Multi-arch Images والـ Native Modules: اشتغلت عندي
  99. 99CI/CD و Rollback و Canary: الـ deploy يبقى ممل
  100. 100Kubernetes من الأول: Pods و Deployments و Services و Probes
  101. 101Modular Monolith و DDD: حدود واضحة من غير network
  102. 102Transactional Outbox و Inbox: الـ event ما يضيعش وما يتنفذش مرتين
  103. 103Sagas: عملية واحدة على تلات services من غير transaction واحدة
  104. 104CQRS و Event Sourcing: الـ events هي الحقيقة
  105. 105الـ Queues والـ Brokers: BullMQ و RabbitMQ و Kafka و SQS
  106. 106Realtime على أكتر من instance: Redis pub/sub و presence و resume
  107. 107الـ Object Storage والـ Media: presigned URLs و uploads مباشرة
  108. 108التكامل مع الـ Email والـ Search: retries و webhooks و indexes
  109. 109GraphQL و gRPC في الإنتاج: contracts و DataLoader و limits و deadlines
  110. 110Kafka على أكتر من zone و region: الـ replication والـ ISR والـ acks والـ MirrorMaker والـ failover
  111. 111TypeScript و runtime boundaries: الـ types بتختفي، والـ schemas لأ
  112. 112ORM ولا SQL: query builders و repositories و migrations و transactions
  113. 113الـ Serverless من جُوّه: cold starts و freeze و timeouts و connections
  114. 114الـ Testing Pyramid: unit و integration و e2e على Postgres حقيقية
  115. 115Contract Testing و Property-Based Testing بـ fast-check
  116. 116الـ Payments: idempotency و webhooks و refunds و reconciliation
  117. 117الـ Timezones والـ Internationalization: UTC و DST والـ locales والـ plurals والفلوس
  118. 118الـ Privacy والـ Data Retention: consent و export و erasure و crypto shredding
  119. 119الـ ADRs والـ Feature Flags: القرارات المكتوبة والـ rollout من غير deploy
  120. 120Multi-region active-active: حل الـ conflicts بـ HLC و CRDTs
  121. 121Readiness و Liveness و Startup Probes: الـ pod جاهز ولا عايش؟
  122. 122Incident Runbooks: من الـ alert للـ postmortem