Node.js
بناء REST API في Node.js — دليل شامل 2026
📅 2026-11-08⏱ 10 دقائق قراءة
في المقال السابق، تعلمت التوجيه و Middleware. الآن سنبني **REST API** كاملاً — وهو ما يربط الـ Frontend بالـ Backend.
في هذا الدليل العملي، سنأخذك خطوة بخطوة لبناء REST API احترافي، مع تمارين وحلول.
## ما هو REST API؟
**REST** = **Representational State Transfer**.
**REST API** هي طريقة لتصميم واجهات برمجية تعتمد على:
- **HTTP Methods:** GET، POST، PUT، DELETE.
- **Resources:** كيانات (مستخدم، منتج، مقال).
- **URLs واضحة:** `/users`، `/users/1`.
- **Status Codes:** 200، 201، 404، 500.
## مبادئ REST
<table>
<thead>
<tr>
<th>المبدأ</th>
<th>الشرح</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Client-Server</strong></td>
<td>فصل الواجهة عن البيانات</td>
</tr>
<tr>
<td><strong>Stateless</strong></td>
<td>كل طلب مستقل</td>
</tr>
<tr>
<td><strong>Cacheable</strong></td>
<td>يمكن تخزين الاستجابات</td>
</tr>
<tr>
<td><strong>Uniform Interface</strong></td>
<td>واجهة موحدة</td>
</tr>
<tr>
<td><strong>Layered System</strong></td>
<td>طبقات متعددة</td>
</tr>
</tbody>
</table>
## تصميم REST API
### القواعد الذهبية:
<table>
<thead>
<tr>
<th>القاعدة</th>
<th>مثال صحيح</th>
<th>مثال خاطئ</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>استخدم الأسماء (جمع)</strong></td>
<td><code>/users</code></td>
<td><code>/getUser</code></td>
</tr>
<tr>
<td><strong>الأسماء وليس الأفعال</strong></td>
<td><code>GET /users</code></td>
<td><code>/getUsers</code></td>
</tr>
<tr>
<td><strong>معرفات في URL</strong></td>
<td><code>/users/1</code></td>
<td><code>/users?id=1</code></td>
</tr>
<tr>
<td><strong>العلاقات المتداخلة</strong></td>
<td><code>/users/1/posts</code></td>
<td><code>/getUserPosts</code></td>
</tr>
<tr>
<td><strong>أحرف صغيرة</strong></td>
<td><code>/blog-posts</code></td>
<td><code>/BlogPosts</code></td>
</tr>
</tbody>
</table>
## مثال: API للمستخدمين
### جدول المسارات
<table>
<thead>
<tr>
<th>Method</th>
<th>URL</th>
<th>الوظيفة</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>GET</strong></td>
<td><code>/api/users</code></td>
<td>جلب كل المستخدمين</td>
</tr>
<tr>
<td><strong>GET</strong></td>
<td><code>/api/users/:id</code></td>
<td>جلب مستخدم واحد</td>
</tr>
<tr>
<td><strong>POST</strong></td>
<td><code>/api/users</code></td>
<td>إضافة مستخدم</td>
</tr>
<tr>
<td><strong>PUT</strong></td>
<td><code>/api/users/:id</code></td>
<td>تحديث مستخدم</td>
</tr>
<tr>
<td><strong>DELETE</strong></td>
<td><code>/api/users/:id</code></td>
<td>حذف مستخدم</td>
</tr>
</tbody>
</table>
## بنية المشروع الاحترافية
```
rest-api/
├── src/
│ ├── controllers/
│ │ └── userController.js
│ ├── routes/
│ │ └── userRoutes.js
│ ├── models/
│ │ └── User.js
│ ├── middleware/
│ │ ├── auth.js
│ │ ├── validate.js
│ │ └── errorHandler.js
│ ├── utils/
│ │ └── ApiError.js
│ └── app.js
├── server.js
├── .env
├── .gitignore
└── package.json
```
## الخطوة 1: إعداد المشروع
```bash
mkdir rest-api
cd rest-api
npm init -y
npm install express cors dotenv
npm install -D nodemon
```
## الخطوة 2: `package.json`
```json
{
"name": "rest-api",
"version": "1.0.0",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
},
"type": "commonjs"
}
```
## الخطوة 3: `.env`
```
PORT=3000
NODE_ENV=development
```
## الخطوة 4: `src/utils/ApiError.js`
```javascript
class ApiError extends Error {
constructor(statusCode, message) {
super(message);
this.statusCode = statusCode;
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
module.exports = ApiError;
```
## الخطوة 5: `src/utils/ApiResponse.js`
```javascript
class ApiResponse {
constructor(statusCode, data, message = "Success") {
this.statusCode = statusCode;
this.data = data;
this.message = message;
this.success = statusCode < 400;
}
}
module.exports = ApiResponse;
```
## الخطوة 6: `src/models/User.js` (في الذاكرة)
```javascript
// قاعدة بيانات مؤقتة (سنستبدلها بـ MongoDB لاحقاً)
let users = [
{ id: 1, name: "أحمد", email: "[email protected]", age: 25 },
{ id: 2, name: "محمد", email: "[email protected]", age: 30 },
];
let nextId = 3;
class User {
static findAll() {
return users;
}
static findById(id) {
return users.find((u) => u.id === parseInt(id));
}
static findByEmail(email) {
return users.find((u) => u.email === email);
}
static create(data) {
const user = { id: nextId++, ...data };
users.push(user);
return user;
}
static update(id, data) {
const user = users.find((u) => u.id === parseInt(id));
if (!user) return null;
Object.assign(user, data);
return user;
}
static delete(id) {
const index = users.findIndex((u) => u.id === parseInt(id));
if (index === -1) return false;
users.splice(index, 1);
return true;
}
}
module.exports = User;
```
## الخطوة 7: `src/controllers/userController.js`
```javascript
const User = require("../models/User");
const ApiError = require("../utils/ApiError");
const ApiResponse = require("../utils/ApiResponse");
// جلب كل المستخدمين
exports.getAllUsers = (req, res) => {
const users = User.findAll();
res.status(200).json(new ApiResponse(200, users, "تم جلب المستخدمين"));
};
// جلب مستخدم واحد
exports.getUserById = (req, res, next) => {
const user = User.findById(req.params.id);
if (!user) {
return next(new ApiError(404, "المستخدم غير موجود"));
}
res.status(200).json(new ApiResponse(200, user));
};
// إضافة مستخدم
exports.createUser = (req, res, next) => {
const { name, email, age } = req.body;
// التحقق من وجود البريد
if (User.findByEmail(email)) {
return next(new ApiError(400, "البريد مستخدم بالفعل"));
}
const user = User.create({ name, email, age });
res.status(201).json(new ApiResponse(201, user, "تم إنشاء المستخدم"));
};
// تحديث مستخدم
exports.updateUser = (req, res, next) => {
const user = User.update(req.params.id, req.body);
if (!user) {
return next(new ApiError(404, "المستخدم غير موجود"));
}
res.status(200).json(new ApiResponse(200, user, "تم التحديث"));
};
// حذف مستخدم
exports.deleteUser = (req, res, next) => {
const deleted = User.delete(req.params.id);
if (!deleted) {
return next(new ApiError(404, "المستخدم غير موجود"));
}
res.status(200).json(new ApiResponse(200, null, "تم الحذف"));
};
```
## الخطوة 8: `src/routes/userRoutes.js`
```javascript
const express = require("express");
const router = express.Router();
const userController = require("../controllers/userController");
const validate = require("../middleware/validate");
// التحقق من البيانات
const validateUser = validate({
name: { required: true, minLength: 2 },
email: { required: true, email: true },
age: { required: false, min: 18, max: 100 },
});
router.get("/", userController.getAllUsers);
router.get("/:id", userController.getUserById);
router.post("/", validateUser, userController.createUser);
router.put("/:id", userController.updateUser);
router.delete("/:id", userController.deleteUser);
module.exports = router;
```
## الخطوة 9: `src/middleware/validate.js`
```javascript
const ApiError = require("../utils/ApiError");
function validate(rules) {
return (req, res, next) => {
const errors = [];
for (const [field, rule] of Object.entries(rules)) {
const value = req.body[field];
if (rule.required && !value) {
errors.push(`${field} مطلوب`);
continue;
}
if (value) {
if (rule.minLength && value.length < rule.minLength) {
errors.push(`${field} قصير جداً (الحد الأدنى ${rule.minLength})`);
}
if (rule.maxLength && value.length > rule.maxLength) {
errors.push(`${field} طويل جداً`);
}
if (rule.email && !value.includes("@")) {
errors.push(`${field} غير صحيح`);
}
if (rule.min !== undefined && value < rule.min) {
errors.push(`${field} يجب أن يكون أكبر من ${rule.min}`);
}
if (rule.max !== undefined && value > rule.max) {
errors.push(`${field} يجب أن يكون أقل من ${rule.max}`);
}
}
}
if (errors.length > 0) {
return next(new ApiError(400, errors.join(", ")));
}
next();
};
}
module.exports = validate;
```
## الخطوة 10: `src/middleware/errorHandler.js`
```javascript
const ApiError = require("../utils/ApiError");
// 404 Handler
const notFound = (req, res, next) => {
next(new ApiError(404, `المسار غير موجود: ${req.originalUrl}`));
};
// Error Handler
const errorHandler = (err, req, res, next) => {
const statusCode = err.statusCode || 500;
const message = err.message || "خطأ في السيرفر";
// تسجيل الخطأ
if (process.env.NODE_ENV === "development") {
console.error("❌ خطأ:", err);
}
res.status(statusCode).json({
success: false,
statusCode,
message,
...(process.env.NODE_ENV === "development" && { stack: err.stack }),
});
};
module.exports = { notFound, errorHandler };
```
## الخطوة 11: `src/app.js`
```javascript
const express = require("express");
const cors = require("cors");
const { notFound, errorHandler } = require("./middleware/errorHandler");
const app = express();
// Middleware عام
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Logger
app.use((req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next();
});
// Health check
app.get("/api/health", (req, res) => {
res.json({
status: "OK",
timestamp: new Date().toISOString(),
uptime: process.uptime(),
});
});
// Routes
app.use("/api/users", require("./routes/userRoutes"));
// 404 و Error Handler (في النهاية)
app.use(notFound);
app.use(errorHandler);
module.exports = app;
```
## الخطوة 12: `server.js`
```javascript
require("dotenv").config();
const app = require("./src/app");
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`🚀 السيرفر يعمل على http://localhost:${PORT}`);
console.log(`📋 Health: http://localhost:${PORT}/api/health`);
console.log(`👥 Users: http://localhost:${PORT}/api/users`);
});
```
## اختبار API
### باستخدام `curl`:
```bash
# جلب كل المستخدمين
curl http://localhost:3000/api/users
# جلب مستخدم واحد
curl http://localhost:3000/api/users/1
# إضافة مستخدم
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-d '{"name":"علي","email":"[email protected]","age":28}'
# تحديث مستخدم
curl -X PUT http://localhost:3000/api/users/1 \
-H "Content-Type: application/json" \
-d '{"name":"أحمد محمد"}'
# حذف مستخدم
curl -X DELETE http://localhost:3000/api/users/1
```
### باستخدام Postman:
1. حمّل Postman من [postman.com](https://www.postman.com/)
2. جرّب الطلبات بنفس الطريقة.
3. احفظ المجموعة (Collection) لمشاركتها.
## شكل الاستجابات
### نجاح:
```json
{
"statusCode": 200,
"data": { "id": 1, "name": "أحمد" },
"message": "Success",
"success": true
}
```
### خطأ:
```json
{
"success": false,
"statusCode": 404,
"message": "المستخدم غير موجود"
}
```
## تمارين عملية
### تمرين 1: API للمقالات
أنشئ API كامل للمقالات (CRUD).
**الحل:**
```javascript
// src/models/Post.js
let posts = [];
let nextId = 1;
class Post {
static findAll() { return posts; }
static findById(id) { return posts.find((p) => p.id === parseInt(id)); }
static create(data) {
const post = { id: nextId++, ...data, createdAt: new Date() };
posts.push(post);
return post;
}
static update(id, data) {
const post = this.findById(id);
if (!post) return null;
Object.assign(post, data, { updatedAt: new Date() });
return post;
}
static delete(id) {
const index = posts.findIndex((p) => p.id === parseInt(id));
if (index === -1) return false;
posts.splice(index, 1);
return true;
}
}
module.exports = Post;
```
### تمرين 2: التحقق من البيانات
أضف تحققاً لمقالات.
**الحل:**
```javascript
const validatePost = validate({
title: { required: true, minLength: 5, maxLength: 200 },
content: { required: true, minLength: 20 },
});
```
### تمرين 3: البحث والتصفية
أضف `?q=` و `?sort=` للمستخدمين.
**الحل:**
```javascript
exports.getAllUsers = (req, res) => {
let users = User.findAll();
const { q, sort } = req.query;
if (q) {
users = users.filter((u) =>
u.name.toLowerCase().includes(q.toLowerCase())
);
}
if (sort === "name") {
users = users.sort((a, b) => a.name.localeCompare(b.name));
}
res.json(new ApiResponse(200, users));
};
```
### تمرين 4: Pagination
أضف `?page=1&limit=10`.
**الحل:**
```javascript
const { page = 1, limit = 10 } = req.query;
const start = (page - 1) * limit;
const paginated = users.slice(start, start + parseInt(limit));
res.json(new ApiResponse(200, {
users: paginated,
total: users.length,
page: parseInt(page),
totalPages: Math.ceil(users.length / limit),
}));
```
### تمرين 5: رفع الملفات
أضف رفع صورة المستخدم.
**الحل:**
```bash
npm install multer
```
```javascript
const multer = require("multer");
const upload = multer({ dest: "uploads/" });
router.post("/:id/avatar", upload.single("avatar"), (req, res) => {
res.json({ file: req.file });
});
```
### تمرين 6: Middleware للمصادقة
أضف JWT للمصادقة.
**الحل:**
```bash
npm install jsonwebtoken bcrypt
```
```javascript
function auth(req, res, next) {
const token = req.headers.authorization?.split(" ")[1];
if (!token) return next(new ApiError(401, "غير مصرح"));
try {
req.user = jwt.verify(token, process.env.JWT_SECRET);
next();
} catch {
next(new ApiError(401, "توكن غير صالح"));
}
}
router.get("/profile", auth, (req, res) => {
res.json(req.user);
});
```
### تمرين 7: Rate Limiting
أضف حداً للطلبات.
**الحل:**
```bash
npm install express-rate-limit
```
```javascript
const rateLimit = require("express-rate-limit");
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
});
app.use("/api", limiter);
```
### تمرين 8: API كامل
ابنِ API كامل مع كل الميزات.
**الحل:** (راجع المثال الكامل أعلاه)
## حل المشاكل الشائعة
### 🔴 المشكلة 1: CORS Error
**الحل:**
```javascript
const cors = require("cors");
app.use(cors());
```
### 🔴 المشكلة 2: `req.body` فارغ
**الحل:**
```javascript
app.use(express.json());
```
### 🔴 المشكلة 3: 500 Internal Server Error
**السبب:** خطأ غير معالج.
**الحل:** أضف `try/catch` أو استخدم Error Handler.
### 🔴 المشكلة 4: 404 لمسار موجود
**السبب:** 404 Handler قبل المسارات.
**الحل:** ضعه **بعد** كل المسارات.
### 🔴 المشكلة 5: Status Code خاطئ
**الحل:**
- **200:** نجاح.
- **201:** إنشاء.
- **204:** حذف.
- **400:** طلب خاطئ.
- **401:** غير مصرح.
- **404:** غير موجود.
- **500:** خطأ في السيرفر.
## جدول الاستجابات
<table>
<thead>
<tr>
<th>الحالة</th>
<th>الكود</th>
<th>الاستخدام</th>
</tr>
</thead>
<tbody>
<tr>
<td>نجاح</td>
<td>200</td>
<td>GET, PUT</td>
</tr>
<tr>
<td>إنشاء</td>
<td>201</td>
<td>POST</td>
</tr>
<tr>
<td>حذف</td>
<td>204</td>
<td>DELETE</td>
</tr>
<tr>
<td>طلب خاطئ</td>
<td>400</td>
<td>بيانات غير صالحة</td>
</tr>
<tr>
<td>غير مصرح</td>
<td>401</td>
<td>توكن مفقود</td>
</tr>
<tr>
<td>ممنوع</td>
<td>403</td>
<td>صلاحيات غير كافية</td>
</tr>
<tr>
<td>غير موجود</td>
<td>404</td>
<td>مسار/مورد</td>
</tr>
<tr>
<td>خطأ في السيرفر</td>
<td>500</td>
<td>خطأ داخلي</td>
</tr>
</tbody>
</table>
## قائمة تحقق نهائية
<table>
<thead>
<tr>
<th>المهمة</th>
<th>الحالة</th>
</tr>
</thead>
<tbody>
<tr>
<td>فهم مبادئ REST</td>
<td>⬜</td>
</tr>
<tr>
<td>تنظيم المشروع</td>
<td>⬜</td>
</tr>
<tr>
<td>فصل Controllers و Routes</td>
<td>⬜</td>
</tr>
<tr>
<td>التحقق من البيانات</td>
<td>⬜</td>
</tr>
<tr>
<td>معالجة الأخطاء</td>
<td>⬜</td>
</tr>
<tr>
<td>اختبار API</td>
<td>⬜</td>
</tr>
<tr>
<td>حل التمارين الثمانية</td>
<td>⬜</td>
</tr>
</tbody>
</table>
## ماذا بعد هذا المقال؟
الآن بعد أن أتقنت REST API، أنت جاهز للمقال التالي:
1. **MongoDB** — قاعدة بيانات حقيقية.
2. **المصادقة (JWT)** — تسجيل الدخول.
3. **مشروع متكامل** — API كامل مع قاعدة بيانات.
## الخلاصة
في هذا المقال، تعلمت:
- ✅ ما هو REST API.
- ✅ مبادئ REST.
- ✅ تنظيم المشروع الاحترافي.
- ✅ Controllers و Routes.
- ✅ التحقق من البيانات.
- ✅ معالجة الأخطاء.
- ✅ بناء API كامل.
- ✅ حل 8 تمارين عملية.
**تذكر:** REST API هو **الواجهة** التي تربط Frontend بـ Backend. أتقنه جيداً.