التعامل مع API في Laravel – الجزء الثامن: إنشاء API Documentation باستخدام Scribe

بعد الانتهاء من بناء REST API، لا ينتهي العمل عند جعل Endpoints تعمل بشكل صحيح فقط، بل يجب أيضًا توثيقها بطريقة يستطيع مطور Frontend أو تطبيق الهاتف فهمها واستخدامها بسهولة.

عندما يستلم مطور آخر API بدون Documentation، سيحتاج إلى معرفة الكثير من التفاصيل بشكل يدوي، مثل:

  • ما هي Endpoints المتاحة؟
  • ما نوع HTTP Method لكل Endpoint؟
  • ما هي Parameters المطلوبة؟
  • ما الحقول الإجبارية والاختيارية؟
  • ما هي Headers المطلوبة؟
  • هل Endpoint يحتاج Authentication؟
  • ما شكل JSON Response؟
  • ما هي HTTP Status Codes المتوقعة؟
  • كيف يتم رفع الملفات؟

لهذا تعتبر API Documentation جزءًا أساسيًا من أي API احترافي.

توجد عدة أدوات لإنشاء Documentation، وفي هذا الدرس سنستخدم:

Scribe

وهي أداة مخصصة لتوليد API Documentation مباشرة من تطبيق Laravel، ويمكنها قراءة Routes وControllers وForm Requests وValidation Rules وإنشاء Documentation قابلة للقراءة والاستخدام.

جدول المحتويات

  1. ما هي API Documentation؟
  2. لماذا نحتاج إلى توثيق API؟
  3. ما هي Scribe؟
  4. ماذا يمكن أن تنشئ Scribe؟
  5. تثبيت Scribe
  6. نشر ملف الإعدادات
  7. ملف config/scribe.php
  8. اختيار نوع Documentation
  9. تحديد Routes التي سيتم توثيقها
  10. إعداد APP_URL
  11. توليد Documentation
  12. عرض Documentation
  13. تسمية Endpoints
  14. تنظيم Endpoints باستخدام Groups
  15. توثيق Body Parameters
  16. توثيق Query Parameters
  17. توثيق URL Parameters
  18. توثيق Headers
  19. استخراج Parameters من Form Request
  20. توثيق رفع الملفات
  21. توثيق Responses
  22. توثيق Authentication
  23. إنشاء Postman Collection
  24. إنشاء OpenAPI Specification
  25. إضافة Logo ومعلومات المشروع
  26. إعادة توليد Documentation
  27. حماية Documentation في Production
  28. مثال متكامل
  29. أفضل الممارسات
  30. ملخص الدرس
  31. الخلاصة

ما هي API Documentation؟

API Documentation هي مرجع يشرح للمطورين كيفية التعامل مع API.

مثلًا إذا كان لدينا Endpoint:

POST /api/v1/brands

فإن Documentation الجيدة يجب أن توضح:

  • أن Method هي POST.
  • أن Endpoint ينشئ Brand جديدًا.
  • أن الحقل name مطلوب.
  • أن photo اختيارية مثلًا.
  • ما نوع كل حقل.
  • ما Headers المطلوبة.
  • ما شكل Response عند النجاح.
  • ما الأخطاء المحتملة.

بدل أن يحتاج مطور Frontend إلى قراءة Source Code، يستطيع الاعتماد على Documentation.

لماذا نحتاج إلى توثيق API؟

API Documentation مهمة حتى لو كنت أنت من قام ببرمجة Backend وFrontend.

بعد عدة أشهر قد لا تتذكر جميع Endpoints والParameters والتفاصيل الدقيقة.

وتصبح أهم عندما يعمل على المشروع عدة مطورين:

Backend Developer
        |
        v
API Documentation
        |
        +-------- Frontend Developer
        |
        +-------- Android Developer
        |
        +-------- iOS Developer
        |
        +-------- External Integration

وبذلك تصبح Documentation بمثابة Contract واضح بين Backend والأنظمة التي تستخدم API.

ما هي Scribe؟

Scribe هي أداة لإنشاء API Documentation من Laravel Application.

تقوم بقراءة معلومات موجودة بالفعل داخل المشروع مثل:

  • Routes.
  • Controllers.
  • Form Requests.
  • Validation Rules.
  • API Resources.
  • PHP DocBlocks.
  • PHP Attributes.

ثم تستخدم هذه المعلومات لإنشاء Documentation منظمة.

هذا يقلل كمية التوثيق التي يجب كتابتها يدويًا ويحافظ على Documentation أقرب إلى الكود الفعلي.

ماذا يمكن أن تنشئ Scribe؟

