تطوير الويب

تهيئة monorepo Nx 22 مع NestJS 11

3 min de lecture

السلسلة: هذا الدرس جزء من سلسلة NestJS 11. اقرأ المقال الرئيسي.

monorepo Nx 22 اليوم أنجع هيكل لمنتج ينطلق بـ API NestJS ويخطّط لإضافة عميل web أو mobile سريعاً. بدل إدارة 3 مستودعات git، 3 خطوط CI و3 مجموعات تبعيات تنحرف مع الوقت، نُشارك workspace واحداً، libs types مشتركة، وأمر nx affected الذي يُعيد بناء ما تغيّر فقط.

المتطلبات

  • Node.js 22.x LTS (Jod)
  • pnpm 9.x — موصى به للـ monorepos (ربح 40% على install مقابل npm)
  • Git 2.40+
  • حساب GitHub
  • المستوى: متوسط
  • 60-90 دقيقة

الخطوة 1 — إنشاء workspace Nx

pnpm dlx create-nx-workspace@22.6.5 acme \
  --preset=apps \
  --packageManager=pnpm \
  --nxCloud=skip
cd acme

الخيار --preset=apps ينشئ workspace أدنى بلا framework مفروض. --nxCloud=skip يُعطّل دعوة Nx Cloud. النتيجة: مجلد acme/ يحوي nx.json، package.json جذر ومجلدَي apps/ وlibs/ فارغين. nx graph يفتح واجهة web ترسم رسماً بيانياً للمشاريع.

الخطوة 2 — إضافة plugin Nest وتوليد التطبيق api

pnpm add -D @nx/nest@22.6.5
nx g @nx/nest:application api \
  --linter=eslint \
  --unitTestRunner=jest \
  --e2eTestRunner=jest

المولّد ينشئ apps/api/ ببنية NestJS المعيارية (main.ts، app.module.ts، إلخ) ومشروع apps/api-e2e/ لاختبارات التكامل. nx serve api يطلق الخادم على المنفذ 3000. للتحقق: curl http://localhost:3000/api يعيد {"message":"Hello API"}.

الخطوة 3 — إنشاء مكتبة types مشتركة

الفائدة الرئيسية لـ monorepo مشاركة الكود بين عدة تطبيقات. مكتبة shared-types تحوي DTO وenums المستخدَمة من backend والعملاء المستقبليين تتجنّب تكرار الواجهة في مستودعين وانحرافها بمرور الوقت.

pnpm add -D @nx/js@22.6.5
nx g @nx/js:library shared-types \
  --directory=libs/shared-types \
  --bundler=tsc \
  --unitTestRunner=jest

المكتبة في libs/shared-types/src/ مع index.ts يُصدّر ما تستهلكه المشاريع الأخرى. الاستيراد جانب apps/api بـ import { UserDto } from '@acme/shared-types' بفضل حلّ المسارات في tsconfig.base.json.

الخطوة 4 — تفعيل TypeScript strict وESLint مشترك

مشروع ينطلق بلا "strict": true يراكم ديون تنميط مكلفة لاحقاً. تفعيل strict منذ أول commit يفرض كود دفاعياً يكشف bugs عند build لا في الإنتاج.

// tsconfig.base.json (مقتطف)
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true
  }
}

الخيار noUncheckedIndexedAccess غالباً الأفيد لـ backend: يفرض التحقق أن وصولاً لمصفوفة أو كائن قد يُرجع undefined. يلغي عائلة كاملة من bugs «cannot read property of undefined».

الخطوة 5 — تقسيم API إلى modules أعمال

NestJS يشجّع التقسيم حسب المجال لا الطبقة التقنية. بدل مجلد controllers/، services/، dtos/ تخلط كل الميزات، ننشئ مجلداً لكل module أعمال.

nx g @nx/nest:module billing --project=api
nx g @nx/nest:controller billing --project=api --module=billing
nx g @nx/nest:service billing --project=api --module=billing

الثلاثة أوامر تنشئ apps/api/src/billing/ مع billing.module.ts، billing.controller.ts وbilling.service.ts. الـ module BillingModule لا يكشف خارجياً إلا ما يُعلنه في exports — افتراضياً لا شيء.

الخطوة 6 — ضبط CI GitHub Actions مع nx affected

# .github/workflows/ci.yml
name: CI
on:
  pull_request:
    branches: [main]
