TypeScript
مشروع Node.js مع TypeScript — API كامل 2026
📅 2026-10-09⏱ 10 دقائق قراءة
في المقال السابق، بنيت تطبيق 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، واكتب:
```bash
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`:**
```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`:
```json
{
"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`:**
```typescript
// المهمة الواحدة
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`:**
```typescript
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`:**
```typescript
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`:**
```typescript
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`:**
```typescript
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`:**
```typescript
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`:**
```typescript
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: تشغيل المشروع
```bash
npm run dev
```
**النتيجة:**
```bash
🚀 السيرفر يعمل على http://localhost:5000
📋 API: http://localhost:5000/api/todos
```
**🎉 مبروك! لديك API كامل يعمل!**
## اختبار API
### 1. جلب كل المهام
```bash
curl http://localhost:5000/api/todos
```
**النتيجة:**
```json
{ "success": true, "data": [] }
```
### 2. إضافة مهمة
```bash
curl -X POST http://localhost:5000/api/todos \
-H "Content-Type: application/json" \
-d '{"text": "تعلم TypeScript"}'
```
**النتيجة:**
```json
{
"success": true,
"data": {
"id": "abc-123",
"text": "تعلم TypeScript",
"completed": false,
"createdAt": "2026-10-09T..."
},
"message": "تم إنشاء المهمة بنجاح"
}
```
### 3. تعديل مهمة
```bash
curl -X PUT http://localhost:5000/api/todos/abc-123 \
-H "Content-Type: application/json" \
-d '{"completed": true}'
```
### 4. حذف مهمة
```bash
curl -X DELETE http://localhost:5000/api/todos/abc-123
```
## فهم كيف يعمل TypeScript هنا
### 1. الأنواع في الطلبات
```typescript
req: Request<{ id: string }, {}, UpdateTodoDTO>
```
**الفائدة:** `req.params.id` هو `string`، و `req.body` هو `UpdateTodoDTO`.
### 2. الأنواع في الاستجابات
```typescript
res: Response<ApiResponse<Todo>>
```
**الفائدة:** كل استجابة تطابق `ApiResponse<Todo>`.
### 3. الأدوية (Generics)
```typescript
interface ApiResponse<T> {
success: boolean;
data?: T;
}
```
**الفائدة:** نفس الـ Interface يعمل مع `Todo[]`, `Todo`, `null`.
## تمارين إضافية
### تمرين 1: إضافة البحث
أضف `GET /api/todos/search?q=text`.
**الحل:**
```typescript
// في 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`.
**الحل:**
```typescript
getPaginated(page: number, limit: number): Todo[] {
const start = (page - 1) * limit;
return this.todos.slice(start, start + limit);
}
```
### تمرين 3: تصفية حسب completed
أضف `?completed=true`.
**الحل:**
```typescript
getFiltered(completed?: boolean): Todo[] {
if (completed === undefined) return this.todos;
return this.todos.filter((t) => t.completed === completed);
}
```
### تمرين 4: التحقق من البيانات
أضف مكتبة `zod` للتحقق.
**الحل:**
```bash
npm install zod
```
```typescript
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:**
```bash
npm install prisma @prisma/client
npx prisma init --datasource-provider sqlite
```
ثم:
```prisma
model Todo {
id String @id @default(uuid())
text String
completed Boolean @default(false)
createdAt DateTime @default(now())
}
```
## حل المشاكل الشائعة
### 🔴 المشكلة 1: `Cannot find module 'express'`
**السبب:** المكتبة غير مثبتة.
**الحل:**
```bash
npm install express @types/express
```
### 🔴 المشكلة 2: `Port 5000 already in use`
**السبب:** منفذ مشغول.
**الحل:** استخدم منفذاً آخر:
```typescript
const PORT = process.env.PORT || 5001;
```
### 🔴 المشكلة 3: `req.body is undefined`
**السبب:** لم تضف `express.json()`.
**الحل:**
```typescript
app.use(express.json());
```
### 🔴 المشكلة 4: `Type 'X' is not assignable to type 'Y'`
**السبب:** نوع خاطئ في الطلب أو الاستجابة.
**الحل:** تأكد من تطابق الأنواع.
## جدول الأوامر الأساسية
<table>
<thead>
<tr>
<th>الأمر</th>
<th>الوظيفة</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>npm run dev</code></td>
<td>تشغيل السيرفر</td>
</tr>
<tr>
<td><code>npm run build</code></td>
<td>بناء المشروع</td>
</tr>
<tr>
<td><code>npm start</code></td>
<td>تشغيل الإنتاج</td>
</tr>
<tr>
<td><code>curl http://localhost:5000/api/todos</code></td>
<td>اختبار API</td>
</tr>
</tbody>
</table>
## قائمة تحقق نهائية
<table>
<thead>
<tr>
<th>المهمة</th>
<th>الحالة</th>
</tr>
</thead>
<tbody>
<tr>
<td>إنشاء المشروع وتثبيت المكتبات</td>
<td>⬜</td>
</tr>
<tr>
<td>إعداد tsconfig.json</td>
<td>⬜</td>
</tr>
<tr>
<td>تعريف الأنواع (types/todo.ts)</td>
<td>⬜</td>
</tr>
<tr>
<td>إنشاء Service</td>
<td>⬜</td>
</tr>
<tr>
<td>إنشاء Controllers</td>
<td>⬜</td>
</tr>
<tr>
<td>إنشاء Routes</td>
<td>⬜</td>
</tr>
<tr>
<td>إضافة Error Handler</td>
<td>⬜</td>
</tr>
<tr>
<td>إعداد app.ts و server.ts</td>
<td>⬜</td>
</tr>
<tr>
<td>تشغيل السيرفر بنجاح</td>
<td>⬜</td>
</tr>
<tr>
<td>اختبار API بـ curl</td>
<td>⬜</td>
</tr>
<tr>
<td>حل تمرين واحد على الأقل</td>
<td>⬜</td>
</tr>
</tbody>
</table>
## 🎉 مبروك! أكملت مسار 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 بالكامل؟ شاركنا في التعليقات!**