Scribe لا تنشئ مجرد قائمة بسيطة بالـRoutes.

يمكنها إنشاء:

  • صفحة HTML Documentation.
  • أمثلة Requests.
  • أمثلة Responses.
  • توثيق Parameters.
  • توثيق Authentication.
  • تقسيم Endpoints إلى Groups.
  • واجهة Try It Out لاختبار بعض Requests من Documentation.
  • Postman Collection.
  • OpenAPI Specification.

وهذا يجعلها مناسبة لإنشاء Documentation يمكن إرسالها مباشرة إلى الفريق الذي سيستهلك API.

تثبيت Scribe

نبدأ بتثبيت Package باستخدام Composer:

composer require knuckleswtf/scribe

بعد انتهاء Composer تصبح Scribe متاحة داخل تطبيق Laravel.

نشر ملف إعدادات Scribe

بعد التثبيت نقوم بنشر Configuration:

php artisan vendor:publish --tag=scribe-config

سيتم إنشاء الملف:

config/scribe.php

وهو الملف الرئيسي الذي نستخدمه للتحكم في طريقة توليد Documentation.

ملف config/scribe.php

يحتوي ملف:

config/scribe.php

على معظم إعدادات Documentation.

من خلاله نستطيع التحكم في أمور مثل:

  • نوع Documentation.
  • Routes التي سيتم تضمينها.
  • اسم API.
  • Base URL.
  • Logo.
  • Authentication.
  • Example Requests.
  • Groups.
  • Response Calls.

اختيار نوع Documentation

من أهم إعدادات Scribe:

'type'

وتوجد طريقتان أساسيتان لتوليد Documentation.

static

'type' => 'static',

في هذا الوضع يتم إنشاء ملفات HTML وAssets ثابتة.

عادةً يتم وضعها داخل:

public/docs

هذا الخيار بسيط جدًا عندما نريد Documentation عامة.

laravel

'type' => 'laravel',

في هذا الوضع يتم تقديم Documentation من داخل Laravel نفسه.

ميزة هذه الطريقة أننا نستطيع تطبيق Middleware على Documentation، وهذا مفيد إذا كانت API Documentation خاصة ولا نريد أن يراها أي شخص.

تحديد Routes التي سيتم توثيقها

من أهم الأمور إخبار Scribe بالـRoutes التي نريد تضمينها.

داخل:

config/scribe.php

سنجد إعدادًا قريبًا من:

'routes' => [
    [
        'match' => [
            'domains' => ['*'],
            'prefixes' => ['api/*'],
        ],

        'include' => [
            //
        ],

        'exclude' => [
            //
        ],
    ],
],

القيمة:

'prefixes' => ['api/*']

تعني أن Scribe ستبحث عن Routes التي تبدأ بـ:

api/

مثل:

/api/v1/brands

/api/v1/products

/api/login

لكن Route مثل:

/admin/dashboard

لن يتم تضمينه وفق هذا الإعداد.

إعداد APP_URL

من المهم أن تكون قيمة:

APP_URL

صحيحة في Environment.

في بيئة التطوير يمكن أن تكون مثلًا:

APP_URL=http://localhost:8000

وفي Production:

APP_URL=https://api.example.com

تستخدم هذه القيمة في عدة أماكن داخل Laravel، ويمكن أن تؤثر على URLs التي تظهر في Documentation حسب Configuration المستخدمة.

توليد API Documentation

بعد ضبط الإعدادات يمكن توليد Documentation باستخدام:

php artisan scribe:generate

ستقوم Scribe عندها بقراءة Routes ومعلومات Endpoints وإنشاء Documentation.

يمكن تنفيذ هذا الأمر مرة أخرى كلما قمنا بتعديل API أو Documentation.

عرض Documentation

طريقة الوصول إلى Documentation تعتمد على قيمة:

type

Static Documentation

إذا كان النوع:

static

يتم إنشاء ملفات Documentation الثابتة داخل:

public/docs

ومن بينها ملف:

index.html

Laravel Documentation

إذا كان النوع:

laravel

يمكن عادةً الوصول إلى Documentation من:

/docs

بحسب إعدادات Scribe.

تسمية Endpoints بطريقة واضحة

Scribe تحاول استخراج معلومات Endpoint تلقائيًا، لكن يمكننا إضافة Description أوضح باستخدام DocBlock فوق دالة Controller.

مثلًا:

/**
 * Get brands.
 *
 * Get a list of all available brands.
 */
public function index()
{
    return BrandResource::collection(
        Brand::query()
            ->latest()
            ->get()
    );
}

السطر الأول:

Get brands.

يستخدم كعنوان مختصر للـEndpoint.

