📍 المقالة الرئيسية: Directus 2026.
Backend Directus جاهز، Frontend للربط. هذا الدرس يفصل التكامل في Next.js 15 (App Router) و Astro 4.
المتطلبات
Directus في الإنتاج مع collections + بيانات. Next.js 15 أو Astro 4. API key Directus للـ frontend. المستوى: متوسط.
إعداد Next.js 15
التثبيت
npm install @directus/sdk
# للأنواع التلقائية: npx directus-sdk-typegen
عميل Directus
// lib/directus.ts
import { createDirectus, rest, staticToken } from '@directus/sdk';
import type { Schema } from '@/types/directus';
export const directus = createDirectus<Schema>(process.env.NEXT_PUBLIC_DIRECTUS_URL!)
.with(rest())
.with(staticToken(process.env.DIRECTUS_TOKEN!));
Server Component قائمة منتجات
// app/produits/page.tsx
import { directus } from '@/lib/directus';
import { readItems } from '@directus/sdk';
export const revalidate = 60; // ISR 60s
export default async function ProductsPage() {
const products = await directus.request(readItems('products', {
fields: ['*', 'category.name', 'images.*'],
filter: { status: { _eq: 'published' } },
sort: ['-date_created'],
limit: 24,
}));
return (
<div className="grid grid-cols-3 gap-6">
{products.map(p => (
<article key={p.id}>
<img src={`${process.env.NEXT_PUBLIC_DIRECTUS_URL}/assets/${p.images[0]?.id}?width=400`} />
<h3>{p.name}</h3>
<p>{p.price.toLocaleString()} {p.currency}</p>
</article>
))}
</div>
);
}
صفحة منتج ديناميكية
// app/produits/[slug]/page.tsx
export async function generateStaticParams() {
const products = await directus.request(readItems('products', {
fields: ['slug'],
filter: { status: { _eq: 'published' } },
}));
return products.map(p => ({ slug: p.slug }));
}
Webhook ISR Revalidate
// app/api/revalidate/route.ts
import { revalidatePath } from 'next/cache';
export async function POST(req: Request) {
const { searchParams } = new URL(req.url);
if (searchParams.get('secret') !== process.env.REVALIDATE_SECRET) {
return new Response('Unauthorized', { status: 401 });
}
revalidatePath('/produits');
return Response.json({ revalidated: true });
}
إعداد Astro 4
// src/lib/directus.ts
import { createDirectus, rest, staticToken } from '@directus/sdk';
export const directus = createDirectus(import.meta.env.DIRECTUS_URL)
.with(rest())
.with(staticToken(import.meta.env.DIRECTUS_TOKEN));
صفحة قائمة مقالات
---
// src/pages/blog/index.astro
import { directus } from '@/lib/directus';
import { readItems } from '@directus/sdk';
const articles = await directus.request(readItems('articles', {
fields: ['*', 'author.name', 'cover_image.*'],
filter: { status: { _eq: 'published' } },
}));
---
<Layout>
{articles.map(a => (
<article>
<a href={`/blog/${a.slug}`}>{a.title}</a>
</article>
))}
</Layout>
الأداء و caching
CDN Cloudflare في الواجهة
Cache assets Directus (الصور) مع Cache-Control لمدة سنة. API responses cache عبر SWR Next.js.
تحويلات الصور
<img src="https://cms.../assets/IMG_ID?width=400&height=300&fit=cover&format=webp" />
الأخطاء الشائعة
| الخطأ | الحل |
|---|---|
| API 401 | Token غير صحيح |
| Relations فارغة | fields=*,relation.* |
| صور CORS | CORS_ENABLED=true |
| ISR لا يعمل | revalidate مفقود |
التكيف مع السياق
CDN Cloudflare مجاني: أساسي لخدمة صور Directus بزمن استجابة منخفض من داكار/أبيدجان. WebP + AVIF: Directus يولد تلقائياً، توفير 50% حجم. SSG vs SSR: Astro SSG للمدونة + Next.js ISR للتجارة الإلكترونية.
دروس الإخوة
الأسئلة المتكررة
GraphQL أو REST؟ REST أبسط.
SDK إجباري؟ لا، fetch قياسي يعمل أيضاً.