توثيق الـ API
يُولّد FORGE توثيق API تفاعلي باستخدام مواصفات OpenAPI 3.0. التوثيق يُولّد تلقائياً من تعليقات الـ handler وتعريفات أنواع الـ DTO، مما يضمن بقاء التوثيق متزامناً دائماً مع الكود. المشروع المُولّد يتضمن واجهتي Swagger UI و ReDoc جاهزتين للاستخدام.
نقاط نهاية التوثيق
| نقطة النهاية | الواجهة | الوصف |
|---|---|---|
/docs | Swagger UI | مستكشف API تفاعلي |
/redoc | ReDoc | توثيق نظيف وقابل للقراءة |
/api-docs/openapi.json | JSON خام | ملف مواصفات OpenAPI 3.0 |
افتح Swagger UI في متصفحك على http://localhost:8080/docs بعد تشغيل خادم التطوير.
نصيحة
Swagger UI يسمح لك بتنفيذ استدعاءات API مباشرة من المتصفح. انقر على "Authorize" والصق JWT token الخاص بك لاختبار نقاط النهاية المُصادق عليها بدون مغادرة صفحة التوثيق.
كيف يعمل
يستخدم FORGE مكتبة Utoipa لتوليد مواصفات OpenAPI من كود Rust. التوثيق يُستمد من ثلاثة مصادر:
- تعليقات الـ Handler تُعرّف مسارات نقاط النهاية والطرق والمعاملات والاستجابات
- derives الـ DTO تُعرّف مخططات الطلب/الاستجابة
- tags الوحدات تُنظّم نقاط النهاية في مجموعات منطقية
┌──────────────┐ ┌──────────────┐ ┌────────────────┐
│ #[utoipa:: │ │ ToSchema │ │ ApiDoc │
│ path(...)] │────▶│ derives │────▶│ struct │
│ على handlers│ │ على DTOs │ │ (يجمع │
│ │ │ │ │ كل شيء) │
└──────────────┘ └──────────────┘ └────────────────┘
│
▼
┌────────────────┐
│ OpenAPI JSON │
│ /docs (Swagger)│
│ /redoc │
└────────────────┘تعليق الـ Handlers
أضف attribute الـ #[utoipa::path] لكل handler لإدراجه في التوثيق المُولّد:
use utoipa;
/// List all users with pagination
#[utoipa::path(
get,
path = "/api/admin/users",
tag = "Users",
params(
("page" = Option<i64>, Query, description = "رقم الصفحة"),
("per_page" = Option<i64>, Query, description = "عدد العناصر لكل صفحة"),
("search" = Option<String>, Query, description = "البحث بالاسم أو البريد"),
),
responses(
(status = 200, description = "قائمة مستخدمين مُرقّمة", body = PaginatedResponse<UserResponse>),
(status = 401, description = "غير مُصادق", body = ErrorResponse),
(status = 403, description = "ممنوع", body = ErrorResponse),
),
security(
("bearer_auth" = [])
)
)]
pub async fn list_users(
State(pool): State<PgPool>,
Extension(user): Extension<AuthUser>,
Query(params): Query<PaginatedRequest>,
) -> Result<impl IntoResponse, AppError> {
let users = UserService::list(&pool, ¶ms).await?;
Ok(Json(ApiResponse::success(users)))
}مرجع التعليقات
| Attribute | الغرض | مثال |
|---|---|---|
get/post/put/delete | طريقة HTTP | get |
path | مسار URL | "/api/admin/users/{id}" |
tag | اسم المجموعة في التوثيق | "Users" |
params | معاملات Query/path | ("id" = Uuid, Path, description = "...") |
request_body | مخطط جسم الطلب | body = CreateUserRequest |
responses | استجابات HTTP الممكنة | (status = 200, body = UserResponse) |
security | متطلبات المصادقة | ("bearer_auth" = []) |
اشتقاق المخططات للـ DTOs
جميع أنواع الطلب والاستجابة تشتق ToSchema لتوليد المخطط تلقائياً:
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use validator::Validate;
/// Request to create a new user
#[derive(Deserialize, Validate, ToSchema)]
pub struct CreateUserRequest {
/// User's full name
#[schema(example = "Jane Doe")]
#[validate(length(min = 1, max = 255))]
pub name: String,
/// Email address (must be unique)
#[schema(example = "jane@example.com")]
#[validate(email)]
pub email: String,
/// Password (minimum 8 characters)
#[schema(example = "securepassword123")]
#[validate(length(min = 8))]
pub password: String,
/// Role IDs to assign
#[schema(example = json!(["550e8400-e29b-41d4-a716-446655440000"]))]
pub role_ids: Option<Vec<Uuid>>,
}
/// User response returned from API
#[derive(Serialize, ToSchema)]
pub struct UserResponse {
pub id: Uuid,
pub name: String,
pub email: String,
pub is_active: bool,
pub roles: Vec<RoleResponse>,
pub created_at: chrono::DateTime<chrono::Utc>,
}نصيحة
استخدم #[schema(example = ...)] لتوفير قيم أمثلة ذات معنى. هذه تظهر في ميزة "Try it out" في Swagger UI، مما يُسهّل على مطوري الواجهة الأمامية فهم الصيغ المتوقعة.
تنظيم الـ Tags
نقاط النهاية تُجمّع بـ tags تُقابل الوحدات:
use utoipa::OpenApi;
#[derive(OpenApi)]
#[openapi(
info(
title = "FORGE API",
version = "1.0.0",
description = "Auto-generated API documentation"
),
tags(
(name = "Auth", description = "نقاط نهاية المصادقة"),
(name = "Users", description = "إدارة المستخدمين"),
(name = "Roles", description = "إدارة الأدوار"),
(name = "Permissions", description = "إدارة الصلاحيات"),
(name = "Contents", description = "إدارة المحتوى"),
(name = "Settings", description = "إعدادات التطبيق"),
(name = "Profile", description = "ملف المستخدم الحالي"),
(name = "Media", description = "رفع الملفات والوسائط"),
),
paths(
handlers::auth::login,
handlers::auth::register,
handlers::auth::refresh,
handlers::auth::logout,
handlers::admin::users::list_users,
handlers::admin::users::create_user,
handlers::admin::users::show_user,
handlers::admin::users::update_user,
handlers::admin::users::delete_user,
// ... more handlers
),
components(
schemas(
CreateUserRequest,
UpdateUserRequest,
UserResponse,
LoginRequest,
TokenResponse,
ApiResponse<UserResponse>,
PaginatedResponse<UserResponse>,
ErrorResponse,
// ... more schemas
)
),
modifiers(&SecurityAddon)
)]
pub struct ApiDoc;مخطط الأمان
مصادقة JWT bearer token مُسجّلة كمخطط أمان:
use utoipa::Modify;
use utoipa::openapi::security::{HttpAuthScheme, HttpBuilder, SecurityScheme};
struct SecurityAddon;
impl Modify for SecurityAddon {
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
if let Some(components) = openapi.components.as_mut() {
components.add_security_scheme(
"bearer_auth",
SecurityScheme::Http(
HttpBuilder::new()
.scheme(HttpAuthScheme::Bearer)
.bearer_format("JWT")
.build(),
),
);
}
}
}تقديم التوثيق
مسارات التوثيق تُسجّل في main.rs:
use utoipa::OpenApi;
use utoipa_swagger_ui::SwaggerUi;
use utoipa_redoc::{Redoc, Servable};
let app = Router::new()
// API routes
.nest("/api/admin", routes::admin::routes(pool.clone()))
.nest("/api", routes::public::routes(pool.clone()))
// Documentation
.merge(SwaggerUi::new("/docs").url("/api-docs/openapi.json", ApiDoc::openapi()))
.merge(Redoc::with_url("/redoc", ApiDoc::openapi()));أوامر CLI
توثيق الـ API يُولّد تلقائياً من تعليقات الـ handler الخاصة بك. مواصفات OpenAPI تُقدّم على https://api.<yourapp>.test/docs عند تشغيل خادم التطوير بـ forge serve.
نصيحة
خادم الـ API يُعرّض Swagger UI مدمج على نقطة النهاية /docs. لا يُحتاج لأمر CLI منفصل — فقط شغّل الخادم وانتقل لعنوان التوثيق.
تخصيص التوثيق
إضافة الأوصاف
استخدم تعليقات توثيق Rust على الـ handlers والـ DTOs. تصبح تلقائياً أوصاف OpenAPI:
/// Create a new content entry
///
/// Creates a content entry with translations for all supported languages.
/// The slug must be unique across all content entries.
/// Requires `contents.create` permission.
#[utoipa::path(/* ... */)]
pub async fn create_content(/* ... */) {
// ...
}إخفاء نقاط النهاية الداخلية
احذف نقاط النهاية الداخلية من قائمة paths في struct الـ ApiDoc. فقط الـ handlers المُدرجة في attribute الـ #[openapi(paths(...))] تظهر في التوثيق.
انظر أيضاً
- الـ Handlers — كتابة handlers مع تعليقات التوثيق
- الـ DTOs — أنواع الطلب/الاستجابة مع اشتقاق المخطط
- الـ Routes — تسجيل المسارات لنقاط النهاية المُوثّقة