أما النص التالي فيستخدم كشرح أكثر تفصيلًا.

تنظيم Endpoints باستخدام Groups

عندما يحتوي API على عشرات Endpoints، فإن عرضها جميعًا في قائمة واحدة يجعل Documentation صعبة القراءة.

يمكن استخدام:

@group

لتقسيم Endpoints حسب الوظيفة.

مثلًا فوق BrandController:

/**
 * @group Brands
 *
 * APIs for managing brands.
 */
class BrandController extends Controller
{
    //
}

وتصبح Endpoints الخاصة بالـBrands موجودة تحت Group باسم:

Brands

كما يمكن إنشاء Groups أخرى:

Authentication

Users

Products

Orders

Payments

Uploads

وهذا يجعل Documentation أكثر تنظيمًا.

توثيق Body Parameters

لنفترض أن لدينا Endpoint:

POST /api/v1/brands

ويحتاج إلى حقل:

name

يمكن توثيقه باستخدام:

@bodyParam

مثل:

/**
 * Create brand.
 *
 * Create a new brand.
 *
 * @bodyParam name string required The brand name. Example: Apple
 */
public function store(
    BrandStoreRequest $request
) {
    //
}

يتكون تعريف Body Parameter من:

@bodyParam
name
string
required
description
Example

وبهذا يستطيع مطور Frontend معرفة أن:

  • اسم الحقل هو name.
  • نوعه string.
  • الحقل required.
  • يوجد Example للقيمة.

توثيق Query Parameters

إذا كان لدينا Endpoint يدعم Pagination أو Filtering:

GET /api/v1/products?page=2&search=laptop

يمكن استخدام:

@queryParam

مثل:

/**
 * Get products.
 *
 * @queryParam page int The page number. Example: 2
 * @queryParam search string Search by product name. Example: laptop
 */
public function index()
{
    //
}

توثيق URL Parameters

إذا كان لدينا:

GET /api/v1/brands/{brand}

يمكن توضيح Path Parameter باستخدام:

@urlParam

مثلًا:

/**
 * Get brand.
 *
 * @urlParam brand int required The brand ID. Example: 10
 */
public function show(Brand $brand)
{
    return new BrandResource($brand);
}

وبذلك يعرف مستخدم Documentation أن:

{brand}

يجب استبدالها بـBrand ID.

توثيق Headers

بعض Endpoints تحتاج إلى Headers معينة.

مثل:

Accept: application/json

أو:

Accept-Language: ar

أو:

X-API-Key: ...

من المهم أن تظهر هذه المعلومات في Documentation حتى يعرف Client ما يجب إرساله.

استخراج Parameters من Form Request تلقائيًا

من أفضل ميزات Scribe أنها تستطيع الاستفادة من Validation Rules الموجودة داخل Laravel.

لنفترض أن لدينا:

class BrandStoreRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'name' => [
                'required',
                'string',
                'max:255',
            ],

            'photo' => [
                'nullable',
                'image',
                'max:2048',
            ],
        ];
    }
}

Scribe تستطيع استخراج معلومات عن Parameters من Validation Rules في Form Request، لذلك لا نحتاج دائمًا إلى تكرار كل Rule داخل DocBlock.

لكن يمكن إضافة Annotations عندما نريد:

  • Description أفضل.
  • Example محدد.
  • شرح خاص للمطور.
  • توضيح قيم معينة.

توثيق رفع الملفات

في الجزء الثاني من السلسلة تعلمنا رفع الملفات من REST API.

يمكن أيضًا توثيق File Parameter في Scribe.

مثل:

/**
 * Create brand.
 *
 * @bodyParam name string required The brand name. Example: Apple
 * @bodyParam photo file The brand image.
 */
public function store(
    BrandStoreRequest $request
) {
    //
}

نوع:

file

يخبر Scribe أن Parameter عبارة عن ملف.

عند وجود File Parameter تستطيع Scribe التعامل مع Example Request باعتباره:

multipart/form-data

توثيق API Responses

Documentation الجيدة لا تشرح Request فقط.

يجب أيضًا أن توضح Response الذي سيحصل عليه Client.

مثلًا عند طلب:

GET /api/v1/brands/10

يمكن أن تكون الاستجابة:

{
    "data": {
        "id": 10,
        "name": "Apple"
    }
}

Scribe تستطيع إنشاء Sample Responses بعدة طرق، ومنها الاستفادة من API Resources أو تشغيل Response Calls وفق Configuration.

كلما كانت أمثلة Response واقعية، أصبح التعامل مع API أسهل على المطورين الآخرين.

توثيق Authentication

