بناء REST API في Node.js — دليل شامل 2026
دليل عملي مفصل لبناء REST API احترافي — المبادئ، التنظيم، التحقق، والاختبار، مع تمارين وحلول
في المقال السابق، تعلمت التوجيه و 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
| المبدأ | الشرح |
|---|---|
| Client-Server | فصل الواجهة عن البيانات |
| Stateless | كل طلب مستقل |
| Cacheable | يمكن تخزين الاستجابات |
| Uniform Interface | واجهة موحدة |
| Layered System | طبقات متعددة |
تصميم REST API
القواعد الذهبية:
| القاعدة | مثال صحيح | مثال خاطئ |
|---|---|---|
| استخدم الأسماء (جمع) | /users | /getUser |
| الأسماء وليس الأفعال | GET /users | /getUsers |
| معرفات في URL | /users/1 | /users?id=1 |
| العلاقات المتداخلة | /users/1/posts | /getUserPosts |
| أحرف صغيرة | /blog-posts | /BlogPosts |
مثال: API للمستخدمين
جدول المسارات
| Method | URL | الوظيفة |
|---|---|---|
| GET | /api/users | جلب كل المستخدمين |
| GET | /api/users/:id | جلب مستخدم واحد |
| POST | /api/users | إضافة مستخدم |
| PUT | /api/users/:id | تحديث مستخدم |
| DELETE | /api/users/:id | حذف مستخدم |
بنية المشروع الاحترافية
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: إعداد المشروع
mkdir rest-api
cd rest-api
npm init -y
npm install express cors dotenv
npm install -D nodemon
الخطوة 2: package.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
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
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 (في الذاكرة)
// قاعدة بيانات مؤقتة (سنستبدلها بـ 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
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
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
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
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
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
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:
# جلب كل المستخدمين
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:
- حمّل Postman من postman.com
- جرّب الطلبات بنفس الطريقة.
- احفظ المجموعة (Collection) لمشاركتها.
شكل الاستجابات
نجاح:
{
"statusCode": 200,
"data": { "id": 1, "name": "أحمد" },
"message": "Success",
"success": true
}
خطأ:
{
"success": false,
"statusCode": 404,
"message": "المستخدم غير موجود"
}
تمارين عملية
تمرين 1: API للمقالات
أنشئ API كامل للمقالات (CRUD).
الحل:
// 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: التحقق من البيانات
أضف تحققاً لمقالات.
الحل:
const validatePost = validate({
title: { required: true, minLength: 5, maxLength: 200 },
content: { required: true, minLength: 20 },
});
تمرين 3: البحث والتصفية
أضف ?q= و ?sort= للمستخدمين.
الحل:
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.
الحل:
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: رفع الملفات
أضف رفع صورة المستخدم.
الحل:
npm install multer
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 للمصادقة.
الحل:
npm install jsonwebtoken bcrypt
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
أضف حداً للطلبات.
الحل:
npm install express-rate-limit
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
الحل:
const cors = require("cors");
app.use(cors());
🔴 المشكلة 2: req.body فارغ
الحل:
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: خطأ في السيرفر.
جدول الاستجابات
| الحالة | الكود | الاستخدام |
|---|---|---|
| نجاح | 200 | GET, PUT |
| إنشاء | 201 | POST |
| حذف | 204 | DELETE |
| طلب خاطئ | 400 | بيانات غير صالحة |
| غير مصرح | 401 | توكن مفقود |
| ممنوع | 403 | صلاحيات غير كافية |
| غير موجود | 404 | مسار/مورد |
| خطأ في السيرفر | 500 | خطأ داخلي |
قائمة تحقق نهائية
| المهمة | الحالة |
|---|---|
| فهم مبادئ REST | ⬜ |
| تنظيم المشروع | ⬜ |
| فصل Controllers و Routes | ⬜ |
| التحقق من البيانات | ⬜ |
| معالجة الأخطاء | ⬜ |
| اختبار API | ⬜ |
| حل التمارين الثمانية | ⬜ |
ماذا بعد هذا المقال؟
الآن بعد أن أتقنت REST API، أنت جاهز للمقال التالي:
- MongoDB — قاعدة بيانات حقيقية.
- المصادقة (JWT) — تسجيل الدخول.
- مشروع متكامل — API كامل مع قاعدة بيانات.
الخلاصة
في هذا المقال، تعلمت:
- ✅ ما هو REST API.
- ✅ مبادئ REST.
- ✅ تنظيم المشروع الاحترافي.
- ✅ Controllers و Routes.
- ✅ التحقق من البيانات.
- ✅ معالجة الأخطاء.
- ✅ بناء API كامل.
- ✅ حل 8 تمارين عملية.
تذكر: REST API هو الواجهة التي تربط Frontend بـ Backend. أتقنه جيداً.
التوجيه و Middleware في Express.js — دليل شامل 2026
MongoDB مع Node.js — دليل شامل 2026
📚 مقالات ذات صلة
مشروع Node.js متكامل — API مع مصادقة JWT 2026
مشروع عملي شامل لبناء API متكامل بـ Node.js و Express و MongoDB — مع مصادقة JWT، رفع الملفات، والنشر
MongoDB مع Node.js — دليل شامل 2026
دليل عملي مفصل لاستخدام MongoDB مع Node.js — Mongoose، Schema، CRUD، العلاقات، مع تمارين وحلول
التوجيه و Middleware في Express.js — دليل شامل 2026
دليل عملي مفصل للتوجيه (Routing) و Middleware في Express.js — Router، تنظيم المسارات، مع تمارين وحلول