الأخطاء في تصميم REST API في Laravel (من واقع تجربة)
خليني أكون واضح…
مشاكل الـ API في Laravel غالبًا ما تبدأ من التصميم، وليس من الكود نفسه.
اشتغلت على مشاريع SaaS وكان فيه APIs تشتغل بشكل ممتاز في البداية، لكن مع زيادة البيانات أو المستخدمين تبدأ المشاكل تظهر: بطء، تعقيد، وصعوبة تعديل أي شيء.
بعد مراجعة أكثر من مشروع، اكتشفت أن السبب يتكرر… أخطاء تصميم من البداية.
1. وضع كل المنطق داخل Controller
في البداية كنت أكتب كل شيء داخل Controller: إنشاء البيانات، إرسال إيميل، تسجيل Logs، وحتى business logic.
public function store(Request $request)
{
$user = User::create([...]);
Mail::to($user)->send(new WelcomeMail());
Log::info('User created');
event(new UserCreated($user));
return response()->json($user);
}
المشكلة تظهر مع الوقت:
- صعوبة قراءة الكود
- صعوبة الاختبار
- أي تعديل بسيط يتطلب الرجوع لنفس المكان
الحل الأفضل هو فصل المسؤوليات:
public function store(StoreUserRequest $request)
{
$user = $this->userService->create($request->validated());
return new UserResource($user);
}
Controller يصبح نقطة دخول فقط، والمنطق الفعلي يكون في Services أو Actions.
2. إرجاع البيانات مباشرة من المودل
هذا خطأ شائع:
return User::all();
في البداية يبدو بسيط، لكن لاحقًا يسبب مشاكل:
- تسريب بيانات غير مقصودة
- عدم ثبات شكل الـ response
- صعوبة التحكم في البيانات
الحل:
return UserResource::collection(User::paginate());
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
];
}
الفكرة هنا أن شكل البيانات يجب أن يكون ثابت ومستقل عن المودل.
3. تصميم Routes بطريقة غير منظمة
من الأخطاء اللي شفتها كثير:
/getUsers
/createUser
/deleteUser
هذا الأسلوب يسبب فوضى مع الوقت.
الحل:
Route::apiResource('users', UserController::class);
بهذا الشكل يصبح عندك:
- GET /users
- POST /users
- DELETE /users/{id}
تنظيم بسيط لكنه يفرق كثير في المشاريع الكبيرة.
4. كتابة Validation داخل Controller
كتابة التحقق داخل Controller تؤدي إلى تكرار وتعقيد:
$request->validate([...]);
الحل الأفضل:
php artisan make:request StoreUserRequest
public function rules()
{
return [
'email' => 'required|email|unique:users',
];
}
بهذا الشكل يكون التحقق مفصول وقابل لإعادة الاستخدام.
5. تجاهل Pagination
إرجاع كل البيانات مرة واحدة:
return Order::all();
هذا قد يعمل في البداية، لكنه يسبب:
- بطء شديد
- استهلاك عالي للذاكرة
الحل:
return OrderResource::collection(
Order::latest()->paginate(20)
);
أي endpoint يرجع قائمة يجب أن يستخدم pagination.
6. مشكلة N+1 في العلاقات
مثال:
$orders = Order::all();
foreach ($orders as $order) {
echo $order->customer->name;
}
هذا يولد عدد كبير من الاستعلامات.
الحل:
$orders = Order::with('customer')->get();
تقليل عدد الاستعلامات يحسن الأداء بشكل ملحوظ.
7. عدم استخدام Versioning
مع الوقت تحتاج تعدل API، وبدون versioning قد تكسر التطبيقات المرتبطة.
/api/v1/users
/api/v2/users
Route::prefix('v1')->group(function () {
Route::apiResource('users', UserController::class);
});
8. استخدام Status Codes بشكل غير دقيق
مثال:
return response()->json(['message' => 'error']);
هذا لا يوضح نوع الخطأ.
الأفضل:
return response()->json([
'message' => 'User not found'
], 404);
Status code مهم لفهم النظام من جهة frontend.
9. خلط منطق العمل مع عمليات قاعدة البيانات
$user = User::create($data);
if ($user->age > 18) {
// منطق إضافي
}
هذا يجعل الكود مشتت.
الأفضل هو نقل المنطق إلى Service أو طبقة مستقلة.
تصميم API ليس مجرد كتابة endpoints، بل هو بناء نظام قابل للنمو.
- افصل المسؤوليات
- ثبّت شكل البيانات
- انتبه للأداء من البداية
- نظّم الـ routes
المشكلة ليست في الكود… بل في قرارات التصميم التي تتراكم مع الوقت.