إذا كانت Endpoints محمية، فمن المهم أن يظهر ذلك بوضوح في Documentation.

مثلًا API يستخدم:

Authorization: Bearer TOKEN

أو:

X-API-Key: API_KEY

يجب إعداد Scribe بحيث تعرض Authentication المطلوبة وطريقة إرسال Credentials.

لا تضع Token حقيقية أو API Key حقيقية داخل Documentation.

استخدم Example فقط مثل:

Authorization: Bearer YOUR_ACCESS_TOKEN

إنشاء Postman Collection

ميزة مهمة في Scribe هي إمكانية إنشاء Postman Collection بجانب Documentation.

هذا مفيد جدًا لأن مطور Frontend أو تطبيق الهاتف يستطيع استيراد Collection إلى Postman وتجربة Endpoints مباشرة بدل إنشاء كل Request يدويًا.

يمكن أن تحتوي Collection على:

  • Endpoints.
  • HTTP Methods.
  • Parameters.
  • Headers.
  • Example Requests.

وهذا يجعل تسليم API إلى فريق آخر أسهل بكثير.

إنشاء OpenAPI Specification

Scribe تستطيع أيضًا إنشاء:

OpenAPI Specification

OpenAPI عبارة عن صيغة معيارية لوصف APIs.

وجود OpenAPI Specification يسمح باستخدام Documentation مع عدد كبير من الأدوات الأخرى التي تفهم OpenAPI.

وهذا يجعل Documentation التي يتم إنشاؤها بواسطة Scribe مفيدة خارج واجهة Scribe نفسها.

إعادة توليد Documentation بعد التعديل

عند تعديل Controller أو Route أو Documentation Annotation، يجب إعادة تشغيل:

php artisan scribe:generate

حتى يتم توليد Documentation المحدثة.

يمكن تشغيل الأمر كلما تم تحديث API.

وفي المشاريع الأكبر يمكن إدخال هذه العملية ضمن Deployment أو CI/CD Pipeline إذا كان ذلك مناسبًا لأسلوب العمل.

حماية API Documentation في Production

هناك نقطة مهمة يجب الانتباه إليها:

ليس كل API Documentation يجب أن تكون Public.

إذا كانت Documentation تحتوي على معلومات عن:

  • Internal Endpoints.
  • Administration APIs.
  • Internal Integrations.
  • Sensitive Operations.
  • طرق Authentication الداخلية.

فقد نريد منع الوصول إليها من أي شخص على الإنترنت.

إذا اخترنا Scribe Documentation من نوع:

laravel

فيمكن تقديم Documentation عبر Laravel، وبالتالي يمكن إضافة Middleware مناسب لحمايتها.

أما النوع:

static

فينشئ ملفات ثابتة في Public Directory، ولذلك لا تمر Requests الخاصة بها عبر Laravel Middleware.

هذه نقطة مهمة جدًا عند اختيار نوع Documentation.

مثال متكامل لتوثيق Brands API

لنفترض أن لدينا Controller:

<?php

namespace App\Http\Controllers\Api\V1;

use App\Http\Controllers\Controller;
use App\Http\Requests\BrandStoreRequest;
use App\Http\Resources\BrandResource;
use App\Models\Brand;

/**
 * @group Brands
 *
 * APIs for managing brands.
 */
class BrandController extends Controller
{
    /**
     * Get brands.
     *
     * Get a list of all brands.
     *
     * @queryParam page int The page number. Example: 1
     */
    public function index()
    {
        return BrandResource::collection(
            Brand::query()
                ->latest()
                ->paginate()
        );
    }


    /**
     * Create brand.
     *
     * Create a new brand.
     *
     * @bodyParam name string required The brand name. Example: Apple
     * @bodyParam photo file The brand image.
     */
    public function store(
        BrandStoreRequest $request
    ) {
        $data = $request->validated();

        if ($request->hasFile('photo')) {
            $data['photo'] = $request
                ->file('photo')
                ->store('brands', 'public');
        }

        $brand = Brand::create($data);

        return new BrandResource($brand);
    }


    /**
     * Get brand.
     *
     * Get the details of a specific brand.
     *
     * @urlParam brand int required The brand ID. Example: 10
     */
    public function show(Brand $brand)
    {
        return new BrandResource($brand);
    }


    /**
     * Delete brand.
     *
     * Delete a specific brand.
     *
     * @urlParam brand int required The brand ID. Example: 10
     */
    public function destroy(Brand $brand)
    {
        $brand->delete();

        return response()->noContent();
    }
}

ثم لدينا Route:

Route::prefix('v1')
    ->group(function () {

        Route::apiResource(
            'brands',
            BrandController::class
        );

    });

