TypeScript📅 2026-10-09⏱ 10 دقائق قراءة📘 مقال 10 من 10

مشروع Node.js مع TypeScript — API كامل 2026

مشروع عملي لبناء API كامل بـ Node.js و Express و TypeScript — خطوة بخطوة مع الكود الكامل والشرح

في المقال السابق، بنيت تطبيق React كاملاً بـ TypeScript. الآن سننتقل إلى Backend — سنبني API كامل باستخدام Node.js و Express و TypeScript.

هذا المشروع سيعلمك:

  • إعداد مشروع Node.js مع TypeScript.
  • بناء API مع Express.
  • تعريف الأنواع للطلبات والاستجابات.
  • معالجة الأخطاء.
  • تنظيم الكود في طبقات (Routes, Controllers, Services).

ما سنبنيه

API لإدارة المهام (Todos) بوظائف:

  • GET /api/todos — جلب كل المهام.
  • GET /api/todos/:id — جلب مهمة واحدة.
  • POST /api/todos — إضافة مهمة جديدة.
  • PUT /api/todos/:id — تعديل مهمة.
  • DELETE /api/todos/:id — حذف مهمة.

هيكل المشروع

سننشئ:

node-todo-api/
├── src/
│   ├── types/
│   │   └── todo.ts
│   ├── controllers/
│   │   └── todoController.ts
│   ├── services/
│   │   └── todoService.ts
│   ├── routes/
│   │   └── todoRoutes.ts
│   ├── middleware/
│   │   └── errorHandler.ts
│   ├── app.ts
│   └── server.ts
├── package.json
├── tsconfig.json
└── .env

الخطوة 1: إنشاء المشروع

افتح Terminal، واكتب:

mkdir node-todo-api
cd node-todo-api
npm init -y
npm install express cors
npm install -D typescript @types/node @types/express @types/cors ts-node nodemon