jobs:
  affected:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with: { fetch-depth: 0 }
      - uses: actions/setup-node@v5
        with: { node-version: 22 }
      - uses: pnpm/action-setup@v6
        with: { version: 9 }
      - run: pnpm install --frozen-lockfile
      - run: npx nx affected -t lint test build --base=origin/main

fetch-depth: 0 إلزامي: بلا تاريخ git كامل، Nx لا يستطيع حساب قاعدة المقارنة ويُعيد بناء كل شيء. --base=origin/main يدلّ على فرع المرجع.

الخطوة 7 — التحقق من الرسم البياني وتثبيت التبعيات

nx graph              # واجهة web على localhost:4211
nx report             # إصدارات plugins
nx affected:graph     # رسم بياني للمتأثّرة منذ main

إن رفع nx report plugin بإصدار مختلف عن نواة Nx، الأمر nx migrate latest يُحاذي الكل ويطبّق codemods الضرورية تلقائياً. عملية يجب فعلها كل 3 أشهر للبقاء على patches الأمنية.

الخطوة 8 — الاستفادة من cache Nx المحلي

Nx يُخزّن مخرَج مهام build، test وlint في .nx/cache/. تنفيذ ثانٍ على نفس commit، حتى بعد reboot، يستعيد المخرَج فوراً بلا إعادة تنفيذ.

nx build api          # المرة الأولى: 8 ثوان
# عدّل ملف غير ذي صلة
nx build api          # المرة الثانية: cache hit، 200 مللي ثانية
nx reset              # تفريغ cache عند الحاجة

الآلية تعتمد hash محسوب من ملفات إدخال المشروع وتبعياته. إن لم يتغيّر hash، يُستعاد المخرَج.

ممارسات تنظيم libs

بدل مكتبة واحدة shared-types تنتهي بحمل كل شيء، نظّم libs حسب scope ونوع. scope يطابق المجال (billing، users، orders)، النوع يطابق طبيعة الكود (data-access، feature، ui، util). مكتبة billing-data-access تحوي استدعاءات API والأنواع؛ billing-feature تحوي منطق الأعمال؛ billing-ui تحوي المكوّنات.

قيود الاستيراد تُعلَن في .eslintrc.json عبر القاعدة @nx/enforce-module-boundaries. مكتبة موسومة type:util لا تستطيع استهلاك إلا مكتبات type:util أخرى.

أخطاء شائعة

الخطأ السبب الحل
NX 0001 عند generate Plugin Nx غير مُحاذٍ مع core nx migrate latest
Import @acme/shared-types غير مُحلَّل Path alias غائب من tsconfig.base.json أعد توليد lib أو أضف المسار
CI تُعيد البناء على كل commit fetch-depth مفقود أضف fetch-depth: 0 إلى checkout
تعارض pnpm-lock بين الفروع إصدارات pnpm مختلفة ثبّت packageManager في package.json
nx serve api لا يُعيد التحميل Watch SWC سيء الإعداد تحقّق --watch في project.json

أسئلة شائعة

لماذا pnpm بدل npm أو yarn؟ pnpm يستخدم store عالمياً وروابط رمزية بدل نسخ الحزم. على monorepo بـ 5 مشاريع، install ينتقل من 90 ثانية (npm) إلى 30 ثانية (pnpm).

هل نُفعّل Nx Cloud؟ لمشروع ناشئ، لا. cache محلي وكشف affected يغطّيان 90% من الربح. Nx Cloud يصير ملائماً عندما يتجاوز الفريق 5 مطوّرين.

هل يمكن خلط NestJS وNext.js في نفس workspace؟ نعم، حتى استخدام شائع. أضف plugin @nx/next. الفخّ الوحيد عزل متغيّرات البيئة جيداً: NEXT_PUBLIC_* جانب front، API_* جانب backend.

كيف نُهاجر مشروع NestJS موجود نحو Nx؟ الأمر nx init يكتشف مشروعاً موجوداً ويقترح هجرة تدريجية تحفظ scripts npm وبنية المجلدات.

مقالات ذات صلة

Sponsoriser ce contenu

Cet emplacement est à vous

Position premium en fin d'article — c'est l'instant où les lecteurs sont le plus engagés. Réservez cet espace pour votre marque, votre formation ou votre offre.

Recevoir nos tarifs
Publicité