وفي Scribe Configuration:

'routes' => [
    [
        'match' => [
            'domains' => ['*'],
            'prefixes' => ['api/*'],
        ],

        'include' => [],

        'exclude' => [],
    ],
],

ثم نقوم بتشغيل:

php artisan scribe:generate

فتقوم Scribe بتوليد Documentation تحتوي على Endpoints الخاصة بـBrands، والParameters والأمثلة والمعلومات التي تم استخراجها من التطبيق.

أفضل الممارسات عند إنشاء API Documentation

لا تكتب عنوان Endpoint فقط

اكتب Description توضح ما الذي تقوم به العملية.

أضف Examples واقعية

بدل:

Example: string

استخدم:

Example: Apple

وثق الحقول الاختيارية والإجبارية

حتى يعرف Client ما الذي يجب إرساله.

استخدم Groups

قسم Documentation إلى:

Authentication
Users
Brands
Products
Orders
Payments

بدل وضع كل Endpoints داخل مجموعة واحدة.

استفد من Form Requests

إذا كانت Validation Rules موجودة بالفعل فلا تكرر كل شيء يدويًا بدون حاجة.

وثق Authentication

يجب أن يعرف Client أي Endpoints تحتاج إلى Token وكيف يتم إرسالها.

وثق Error Responses

لا تعرض فقط مثال النجاح.

يجب أن يعرف Client كيف يتعامل مع حالات مثل:

401 Unauthorized

403 Forbidden

404 Not Found

422 Unprocessable Content

429 Too Many Requests

لا تستخدم Credentials حقيقية في الأمثلة

لا تضع:

  • Access Token حقيقية.
  • API Keys حقيقية.
  • Passwords حقيقية.
  • بيانات مستخدم حقيقية.

حدث Documentation مع API

Documentation قديمة أحيانًا أسوأ من عدم وجود Documentation، لأنها تجعل Client يعتمد على معلومات غير صحيحة.

لذلك عند تغيير API يجب تحديث Documentation وإعادة تشغيل:

php artisan scribe:generate

احمِ Documentation الداخلية

إذا كانت Documentation خاصة، لا تنشرها Public بدون Authentication مناسب.

ملخص الدرس

المفهومالوصف
Scribeأداة لتوليد API Documentation من تطبيق Laravel.
scribe:generateالأمر المستخدم لتوليد Documentation.
config/scribe.phpملف إعدادات Scribe الرئيسي.
staticإنشاء Documentation كملفات HTML ثابتة.
laravelتقديم Documentation من خلال Laravel وإمكانية استخدام Middleware.
routes.match.prefixesتحديد Route Prefixes التي ستقوم Scribe بتوثيقها.
@groupتقسيم Endpoints إلى مجموعات.
@bodyParamتوثيق Parameters الموجودة في Request Body.
@queryParamتوثيق Query String Parameters.
@urlParamتوثيق Route Parameters.
Form Requestيمكن لـScribe استخراج معلومات Parameters وValidation منه.
Postman Collectionملف يمكن استيراده إلى Postman لتجربة API.
OpenAPISpecification معيارية لوصف API.

الخلاصة

بعد الانتهاء من تطوير REST API، تعتبر Documentation واحدة من أهم الخطوات قبل تسليم API إلى Frontend Developer أو Mobile Developer أو أي نظام خارجي.

في هذا الدرس استخدمنا:

Scribe

لإنشاء API Documentation مباشرة من مشروع Laravel.

بدأنا بتثبيت Package:

composer require knuckleswtf/scribe

ثم نشر Configuration:

php artisan vendor:publish --tag=scribe-config

وبعد إعداد:

config/scribe.php

حددنا Routes التي نريد توثيقها باستخدام:

'prefixes' => ['api/*']

ثم قمنا بتوليد Documentation بواسطة:

php artisan scribe:generate

كما تعلمنا كيفية جعل Documentation أكثر وضوحًا باستخدام:

@group

@bodyParam

@queryParam

@urlParam

وتعرفنا أيضًا على أن Scribe تستطيع استخراج معلومات من Form Requests، وإنشاء Sample Responses، وPostman Collection، وOpenAPI Specification.

الهدف من Documentation ليس فقط عرض أسماء Endpoints، بل إنشاء مرجع كامل يستطيع أي مطور من خلاله فهم API واستخدامه بدون الحاجة إلى قراءة Source Code أو سؤال Backend Developer عن كل Request.

كلما كانت Documentation واضحة ومحدثة، أصبح تطوير Frontend وتطبيقات الهاتف وعمليات Integration أسرع وأسهل وأقل عرضة للأخطاء.