شرح المكتبات:

  • express: إطار عمل للـ API.
  • cors: للسماح بالطلبات من نطاقات مختلفة.
  • typescript: لغة TypeScript.
  • @types/*: تعريفات الأنواع.
  • ts-node: تشغيل TypeScript مباشرة.
  • nodemon: إعادة تشغيل السيرفر تلقائياً.

الخطوة 2: إعداد TypeScript

أنشئ ملف tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["ES2020"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "declaration": true,
    "sourceMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

الخطوة 3: تحديث package.json

افتح package.json وعدّل scripts:

{
  "name": "node-todo-api",
  "version": "1.0.0",
  "scripts": {
    "dev": "nodemon --exec ts-node src/server.ts",
    "build": "tsc",
    "start": "node dist/server.js"
  },
  "dependencies": {
    "express": "^4.18.2",
    "cors": "^2.8.5"
  },
  "devDependencies": {
    "typescript": "^5.0.0",
    "@types/node": "^20.0.0",
    "@types/express": "^4.17.17",
    "@types/cors": "^2.8.13",
    "ts-node": "^10.9.1",
    "nodemon": "^3.0.1"
  }
}

الخطوة 4: تعريف الأنواع (Types)

أنشئ ملف src/types/todo.ts:

// المهمة الواحدة
export interface Todo {
  id: string;
  text: string;
  completed: boolean;
  createdAt: string;
}

// طلب إضافة مهمة
export interface CreateTodoDTO {
  text: string;
}

// طلب تعديل مهمة
export interface UpdateTodoDTO {
  text?: string;
  completed?: boolean;
}

// استجابة API
export interface ApiResponse<T> {
  success: boolean;
  data?: T;
  message?: string;
  error?: string;
}

شرح:

  • Todo: شكل المهمة الكاملة.
  • CreateTodoDTO: البيانات المطلوبة لإضافة مهمة.
  • UpdateTodoDTO: البيانات المطلوبة للتعديل (كلها اختيارية).
  • ApiResponse<T>: استجابة عامة (Generic) لأي نوع.

الخطوة 5: طبقة الخدمات (Service)

هذه الطبقة تحتوي على منطق العمل (Business Logic).

أنشئ ملف src/services/todoService.ts:

import { Todo, CreateTodoDTO, UpdateTodoDTO } from "../types/todo";
import { randomUUID } from "crypto";

class TodoService {
  private todos: Todo[] = [];

  // جلب كل المهام
  getAll(): Todo[] {
    return this.todos;
  }

  // جلب مهمة واحدة
  getById(id: string): Todo | undefined {
    return this.todos.find((todo) => todo.id === id);
  }

  // إضافة مهمة
  create(dto: CreateTodoDTO): Todo {
    const newTodo: Todo = {
      id: randomUUID(),
      text: dto.text,
      completed: false,
      createdAt: new Date().toISOString(),
    };
    this.todos.push(newTodo);
    return newTodo;
  }

  // تعديل مهمة
  update(id: string, dto: UpdateTodoDTO): Todo | null {
    const index = this.todos.findIndex((todo) => todo.id === id);
    if (index === -1) return null;

    this.todos[index] = {
      ...this.todos[index],
      ...dto,
    };
    return this.todos[index];
  }

  // حذف مهمة
  delete(id: string): boolean {
    const index = this.todos.findIndex((todo) => todo.id === id);
    if (index === -1) return false;

    this.todos.splice(index, 1);
    return true;
  }
}

export const todoService = new TodoService();

شرح:

  • class TodoService: فئة تحتوي على منطق العمل.
  • private todos: مصفوفة داخلية (سنستبدلها بقاعدة بيانات لاحقاً).
  • getAll, getById, create, update, delete: الطرق الأساسية.
  • export const todoService: كائن واحد (Singleton).

الخطوة 6: طبقة المتحكمات (Controllers)

هذه الطبقة تتعامل مع الطلبات والاستجابات.

أنشئ ملف src/controllers/todoController.ts:

import { Request, Response, NextFunction } from "express";
import { todoService } from "../services/todoService";
import { CreateTodoDTO, UpdateTodoDTO, ApiResponse, Todo } from "../types/todo";

// جلب كل المهام
export const getAllTodos = (
  req: Request,
  res: Response<ApiResponse<Todo[]>>,
  next: NextFunction
): void => {
  try {
    const todos = todoService.getAll();
    res.json({
      success: true,
      data: todos,
    });
  } catch (error) {
    next(error);
  }
};

// جلب مهمة واحدة
export const getTodoById = (
  req: Request<{ id: string }>,
  res: Response<ApiResponse<Todo>>,
  next: NextFunction
): void => {
  try {
    const todo = todoService.getById(req.params.id);
    if (!todo) {
      res.status(404).json({
        success: false,
        error: "المهمة غير موجودة",
      });
      return;
    }
    res.json({
      success: true,
      data: todo,
    });
  } catch (error) {
    next(error);
  }
};

// إضافة مهمة
export const createTodo = (
  req: Request<{}, {}, CreateTodoDTO>,
  res: Response<ApiResponse<Todo>>,
  next: NextFunction
): void => {
  try {
    const { text } = req.body;
    if (!text || text.trim() === "") {
      res.status(400).json({
        success: false,
        error: "النص مطلوب",
      });
      return;
    }
    const newTodo = todoService.create({ text: text.trim() });
    res.status(201).json({
      success: true,
      data: newTodo,
      message: "تم إنشاء المهمة بنجاح",
    });
  } catch (error) {
    next(error);
  }
};

// تعديل مهمة
export const updateTodo = (
  req: Request<{ id: string }, {}, UpdateTodoDTO>,
  res: Response<ApiResponse<Todo>>,
  next: NextFunction
): void => {
  try {
    const updated = todoService.update(req.params.id, req.body);
    if (!updated) {
      res.status(404).json({
        success: false,
        error: "المهمة غير موجودة",
      });
      return;
    }
    res.json({
      success: true,
      data: updated,
      message: "تم التحديث بنجاح",
    });
  } catch (error) {
    next(error);
  }
};

// حذف مهمة
export const deleteTodo = (
  req: Request<{ id: string }>,
  res: Response<ApiResponse<null>>,
  next: NextFunction
): void => {
  try {
    const deleted = todoService.delete(req.params.id);
    if (!deleted) {
      res.status(404).json({
        success: false,
        error: "المهمة غير موجودة",
      });
      return;
    }
    res.json({
      success: true,
      message: "تم الحذف بنجاح",
    });
  } catch (error) {
    next(error);
  }
};

شرح:

  • Request<Params, ResBody, ReqBody>: أنواع Express.
  • Response<ApiResponse<T>>: الاستجابة بنوع عام.
  • next: لدالة معالجة الأخطاء.

الخطوة 7: طبقة المسارات (Routes)

أنشئ ملف src/routes/todoRoutes.ts:

import { Router } from "express";
import {
  getAllTodos,
  getTodoById,
  createTodo,
  updateTodo,
  deleteTodo,
} from "../controllers/todoController";

const router = Router();

router.get("/", getAllTodos);
router.get("/:id", getTodoById);
router.post("/", createTodo);
router.put("/:id", updateTodo);
router.delete("/:id", deleteTodo);

export default router;

الخطوة 8: Middleware لمعالجة الأخطاء

أنشئ ملف src/middleware/errorHandler.ts:

import { Request, Response, NextFunction } from "express";
import { ApiResponse } from "../types/todo";

export const errorHandler = (
  err: Error,
  req: Request,
  res: Response<ApiResponse<null>>,
  next: NextFunction
): void => {
  console.error("Error:", err.message);
  res.status(500).json({
    success: false,
    error: "حدث خطأ في السيرفر",
  });
};

export const notFoundHandler = (
  req: Request,
  res: Response<ApiResponse<null>>
): void => {
  res.status(404).json({
    success: false,
    error: "المسار غير موجود",
  });
};

الخطوة 9: تطبيق Express

أنشئ ملف src/app.ts:

import express, { Application } from "express";
import cors from "cors";
import todoRoutes from "./routes/todoRoutes";
import { errorHandler, notFoundHandler } from "./middleware/errorHandler";

const app: Application = express();

// Middleware
app.use(cors());
app.use(express.json());

// Routes
app.use("/api/todos", todoRoutes);

// Health check
app.get("/api/health", (req, res) => {
  res.json({ status: "OK", timestamp: new Date().toISOString() });
});

// Error handlers (بعد كل المسارات)
app.use(notFoundHandler);
app.use(errorHandler);

export default app;

الخطوة 10: نقطة البداية (Server)

أنشئ ملف src/server.ts:

import app from "./app";

const PORT = process.env.PORT || 5000;

app.listen(PORT, () => {
  console.log(`🚀 السيرفر يعمل على http://localhost:${PORT}`);
  console.log(`📋 API: http://localhost:${PORT}/api/todos`);
});

الخطوة 11: تشغيل المشروع

npm run dev

النتيجة:

🚀 السيرفر يعمل على http://localhost:5000
📋 API: http://localhost:5000/api/todos

🎉 مبروك! لديك API كامل يعمل!

اختبار API

1. جلب كل المهام

curl http://localhost:5000/api/todos

النتيجة:

{ "success": true, "data": [] }

2. إضافة مهمة

curl -X POST http://localhost:5000/api/todos \
  -H "Content-Type: application/json" \
  -d '{"text": "تعلم TypeScript"}'

النتيجة:

{
  "success": true,
  "data": {
    "id": "abc-123",
    "text": "تعلم TypeScript",
    "completed": false,
    "createdAt": "2026-10-09T..."
  },
  "message": "تم إنشاء المهمة بنجاح"
}

3. تعديل مهمة

curl -X PUT http://localhost:5000/api/todos/abc-123 \
  -H "Content-Type: application/json" \
  -d '{"completed": true}'

4. حذف مهمة

curl -X DELETE http://localhost:5000/api/todos/abc-123

فهم كيف يعمل TypeScript هنا

1. الأنواع في الطلبات

req: Request<{ id: string }, {}, UpdateTodoDTO>

الفائدة: req.params.id هو string، و req.body هو UpdateTodoDTO.

2. الأنواع في الاستجابات

res: Response<ApiResponse<Todo>>

الفائدة: كل استجابة تطابق ApiResponse<Todo>.

3. الأدوية (Generics)

interface ApiResponse<T> {
  success: boolean;
  data?: T;
}

الفائدة: نفس الـ Interface يعمل مع Todo[], Todo, null.

تمارين إضافية

تمرين 1: إضافة البحث

أضف GET /api/todos/search?q=text.

الحل:

// في Service
search(query: string): Todo[] {
  return this.todos.filter((t) =>
    t.text.toLowerCase().includes(query.toLowerCase())
  );
}

// في Controller
export const searchTodos = (req, res, next) => {
  const query = req.query.q as string || "";
  const results = todoService.search(query);
  res.json({ success: true, data: results });
};

تمرين 2: Pagination

أضف ?page=1&limit=10.

الحل:

getPaginated(page: number, limit: number): Todo[] {
  const start = (page - 1) * limit;
  return this.todos.slice(start, start + limit);
}

تمرين 3: تصفية حسب completed

أضف ?completed=true.

الحل:

getFiltered(completed?: boolean): Todo[] {
  if (completed === undefined) return this.todos;
  return this.todos.filter((t) => t.completed === completed);
}

تمرين 4: التحقق من البيانات

أضف مكتبة zod للتحقق.

الحل:

npm install zod
import { z } from "zod";

const CreateTodoSchema = z.object({
  text: z.string().min(1).max(200),
});

// في Controller
const result = CreateTodoSchema.safeParse(req.body);
if (!result.success) {
  res.status(400).json({
    success: false,
    error: result.error.errors[0].message,
  });
  return;
}

تمرين 5: قاعدة بيانات حقيقية

استبدل المصفوفة بـ SQLite أو MongoDB.

الحل باستخدام Prisma + SQLite:

npm install prisma @prisma/client
npx prisma init --datasource-provider sqlite

ثم:

model Todo {
  id        String   @id @default(uuid())
  text      String
  completed Boolean  @default(false)
  createdAt DateTime @default(now())
}

حل المشاكل الشائعة

🔴 المشكلة 1: Cannot find module 'express'

السبب: المكتبة غير مثبتة.

الحل:

npm install express @types/express

🔴 المشكلة 2: Port 5000 already in use

السبب: منفذ مشغول.

الحل: استخدم منفذاً آخر:

const PORT = process.env.PORT || 5001;

🔴 المشكلة 3: req.body is undefined

السبب: لم تضف express.json().

الحل:

app.use(express.json());

🔴 المشكلة 4: Type 'X' is not assignable to type 'Y'

السبب: نوع خاطئ في الطلب أو الاستجابة.

الحل: تأكد من تطابق الأنواع.

جدول الأوامر الأساسية

الأمرالوظيفة
npm run devتشغيل السيرفر
npm run buildبناء المشروع
npm startتشغيل الإنتاج
curl http://localhost:5000/api/todosاختبار API

قائمة تحقق نهائية

المهمةالحالة
إنشاء المشروع وتثبيت المكتبات⬜
إعداد tsconfig.json⬜
تعريف الأنواع (types/todo.ts)⬜
إنشاء Service⬜
إنشاء Controllers⬜
إنشاء Routes⬜
إضافة Error Handler⬜
إعداد app.ts و server.ts⬜
تشغيل السيرفر بنجاح⬜
اختبار API بـ curl⬜
حل تمرين واحد على الأقل⬜

🎉 مبروك! أكملت مسار TypeScript!

لقد وصلت إلى نهاية المسار. أنت الآن تعرف:

  • ✅ أساسيات TypeScript: الأنواع، الواجهات، الدوال.
  • ✅ المفاهيم المتقدمة: الفئات، الأدوية، الأنواع المتقدمة.
  • ✅ مشروع React: واجهة أمامية تفاعلية.
  • ✅ مشروع Node.js: API خلفي كامل.

ماذا بعد؟

الآن أنت جاهز لـ:

  1. Next.js + TypeScript — إطار عمل كامل.
  2. قواعد البيانات — PostgreSQL, MongoDB.
  3. المصادقة — JWT, OAuth.
  4. النشر — Vercel, Railway, AWS.
  5. الاختبارات — Jest, Vitest.
  6. بناء معرض أعمالك — ارفع مشاريعك على GitHub.

الخلاصة

في هذا المشروع، طبقت:

  • ✅ Node.js و Express لبناء API.
  • ✅ TypeScript للأنواع والأمان.
  • ✅ تنظيم الكود في طبقات.
  • ✅ معالجة الأخطاء بشكل احترافي.
  • ✅ الأدوية (Generics) في ApiResponse<T>.
  • ✅ الواجهات لكل نوع بيانات.
  • ✅ اختبار API بـ curl.

هذا المشروع هو أساس كل Backend احترافي. احتفظ بالكود، وطور فيه بنفسك!

🎉 هل أكملت مسار TypeScript بالكامل؟ شاركنا في التعليقات!

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

TypeScript

مشروع React مع TypeScript — تطبيق مهام كامل 2026

مشروع عملي لبناء تطبيق مهام (Todo App) بـ React و TypeScript — خطوة بخطوة مع الكود الكامل والشرح

TypeScript

الأنواع المتقدمة في TypeScript — دليل شامل 2026

دليل عملي مفصل للأنواع المتقدمة في TypeScript — Union, Intersection, Conditional, Mapped, Template Literal، مع تمارين وحلول

TypeScript

الأدوية (Generics) في TypeScript — دليل شامل 2026

دليل عملي مفصل للأدوية في TypeScript — الدوال العامة، الواجهات العامة، القيود، مع تمارين وحلول