api-core-runtime
v1.1.0
Published
A lightweight, type-safe API client runtime for modern web applications.
Readme
🚀 Api Core Runtime
مكتبة متكاملة وخفيفة الوزن لإدارة الاتصال بالـ API في تطبيقات الويب الحديثة، مصممة خصيصاً لتوفير تجربة تطوير آمنة (Type-Safe) وسلسة مع دعم كامل لـ React Hooks، إدارة الحالة (State Management)، والتعامل المتقدم مع المصادقة (Authentication).
✨ المميزات الرئيسية
- Type-Safety First: دعم كامل لـ TypeScript لضمان تطابق أنواع المدخلات والمخرجات.
- Modular Architecture: تنظيم الـ API endpoints داخل وحدات (Modules) مستقلة وسهلة الصيانة.
- React Hooks Integration: خطافات جاهزة (
useQuery,useMutation,useInfiniteQuery) تشبه مكتبة React Query ولكن مدمجة مباشرة مع هيكل الـ API الخاص بك. - Advanced Auth Handling: نظام مدمج لإدارة الـ Tokens، التجديد التلقائي للجلسة (Refresh Token)، والتعامل مع حالات عدم المصادقة.
- SSR & Server Support: إمكانية استخدام نفس تعريفات الـ API في بيئة الخادم (Server-Side) بسهولة.
- Smart Caching: نظام تخزين مؤقت (Caching) ذكي وإلغاء تكرار الطلبات (Request Deduplication).
- Infinite Scrolling: دعم مدمج للقوائم اللانهائية (Infinite Queries).
� التثبيت
npm install api-core-runtime axios uuid
# أو
yarn add api-core-runtime axios uuid🛠️ الإعداد الأولي (Setup)
أولاً، قم بإنشاء ملف لإعداد الـ ApiCore (مثلاً src/api/client.ts). هذا هو المكان الذي تضبط فيه الإعدادات العامة مثل الرابط الأساسي والمصادقة.
// src/api/client.ts
import { ApiCore } from 'api-core-runtime';
export const apiCore = new ApiCore({
baseURL: 'https://api.example.com/v1',
// إعدادات المصادقة (اختياري)
auth: {
// دالة لجلب التوكن الحالي (من LocalStorage أو Cookies)
getToken: () => localStorage.getItem('accessToken'),
// مفتاح الهيدر (الافتراضي Authorization)
authHeaderKey: 'Authorization',
// نوع التوكن (الافتراضي Bearer)
authTokenType: 'Bearer',
// دالة تنفذ عند انتهاء صلاحية التوكن (401)
refreshSession: async (error) => {
try {
const response = await fetch('/api/refresh-token'); // مثال
const data = await response.json();
localStorage.setItem('accessToken', data.token);
return true; // تم التجديد بنجاح
} catch (e) {
return false; // فشل التجديد
}
},
// دالة تنفذ عند فشل المصادقة تماماً
onUnauthenticated: () => {
window.location.href = '/login';
}
},
// إعدادات Axios الإضافية
timeout: 30000,
});🏗️ تعريف الوحدات (Modules Definition)
بدلاً من كتابة الروابط بشكل نصي في كل مكان، نستخدم ModuleBuilder لتعريف وحدات مترابطة.
// src/api/modules/users.ts
import { ModuleBuilder, action } from 'api-core-runtime';
import { apiCore } from '../client';
// تعريف أنواع البيانات (Types)
interface UserProfile {
id: string;
name: string;
email: string;
}
interface UpdateUserRequest {
name?: string;
email?: string;
}
// تعريف الوحدة
export const users = new ModuleBuilder(apiCore, {
// 1. تعريف Query (جلب بيانات)
getProfile: {
method: 'GET',
path: '/users/me',
},
// 2. تعريف Mutation (تعديل بيانات)
updateProfile: {
method: 'PUT',
path: '/users/me',
// نحدد الأنواع هنا لضمان الـ Type-Safety
} as import('api-core-runtime').ApiActionConfig<UpdateUserRequest, UserProfile>,
// 3. مثال لـ Infinite Query
getPosts: {
method: 'GET',
path: '/users/posts',
isPublic: true, // لا يرسل التوكن
}
});🚀 الاستخدام (Usage)
1. جلب البيانات (useQuery)
استخدم الدالة المعرفة في الوحدة مباشرة كـ Hook.
import { users } from './api/modules/users';
function UserProfile() {
const { data, isLoading, error } = users.useQuery('getProfile');
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error loading profile</div>;
return (
<div>
<h1>{data?.data.name}</h1>
<p>{data?.data.email}</p>
</div>
);
}2. تعديل البيانات (useMutation)
import { users } from './api/modules/users';
function EditProfile() {
const { mutate, isLoading } = users.useMutation('updateProfile', {
onSuccess: (response) => {
console.log('Updated successfully', response);
},
onError: (err) => {
console.error('Update failed', err);
}
});
const handleSave = () => {
mutate({ name: 'New Name' }); // TypeScript سيتحقق من أن المدخلات صحيحة هنا
};
return (
<button onClick={handleSave} disabled={isLoading}>
Save Changes
</button>
);
}3. القوائم اللانهائية (useInfiniteQuery)
import { users } from './api/modules/users';
function UserPosts() {
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage
} = users.useInfiniteQuery('getPosts', {
getNextPageParam: (lastPage) => lastPage.meta.nextPageCursor, // استخراج مؤشر الصفحة التالية
input: { limit: 10 } // المدخلات الأولية
});
return (
<div>
{data?.pages.map((page, i) => (
<div key={i}>
{page.data.items.map(post => <div key={post.id}>{post.title}</div>)}
</div>
))}
<button
onClick={() => fetchNextPage()}
disabled={!hasNextPage || isFetchingNextPage}
>
{isFetchingNextPage ? 'Loading more...' : 'Load More'}
</button>
</div>
);
}4. الاستخدام في الخادم (Server-Side / SSR)
يمكنك استخدام نفس الوحدات لجلب البيانات مباشرة في بيئة الخادم (مثلاً في Next.js getServerSideProps أو Server Components).
import { users } from './api/modules/users';
// دالة Server Component أو API Route
export async function GET() {
try {
const response = await users.server.fetch('getProfile');
return Response.json(response.data);
} catch (error) {
return Response.error();
}
}🔧 إدارة الـ Cache (QueryClient Helpers)
المكتبة توفر مجموعة من الدوال المساعدة لإدارة الـ cache بسهولة مع type-safety كاملة.
Invalidate Queries
// إبطال query محددة وإعادة تحميلها
await users.invalidate('getProfile');
// إبطال مع input محدد
await users.invalidate('getUserPosts', { userId: '123' });
// خيارات متقدمة
await users.invalidate('getProfile', undefined, {
exact: true, // فقط الـ query المطابق بالضبط
refetchType: 'active' // فقط الـ queries النشطة
});Set/Get Query Data
// تحديث يدوي للـ cache
users.setData('getProfile', {
id: '1',
name: 'Updated Name',
email: '[email protected]'
});
// تحديث باستخدام updater function
users.setData('getProfile', (old) => ({
...old,
name: 'New Name'
}));
// قراءة البيانات من الـ cache
const cachedProfile = users.getData('getProfile');
if (cachedProfile) {
console.log(cachedProfile.name);
}إدارة متقدمة
// إلغاء queries قيد التنفيذ (لمنع race conditions)
await users.cancel('getProfile');
// إعادة تعيين query لحالتها الأولية
await users.reset('getProfile');
// حذف query من الـ cache
users.remove('getProfile');
// التحقق من عدد الـ queries قيد التنفيذ
const fetchingCount = users.isFetching('getProfile');🚨 معالجة الأخطاء المحسنة (Enhanced Error Handling)
المكتبة توفر ApiError class موحد لمعالجة الأخطاء بشكل احترافي.
استخدام ApiError
import { isApiError } from 'api-core-runtime';
function UserProfile() {
const { data, error } = users.useQuery('getProfile');
if (error) {
if (isApiError(error)) {
// الوصول لجميع تفاصيل الخطأ
console.log('Status:', error.status); // 404
console.log('Code:', error.code); // 'ERR_NOT_FOUND'
console.log('Messages:', error.getMessages()); // ['User not found']
// التحقق من status محدد
if (error.isStatus(404)) {
return <NotFoundPage />;
}
// التحقق من multiple statuses
if (error.isStatusIn([401, 403])) {
return <UnauthorizedPage />;
}
// أخطاء validation
const validationErrors = error.getValidationErrors();
if (validationErrors) {
console.log('Field errors:', validationErrors);
// { email: ['Invalid format'], name: ['Required'] }
}
}
}
return <div>{data?.name}</div>;
}معالجة Errors في Mutations
const mutation = users.useMutation('updateProfile', {
onError: (error) => {
if (isApiError(error)) {
if (error.isStatus(422)) {
// عرض أخطاء الـ validation
const validationErrors = error.getValidationErrors();
Object.entries(validationErrors || {}).forEach(([field, messages]) => {
toast.error(`${field}: ${messages.join(', ')}`);
});
} else {
// أخطاء عامة
error.getMessages().forEach(msg => toast.error(msg));
}
}
}
});🛠️ أدوات التطوير (React Query Devtools)
استخدم أدوات التطوير من TanStack Query لمراقبة وتصحيح الـ cache.
التثبيت والتكامل
npm install @tanstack/react-query-devtoolsimport { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { apiCore } from './api/client';
function App() {
return (
<QueryClientProvider client={apiCore.queryClient}>
<YourApp />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
}للمزيد من التفاصيل، راجع:
⚙️ التكوين المتقدم (Configuration)
ApiCoreConfig
| الخاصية | النوع | الوصف |
| :--- | :--- | :--- |
| baseURL | string | الرابط الأساسي للـ API. |
| auth | AuthConfig | كائن إعدادات المصادقة (التوكن، التجديد، إلخ). |
| headers | object | function | هيدرز إضافية ترسل مع كل طلب. |
| timeout | number | مهلة الطلب (بالمللي ثانية). |
| axiosConfig | AxiosRequestConfig | أي إعدادات إضافية خاصة بمكتبة Axios. |
| responseAdapter | function | دالة لتحويل شكل استجابة الباك اند إلى الشكل القياسي StandardApiResponse. |
ApiActionConfig
عند تعريف أي Endpoint، يمكنك تحديد الخصائص التالية:
| الخاصية | النوع | الوصف |
| :--- | :--- | :--- |
| method | 'GET' \| 'POST' ... | نوع الطلب HTTP. |
| path | string | مسار الـ Endpoint. |
| isPublic | boolean | إذا كانت true، لن يتم إرسال توكن المصادقة مع الطلب. |
| contentType | 'json' \| 'formData' | نوع المحتوى المرسل (تلقائياً json). |
📄 الترخيص
هذه المكتبة مرخصة تحت [MIT License].
