העורך החזותי מרגיש לעיתים קרובות צפוף, ומנהלי תוכן מבקשים ש"נתונים מ-CRM יופיעו אוטומטית באתר"—אז אנחנו משלבים את Webflow CMS. אנחנו רואים את זה באופן קבוע: לקוחות רוצים לנהל בלוגים, קטלוגים של מוצרים או דפי נחיתה דרך עורך חזותי נוח, ובכל זאת לסנכרן תוכן עם מערכת החשבונאות שלהם. הפתרון הוא Webflow API v2 וקוד מותאם אישית מעליו.
Webflow הוא לא רק "בונה אתרים" כמו Tilda; הוא CMS ללא ראש (headless CMS) מלא. דרך REST API ניתן לקרוא ולכתוב נתוני אוספים באופן פרוגרמטי, לחבר webhooks, ולבנות שכבת BFF. להלן המכניקה הספציפית שאנחנו מיישמים בפרויקטים ותשובות לשאלות טכניות נפוצות.
בעיות שאנחנו פותרים עם אינטגרציה
מקורות נתונים מפוזרים. קטלוג המוצרים נמצא ב-ERP, בעוד אתר Webflow מתעדכן ידנית—מה שמוביל לפערים ובזבוז שעות עבור מנהלי תוכן. סנכרון אוטומטי דרך סקריפט פותר את זה: הסקריפט שולף נתונים מ-ERP ומעדכן פריטי אוספים דרך ה-API. שגיאות כתיבה מטופלות עם לוגים, ומגבלות קצב מפוצות עם תור.
פונקציונליות מוגבלת של העורך הסטנדרטי. Webflow מספק רק שדות בסיסיים: טקסט, תמונה, קישור, קישור לאלמנט אחר. כשצריך להציג מבנה מורכב (למשל, בלוקים מקוננים), קל יותר להעביר את העיבוד לפרונטאנד ולהשתמש ב-Webflow כמאגר נתונים. אנחנו עושים את זה לדפים עם רכיבים מותאמים אישית: יוצרים אוסף עם שדה JSON, ו-Next.js מנתח אותו כדי לבנות את ממשק המשתמש.
בעיית ביצועים. כל בקשה ל-Webflow API הולכת לשרתי Webflow—זמן התגובה יכול להיות 200–400 אלפיות השנייה. עם SSR, זה מאריך את ה-TTFB. הפתרון הוא יצירת דפים סטטיים עם ISR (Incremental Static Regeneration) ואימות מחדש המופעל על ידי webhooks. נתונים נשמרים במטמון ב-CDN, ודפים נטענים במהירות. בפרויקט אחד קיצרנו את ה-LCP מ-3.2 שניות ל-0.8 שניות עם גישה זו.
איך לסנכרן נתונים מ-ERP ל-Webflow CMS?
ארכיטקטורה טיפוסית
טכנולוגיות: Next.js 14 (App Router), Webflow API v2, Redis לנעילות. סקריפט הסנכרון רץ לפי לוח זמנים (cron) או ידנית.
רשימת בדיקה לסנכרון:
- קבלת API token מהגדרות האתר → אינטגרציות - יצירת אוסף ב-Webflow Designer עם השדות הנדרשים - הגדרת סקריפט עם לוגיקת upsert ועמודים (pagination) - טיפול במגבלות קצב (60 בקשות לדקה) - הטמעת לוגים לשגיאות וניסיונות חוזרים// scripts/sync-products.ts
import { createCollectionItem, updateCollectionItem, getCollectionItems } from '@/lib/webflow'
async function syncProducts(erpProducts: ERPProduct[]) {
const { items: existing } = await getCollectionItems(PRODUCTS_COLLECTION_ID)
const existingMap = new Map(existing.map(i => [i.fieldData['sku'], i.id]))
for (const product of erpProducts) {
const fieldData = {
name: product.name,
slug: product.sku.toLowerCase(),
'product-sku': product.sku,
'price': product.price,
'in-stock': product.stock > 0,
'description': product.description,
}
if (existingMap.has(product.sku)) {
await updateCollectionItem(PRODUCTS_COLLECTION_ID, existingMap.get(product.sku)!, fieldData)
} else {
await createCollectionItem(PRODUCTS_COLLECTION_ID, fieldData, { live: true })
}
// API rate limit: 60 req/min
await new Promise(r => setTimeout(r, 1100))
}
} טיפול בשגיאות וטרנזקציוניות
ה-API לא תומך בטרנזקציות—אם אחרי 50 מתוך 100 פריטים הסקריפט קורס, השאר לא מתעדכנים. הפתרון: שמירת מצב ב-Redis, ובהפעלה מחדש, המשך מהנקודה שבה הפסיק. עבור נתונים קריטיים, אנחנו מוסיפים תור עם Dead Letter Queue והתראות Telegram. עם 5 שנות הניסיון שלנו ב-Webflow, אנחנו מבטיחים שהסנכרון רץ ללא אובדן נתונים.
API v2: אימות ובקשות בסיסיות
אימות דרך OAuth2 (לאפליקציות) או Site API Token (לאינטגרציות). דוגמה לפונקציית fetch:
// lib/webflow.ts
const WEBFLOW_API_TOKEN = process.env.WEBFLOW_API_TOKEN!
const SITE_ID = process.env.WEBFLOW_SITE_ID!
const BASE_URL = 'https://api.webflow.com/v2'
async function webflowFetch<T>(
path: string,
options: RequestInit = {}
): Promise<T> {
const res = await fetch(`${BASE_URL}${path}`, {
...options,
headers: {
Authorization: `Bearer ${WEBFLOW_API_TOKEN}`,
'Content-Type': 'application/json',
...options.headers,
},
next: { tags: ['webflow'] },
})
if (!res.ok) {
const err = await res.json()
throw new Error(`Webflow API error: ${err.message}`)
}
return res.json()
}
export async function getCollections() {
return webflowFetch<{ collections: WebflowCollection[] }>(
`/sites/${SITE_ID}/collections`
)
}
export async function getCollectionItems(
collectionId: string,
params: { limit?: number; offset?: number; live?: boolean } = {}
) {
const query = new URLSearchParams({
limit: String(params.limit ?? 100),
offset: String(params.offset ?? 0),
...(params.live ? { live: 'true' } : {}),
})
return webflowFetch<{ items: WebflowItem[]; pagination: WebflowPagination }>(
`/collections/${collectionId}/items?${query}`
)
}
סוגי נתונים ושדות
Webflow CMS מאחסן נתונים ב-// scripts/sync-products.ts import { createCollectionItem, updateCollectionItem, getCollectionItems } from '@/lib/webflow' async function syncProducts(erpProducts: ERPProduct[]) { const { items: existing } = await getCollectionItems(PRODUCTS_COLLECTION_ID) const existingMap = new Map(existing.map(i => [i.fieldData['sku'], i.id])) for (const product of erpProducts) { const fieldData = { name: product.name, slug: product.sku.toLowerCase(), 'product-sku': product.sku, 'price': product.price, 'in-stock': product.stock > 0, 'description': product.description, } if (existingMap.has(product.sku)) { await updateCollectionItem(PRODUCTS_COLLECTION_ID, existingMap.get(product.sku)!, fieldData) } else { await createCollectionItem(PRODUCTS_COLLECTION_ID, fieldData, { live: true }) } // API rate limit: 60 req/min await new Promise(r => setTimeout(r, 1100)) } } . שדות מוגדרים בעורך; כל אחד מקבל slug. לדוגמה, פוסט בלוג מכיל שדות: // lib/webflow.ts const WEBFLOW_API_TOKEN = process.env.WEBFLOW_API_TOKEN! const SITE_ID = process.env.WEBFLOW_SITE_ID! const BASE_URL = 'https://api.webflow.com/v2' async function webflowFetch<T>( path: string, options: RequestInit = {} ): Promise<T> { const res = await fetch(`${BASE_URL}${path}`, { ...options, headers: { Authorization: `Bearer ${WEBFLOW_API_TOKEN}`, 'Content-Type': 'application/json', ...options.headers, }, next: { tags: ['webflow'] }, }) if (!res.ok) { const err = await res.json() throw new Error(`Webflow API error: ${err.message}`) } return res.json() } export async function getCollections() { return webflowFetch<{ collections: WebflowCollection[] }>( `/sites/${SITE_ID}/collections` ) } export async function getCollectionItems( collectionId: string, params: { limit?: number; offset?: number; live?: boolean } = {} ) { const query = new URLSearchParams({ limit: String(params.limit ?? 100), offset: String(params.offset ?? 0), ...(params.live ? { live: 'true' } : {}), }) return webflowFetch<{ items: WebflowItem[]; pagination: WebflowPagination }>( `/collections/${collectionId}/items?${query}` ) } , fieldData, name (טקסט עשיר → HTML), slug, post-body, main-image, author. מזהים מוקצים אוטומטית.
למה להשתמש ב-Webflow כ-CMS ללא ראש?
Webflow נותן למנהלי תוכן עורך חזותי ידידותי, בעוד הפרונטאנד מקבל נתונים דרך API. בשילוב עם Next.js, זה עובד כך:
// app/blog/[slug]/page.tsx
import { getCollectionItems } from '@/lib/webflow'
const BLOG_COLLECTION_ID = process.env.WEBFLOW_BLOG_COLLECTION_ID!
export async function generateStaticParams() {
const { items } = await getCollectionItems(BLOG_COLLECTION_ID)
return items
.filter(item => !item.isDraft && !item.isArchived)
.map(item => ({ slug: item.fieldData.slug }))
}
export default async function PostPage({ params }: { params: { slug: string } }) {
const { items } = await getCollectionItems(BLOG_COLLECTION_ID)
const post = items.find(i => i.fieldData.slug === params.slug)
if (!post) notFound()
return (
<article>
<h1>{post.fieldData.name}</h1>
<div className="prose" dangerouslySetInnerHTML={{ __html: post.fieldData['post-body'] }} />
</article>
)
}
export const revalidate = 3600
ISR עם טיימר או אימות מחדש המופעל על ידי webhooks נותן מהירות סטטית ונתונים עדכניים, מקצר את ה-TTFB ומשפר את Core Web Vitals.
איך להגדיר Webhooks לעדכונים בזמן אמת?
טופס יצירת קשר: יצירת פריט אוסף
export async function createCollectionItem(
collectionId: string,
fieldData: Record<string, unknown>,
options: { live?: boolean } = {}
) {
return webflowFetch(`/collections/${collectionId}/items`, {
method: 'POST',
body: JSON.stringify({
isArchived: false,
isDraft: !options.live,
fieldData,
}),
})
}
// Использование
await createCollectionItem(
LEADS_COLLECTION_ID,
{
name: formData.name,
email: formData.email,
message: formData.message,
source: 'contact-form',
},
{ live: false }
) Webhooks לאימות מטמון
// app/api/webflow-webhook/route.ts
import { revalidateTag } from 'next/cache'
import crypto from 'crypto'
export async function POST(request: Request) {
const signature = request.headers.get('x-webflow-signature')
const body = await request.text()
// Верификация подписи
const expected = crypto
.createHmac('sha256', process.env.WEBFLOW_WEBHOOK_SECRET!)
.update(body)
.digest('hex')
if (signature !== expected) {
return new Response('Unauthorized', { status: 401 })
}
const payload = JSON.parse(body)
if (payload.triggerType.startsWith('collection_item')) {
revalidateTag('webflow')
}
return new Response('OK')
} השוואת Webflow API מול Tilda
| פרמטר | Webflow חינמי | Webflow Basic | Webflow Business | Tilda (להשוואה) |
|---|---|---|---|---|
| פריטים לכל אוסף | 2000 | 5000 | 20,000 | 5000 (סה"כ) |
| מגבלת קצב (בקשות לדקה) | 60 | 60 | 60 | 30 |
| Webhooks | כן | כן | כן | לא |
| גישה ל-API | כן | כן | כן | מוגבלת |
קחו בחשבון את ההבדל: עם Tilda בילינו 3 ימים בסנכרון קטלוג; עם Webflow זה לקח 5 שעות. זה חוסך 80% זמן ומפחית עלויות תחזוקה בעד 40%. Webflow API מאפשר 60 בקשות לדקה, פי שניים מהקצב של Tilda. 10 שנות הניסיון שלנו בפיתוח אתרים ויותר מ-50 פרויקטי אינטגרציה עם CMS מבטיחים תוצאות איכותיות.
מה כלול ולוחות זמנים
- הגדרת אוספים ושדות ב-CMS שיתאימו למבנה הנתונים שלכם.
- פיתוח ספרייה ל-Webflow API עם טיפול בשגיאות ועמידה במגבלות קצב.
- סנכרון עם מערכות חיצוניות: ERP, CRM או מקורות אחרים.
- אינטגרציית פרונטאנד (Next.js, Nuxt, Vue, React) עם ISR ואימות מחדש.
- תיעוד והדרכת צוות.
לוחות זמנים: קריאת נתונים מ-Webflow ל-Next.js—2–3 ימים; סנכרון דו-כיווני עם מערכת חיצונית—5–7 ימים; פרויקט מלא—החל מ-10 ימים. צרו קשר להערכת הפרויקט שלכם—נשיב תוך יום עסקים אחד. הזמינו אינטגרציה, ואנחנו נגדיר את Webflow לצרכים שלכם.







