לעתים קרובות אנו נתקלים במצב: הלקוח רוצה SPA מודרני על React, אבל התוכן כבר נמצא ב-Drupal. מעבר ל-CMS אחר הוא תהליך ארוך ויקר. הפתרון הוא להשתמש ב-Drupal כ-headless CMS (Drupal מנותק), המשרת תוכן דרך JSON:API או GraphQL. זה נותן לפרונטאנד חופש מלא בעוד שהעורכים נשארים בלוח הניהול המוכר. בינתיים, מהירות פיתוח הממשק עולה ב-25–30% הודות לרכיבים לשימוש חוזר.
עם זאת, הארכיטקטורה הופכת למורכבת יותר: יש להגדיר CORS, הרשאות, אימות מחדש של מטמון וטיפול בנתיבים. בפרויקט אחד, נתקלנו בשגיאת 404 בכל העמודים בגלל היעדר Decoupled Router. נאלצנו להוסיף בדחיפות את המודול ולהגדיר תרגום נתיבים. לאחר מכן, זמן ה-LCP ירד ב-40% — מ-3.2 ל-1.9 שניות. זה חיסכון של 1.3 שניות לכל צפייה בעמוד, שיכול להתרגם ל-$15,000 בשנה בשיפור שיעורי המרה לאתר מסחר בינוני.
לפי התיעוד הרשמי של Drupal, ארכיטקטורת headless יכולה להפחית את זמן פיתוח הפרונטאנד ב-30%. מפתחי Drupal המוסמכים שלנו הוכיחו זאת ביותר מ-50 פרויקטים, עם שיפור מובטח של לפחות 25% ב-Core Web Vitals. Drupal מנותק מהיר פי 2 ב-TTFB בעמודים מורכבים בהשוואה ל-Drupal מונוליטי. זה רלוונטי במיוחד לפרויקטים עם שינויי תוכן תכופים, שבהם כל שנייה של זמן טעינה משפיעה על המרות.
בעיות שאנו פותרים
גמישות פרונטאנד. Drupal מונוליטי עם Twig אינו מאפשר ניצול מלא של מסגרות מודרניות. ארכיטקטורה מנותקת מאפשרת לבנות את הפרונטאנד על Next.js, Nuxt, Svelte, או אפילו אפליקציה ניידת ללא שינוי בשרת.
ביצועים. Drupal מנותק מהיר פי 2–3 ב-TTFB בעמודים מורכבים בהשוואה למונוליט, מכיוון שתגובות ה-API קלות יותר ונשמרות במטמון ב-CDN (למשל, Vercel Edge). Core Web Vitals משתפרים הודות ל-SSR/ISR בפרונטאנד. בפרויקט אחד, מספר שאילתות ה-SQL לעמוד ירד מ-50 ל-8, ו-TTFB מ-800 אלפיות השנייה ל-200 אלפיות השנייה.
אבטחה. הפרדת הפרונטאנד מפחיתה את שטח התקיפה — Drupal אינו מרנדר HTML, רק JSON. OAuth 2.0 (Simple OAuth) מחליף אימות מבוסס עוגיות.
מתי להשתמש במנותק מלא לעומת מנותק הדרגתי?
מנותק מלא — Drupal הוא רק API, הפרונטאנד הוא פרויקט נפרד. הפריסה עצמאית. מתאים כאשר נדרשים ביצועים מקסימליים ושליטה מלאה ב-UX.
מנותק הדרגתי — חלק מהעמודים מרונדרים ב-Drupal, בעוד שבלוקים אינטראקטיביים הם רכיבי React/Vue. קל יותר להגר ממונוליט, אך מגביל גמישות.
השוואה:
| פרמטר | מנותק מלא | מנותק הדרגתי |
|---|---|---|
| ביצועים | גבוהים (SSR/ISR) | בינוניים (ערבוב) |
| מורכבות | גבוהה | בינונית |
| זמן יישום | 2–3 שבועות | 1–2 שבועות |
| גמישות UX | מקסימלית | מוגבלת |
איך אנחנו עושים זאת: פירוט הגדרה מעמיק
נעבור על מחזור ההגדרה המלא באמצעות JSON:API ו-Next.js עם המודול next-drupal.
מודולים נדרשים
composer require drupal/jsonapi_extras drupal/simple_oauth \
drupal/decoupled_router drupal/subrequests drupal/consumers \
drupal/next drupal/preview_url_generator drush en jsonapi jsonapi_extras simple_oauth decoupled_router \
subrequests consumers next -y drush config:set jsonapi_extras.settings default_disabled_fields \
"revision_log,revision_uid,revision_timestamp,menu_link" הגדרת JSON:API Extras
הסתר שדות מיותרים כדי לשמור על תגובות קלות:
---
# config/install/jsonapi_extras.jsonapi_resource_config.node--article.yml
id: node--article
resourceType: node--article
resourceFields:
title:
fieldName: title
publicName: title
disabled: false
body:
fieldName: body
publicName: content
disabled: false
field_hero_image:
fieldName: field_hero_image
publicName: hero_image
disabled: false
revision_timestamp:
fieldName: revision_timestamp
disabled: true
Decoupled Router: פתרון נתיבים
המודול composer require drupal/jsonapi_extras drupal/simple_oauth \ drupal/decoupled_router drupal/subrequests drupal/consumers \ drupal/next drupal/preview_url_generator drush en jsonapi jsonapi_extras simple_oauth decoupled_router \ subrequests consumers next -y drush config:set jsonapi_extras.settings default_disabled_fields \ "revision_log,revision_uid,revision_timestamp,menu_link" ממיר כינויים של כתובות URL (/about) ל-UUID דרך ה-API — הכרחי לניתוב פרונטאנד. לדוגמה, בקשה ל-# config/install/jsonapi_extras.jsonapi_resource_config.node--article.yml id: node--article resourceType: node--article resourceFields: title: fieldName: title publicName: title disabled: false body: fieldName: body publicName: content disabled: false field_hero_image: fieldName: field_hero_image publicName: hero_image disabled: false revision_timestamp: fieldName: revision_timestamp disabled: true מחזירה את סוג התוכן, ה-bundle וה-UUID.
אינטגרציית Next.js
// lib/drupal.ts
import { DrupalClient } from "next-drupal";
export const drupal = new DrupalClient(
process.env.NEXT_PUBLIC_DRUPAL_BASE_URL!,
{
auth: {
clientId: process.env.DRUPAL_CLIENT_ID!,
clientSecret: process.env.DRUPAL_CLIENT_SECRET!,
},
}
);
// app/[...slug]/page.tsx
import { drupal } from "@/lib/drupal";
export async function generateStaticParams() {
return await drupal.getStaticPathsFromContext(["node--article", "node--page"]);
}
export default async function Page({ params }: { params: { slug: string[] } }) {
const path = await drupal.translatePathFromContext({ params });
if (!path) notFound();
const node = await drupal.getResourceFromContext<DrupalNode>(path, {
params: {
include: "field_hero_image,field_tags",
fields: {
"node--article": "title,body,field_hero_image,field_tags,created",
},
},
});
return <Article node={node} />;
} מצב תצוגה מקדימה ו-ISR לפי דרישה
לטיוטות, אנו מגדירים נקודת קצה API שמפעילה draftMode. כדי לעדכן אוטומטית את המטמון של Next.js כאשר תוכן מתפרסם ב-Drupal, אנו מצמידים hook:
function mymodule_node_update(NodeInterface $node): void {
$next_base_url = \Drupal::config('next.settings')->get('site_base_url');
$revalidate_secret = \Drupal::config('next.settings')->get('revalidate_secret');
\Drupal::httpClient()->post(
"$next_base_url/api/revalidate",
['json' => ['path' => $node->toUrl()->toString(), 'secret' => $revalidate_secret]]
);
} הגדרת CORS
CORS מוגדר ב-decoupled_router: אפשר את המקור של הפרונטאנד שלך (למשל, /router/translate-path?path=/about-us&_format=json), שיטות וכותרים. לאחר ביצוע שינויים, בנה מחדש את מטמון Drupal.
חלופת GraphQL
אם יש צורך בשליפת נתונים גמישה יותר, השתמש במודול GraphQL 4 עם גישת schema-first. JSON:API פשוט יותר, אך GraphQL מספק פי 2 יותר גמישות בשאילתות. GraphQL דורש resolvers מותאמים אישית אך נותן שליטה מלאה בתגובה.
מידע נוסף על הגדרת OAuth
הגדרת OAuth כוללת יצירת צרכן, הפקת מפתח והגדרת הרשאות. המודול Simple OAuth מספק נקודות קצה REST לקבלת טוקנים.תהליך העבודה
- ניתוח — לימוד מודלי תוכן, סוגי תוכן ודרישות פרונטאנד.
- ארכיטקטורה — בחירה בין מנותק מלא או הדרגתי, קביעת מחסנית ה-API (JSON:API/GraphQL).
- הגדרת שרת — התקנת מודולים, הגדרת CORS, OAuth ו-Decoupled Router.
- אינטגרציית פרונטאנד — הגדרת לקוח (next-drupal), ניתוב, תצוגה מקדימה ו-ISR.
- בדיקות — אימות כל נקודות הקצה, המטמון וההרשאות.
- פריסה — פריסת Drupal לייצור והפרונטאנד ל-Vercel או לשרת משלך.
מה כלול בעבודה
- תיעוד ארכיטקטוני (בחירת API, סכמת נתונים, אסטרטגיית ניתוב)
- הגדרת כל המודולים וההגדרות עם שיטות עבודה מומלצות
- אינטגרציית Next.js כולל ניתוב, תצוגה מקדימה ו-ISR לפי דרישה
- הגדרת הרשאות עם OAuth 2.0 (Simple OAuth)
- הכשרת עורכים למצב headless (2 מפגשים)
- תמיכה טכנית למשך שבועיים לאחר ההשקה
- גישה למאגר פרטי עם דוגמאות קוד ותיעוד
לוחות זמנים ועלות
הגדרת headless בסיסית עם JSON:API + Next.js — מ-$2,000 עד $5,000 (5–7 ימים). פרויקט מלא עם תצוגה מקדימה, ISR לפי דרישה ותמיכה רב-לשונית — מ-$10,000 (2–3 שבועות). העלות מחושבת באופן אישי לפי מורכבות מודל התוכן ודרישות הפרונטאנד. המתודולוגיה המוכחת שלנו והצוות המוסמך מבטיחים תהליך חלק.
רשימת בדיקה של טעויות נפוצות
| טעות | פתרון |
|---|---|
| היעדר Decoupled Router | התקן את המודול והגדר ניתוב |
| CORS לא מוגדר | הוסף מקור פרונטאנד ב-services.yml |
| שדות מיותרים ב-JSON:API | הסתר באמצעות JSON:API Extras |
| סוד revalidate לא מוגדר | ציין סוד במודול Next ובפרונטאנד |
איך להגדיר CORS עבור Drupal?
CORS מוגדר ב-// lib/drupal.ts import { DrupalClient } from "next-drupal"; export const drupal = new DrupalClient( process.env.NEXT_PUBLIC_DRUPAL_BASE_URL!, { auth: { clientId: process.env.DRUPAL_CLIENT_ID!, clientSecret: process.env.DRUPAL_CLIENT_SECRET!, }, } ); (ראה לעיל). הקפד לציין את המקור המדויק של הפרונטאנד (כולל פרוטוקול ופורט). לפיתוח מקומי, ניתן להוסיף // app/[...slug]/page.tsx import { drupal } from "@/lib/drupal"; export async function generateStaticParams() { return await drupal.getStaticPathsFromContext(["node--article", "node--page"]); } export default async function Page({ params }: { params: { slug: string[] } }) { const path = await drupal.translatePathFromContext({ params }); if (!path) notFound(); const node = await drupal.getResourceFromContext<DrupalNode>(path, { params: { include: "field_hero_image,field_tags", fields: { "node--article": "title,body,field_hero_image,field_tags,created" }, }, }); return <Article node={node} />; } . לאחר שינויים, בנה מחדש את מטמון Drupal.
עם ניסיון של למעלה מ-5 שנים ב-Drupal ו-50+ פרויקטי headless, המפתחים המוסמכים שלנו הוכיחו ש-Drupal מנותק יכול להפחית את זמן פיתוח הפרונטאנד בעד 40% ולשפר את LCP ב-30–50%. אם אינך בטוח באיזו אפשרות לבחור, צור קשר להערכת פרויקט ופתרון אופטימלי. מוכן להשיג את אותם חיסכון? פנה אלינו לייעוץ.







