אפליקציות REST של Bitrix24 עם TypeScript – פתרון מלא
למה TypeScript לאפליקציות REST של Bitrix24?
אין חבילת TypeScript רשמית ל-SDK של BX24. שיטות כמו crm.deal.list, tasks.task.add, im.message.send עובדות כ"קופסה שחורה" – ללא הצעות IDE. מפתחים חייבים לעבור כל הזמן לתיעוד, ובזמן ריצה נתקלים ב-undefined בגלל שדות שגויים. פתרנו את זה ביותר מ-50 פרויקטים: SDK מסוגד של BX24, עטיפות ל-callMethod ו-callBatch, שילוב ווידג'טים של React. אחריות על קוד – שנה אחת.
הקלדה מפחיתה שגיאות פיתוח בחצי בהשוואה ל-JavaScript רגיל. פרויקט TypeScript אחד חוסך עד 40% מזמן הדיבוג והתחזוקה, ומקצץ בעלויות תמיכה עד 30%. מחסנית: TypeScript, React, Node.js, MySQL.
TypeScript מבטל את נקודת הכאב המרכזית: השלמה אוטומטית ב-IDE. אין צורך לזכור שמות שדות – העורך מציע מבנה של BX24Deal, BX24Task או BX24CallResult. חצי מהשגיאות בשלב הפיתוח בהשוואה ל-JavaScript רגיל. אנו משתמשים במצב קפדני עם בדיקת strict: true. זה מבטיח שהקוד לא יישבר בגלל טיפוסים שגויים. בנוסף, אילוצים גנריים והסקת טיפוסים של TypeScript משפרים את אמינות הקוד וקריאותו.
ארכיטקטורה של אפליקציות REST של Bitrix24
שלושה סוגי אפליקציות במערכת האקולוגית של Bitrix24:
-
אפליקציית אינטרנט (iframe) – נטענת בתוך ממשק Bitrix24 ב-iframe. JavaScript/TypeScript עם גישה ל-SDK של
BX24.js. - אפליקציית שרת – PHP/Node.js, עובדת באופן עצמאי, מתקשרת עם Bitrix24 דרך REST על OAuth2.
- ווידג'ט – אפליקציה קומפקטית בסרגל הצד או ב-CRM.
TypeScript ישים בכל שלושת המקרים, אך עם נקודות כניסה שונות.
| סוג | מורכבות פיתוח | הקלדה | ביצועים |
|---|---|---|---|
| אפליקציית אינטרנט (iframe) | בינונית | מלאה (TS בלקוח) | גבוהים (עיבוד מקומי) |
| שרת (Node.js) | גבוהה | מלאה (TS בשרת) | בינוניים (תלוי ב-API) |
| ווידג'ט | נמוכה | חלקית (SDK מוגבל) | גבוהים |
TypeScript עדיף על JavaScript עבור אפליקציות iframe ושרת: בדיקת טיפוסים סטטית מפחיתה באגים ב-30–50%. עבור ווידג'טים, ההקלדה פחות קריטית, אבל אנחנו עדיין מוסיפים טיפוסים בסיסיים.
הקלדת SDK של BX24
אין חבילת TypeScript רשמית ל-SDK של BX24. אנו כותבים הצהרה:
// types/bx24.d.ts
declare global {
const BX24: {
init(callback: () => void): void;
isAdmin(): boolean;
getAuth(): BX24Auth;
refreshAuth(callback: (auth: BX24Auth) => void): void;
callMethod(
method: string,
params?: Record<string, unknown>,
callback?: (result: BX24CallResult) => void
): void;
callBatch(
calls: Record<string, [string, Record<string, unknown>?]>,
callback: (result: Record<string, BX24CallResult>) => void,
bHaltOnError?: boolean
): void;
resizeWindow(width: number, height: number): void;
closeApplication(): void;
placement: {
info(): BX24PlacementInfo;
call(command: string, params?: Record<string, unknown>): void;
};
};
}
interface BX24Auth {
access_token: string;
refresh_token: string;
expires_in: number;
domain: string;
member_id: string;
}
interface BX24CallResult {
status(): number;
data(): unknown;
error(): string | false;
more(): boolean;
next(): void;
total(): number;
}
interface BX24PlacementInfo {
placement: string;
options: Record<string, string>;
}
export {}; טיפוסים לנתוני CRM
// types/crm.ts
export interface BX24Deal {
ID: string;
TITLE: string;
STAGE_ID: string;
OPPORTUNITY: string;
CURRENCY_ID: string;
ASSIGNED_BY_ID: string;
DATE_CREATE: string;
DATE_MODIFY: string;
CONTACT_ID: string | null;
COMPANY_ID: string | null;
COMMENTS: string | null;
UF_CRM_CUSTOM_FIELD?: string;
[key: string]: unknown;
}
export interface BX24Contact {
ID: string;
NAME: string;
LAST_NAME: string;
PHONE: Array<{ VALUE: string; VALUE_TYPE: string }>;
EMAIL: Array<{ VALUE: string; VALUE_TYPE: string }>;
}
export interface BX24Activity {
ID: string;
SUBJECT: string;
OWNER_ID: string;
OWNER_TYPE_ID: string;
CREATED: string;
}
export type StageId =
| 'NEW'
| 'PREPARATION'
| 'PREPAYMENT_INVOICE'
| 'EXECUTING'
| 'FINAL_INVOICE'
| 'WON'
| 'LOSE';
export type AppType = 'iframe' | 'server' | 'widget';
עטיפות API וביצועים
איך להקליד callMethod עם פגינציה אוטומטית?
עטיפה פשוטה עם שליטה מלאה בטיפוסים:
// api/bx24client.ts
export function callMethod<T>(
method: string,
params: Record<string, unknown> = {}
): Promise<T[]> {
return new Promise((resolve, reject) => {
const results: T[] = [];
const handleResult = (
result: ReturnType<
typeof BX24.callMethod extends (...args: unknown[]) => infer R ? R : never
>
) => {
if (result.error()) {
reject(new Error(String(result.error())));
return;
}
const data = result.data() as T[];
results.push(...(Array.isArray(data) ? data : [data as T]));
if (result.more()) {
result.next();
} else {
resolve(results);
}
};
BX24.callMethod(method, params, handleResult);
});
}
// Использование
import type { BX24Deal } from '@/types/crm';
const deals = await callMethod<BX24Deal>('crm.deal.list', {
filter: { STAGE_ID: 'NEW' },
select: ['ID', 'TITLE', 'OPPORTUNITY', 'ASSIGNED_BY_ID'],
order: { DATE_CREATE: 'DESC' },
});
// types/bx24.d.ts declare global { const BX24: { init(callback: () => void): void; isAdmin(): boolean; getAuth(): BX24Auth; refreshAuth(callback: (auth: BX24Auth) => void): void; callMethod( method: string, params?: Record<string, unknown>, callback?: (result: BX24CallResult) => void ): void; callBatch( calls: Record<string, [string, Record<string, unknown>?]>, callback: (result: Record<string, BX24CallResult>) => void, bHaltOnError?: boolean ): void; resizeWindow(width: number, height: number): void; closeApplication(): void; placement: { info(): BX24PlacementInfo; call(command: string, params?: Record<string, unknown>): void; }; }; } interface BX24Auth { access_token: string; refresh_token: string; expires_in: number; domain: string; member_id: string; } interface BX24CallResult { status(): number; data(): unknown; error(): string | false; more(): boolean; next(): void; total(): number; } interface BX24PlacementInfo { placement: string; options: Record<string, string>; } export {}; + // types/crm.ts export interface BX24Deal { ID: string; TITLE: string; STAGE_ID: string; OPPORTUNITY: string; CURRENCY_ID: string; ASSIGNED_BY_ID: string; DATE_CREATE: string; DATE_MODIFY: string; CONTACT_ID: string | null; COMPANY_ID: string | null; COMMENTS: string | null; UF_CRM_CUSTOM_FIELD?: string; [key: string]: unknown; } export interface BX24Contact { ID: string; NAME: string; LAST_NAME: string; PHONE: Array<{ VALUE: string; VALUE_TYPE: string }>; EMAIL: Array<{ VALUE: string; VALUE_TYPE: string }>; } export interface BX24Activity { ID: string; SUBJECT: string; OWNER_ID: string; OWNER_TYPE_ID: string; CREATED: string; } export type StageId = | 'NEW' | 'PREPARATION' | 'PREPAYMENT_INVOICE' | 'EXECUTING' | 'FINAL_INVOICE' | 'WON' | 'LOSE'; export type AppType = 'iframe' | 'server' | 'widget'; – מנגנון פגינציה של SDK של BX24. העטיפה עוברת אוטומטית על כל הדפים ומחזירה את המערך המלא. זה מפחית את העומס על המפתח ומבטיח שלא נפספס נתונים.
שאילתות אצווה לביצועים
כל // api/bx24client.ts export function callMethod<T>( method: string, params: Record<string, unknown> = {} ): Promise<T[]> { return new Promise((resolve, reject) => { const results: T[] = []; const handleResult = (result: ReturnType<typeof BX24.callMethod extends (...args: unknown[]) => infer R ? R : never>) => { if (result.error()) { reject(new Error(String(result.error()))); return; } const data = result.data() as T[]; results.push(...(Array.isArray(data) ? data : [data as T])); if (result.more()) { result.next(); } else { resolve(results); } }; BX24.callMethod(method, params, handleResult); }); } // Использование import type { BX24Deal } from '@/types/crm'; const deals = await callMethod<BX24Deal>('crm.deal.list', { filter: { STAGE_ID: 'NEW' }, select: ['ID', 'TITLE', 'OPPORTUNITY', 'ASSIGNED_BY_ID'], order: { DATE_CREATE: 'DESC' }, }); הוא בקשת HTTP נפרדת. עבור אפליקציות עם עומס API גבוה – השתמשו ב-result.more():
export function callBatch<T extends Record<string, unknown>>(
calls: Record<string, [string, Record<string, unknown>?]>
): Promise<T> {
return new Promise((resolve, reject) => {
BX24.callBatch(calls, (results) => {
const output = {} as T;
let hasError = false;
for (const [key, result] of Object.entries(results)) {
if (result.error()) {
hasError = true;
console.error(`Batch error for "${key}":`, result.error());
} else {
(output as Record<string, unknown>)[key] = result.data();
}
}
if (hasError) reject(new Error('Batch had errors'));
else resolve(output);
});
});
}
// Загрузка сделки со связанными данными за один запрос
const data = await callBatch<{
deal: BX24Deal;
contact: BX24Contact;
history: BX24Activity[];
}>({
deal: ['crm.deal.get', { id: dealId }],
contact: ['crm.contact.get', { id: contactId }],
history: ['crm.activity.list', { filter: { OWNER_ID: dealId, OWNER_TYPE_ID: '2' } }],
});
שאילתות אצווה מפחיתות את זמן טעינת הממשק פי 3–5 בהשוואה לקריאות רציפות. בפרויקט אחד עבור רשת קמעונאית, הפחתנו את זמן פתיחת כרטיס עסקה מ-8 ל-1.5 שניות.
שילוב React ב-iframe של Bitrix24
// main.tsx
import React from 'react';
import { createRoot } from 'react-dom/client';
import { App } from './App';
BX24.init(() => {
const container = document.getElementById('app');
if (!container) return;
const root = createRoot(container);
root.render(<App />);
const resizeObserver = new ResizeObserver(() => {
BX24.resizeWindow(
document.body.scrollWidth,
document.body.scrollHeight
);
});
resizeObserver.observe(document.body);
}); פיתוח ותמיכה
תהליך עבודה
- אנליטיקה – לימוד הלוגיקה העסקית, זיהוי ישויות CRM, קביעת תדירות בקשות API.
- עיצוב – יצירת טיפוסים ל-SDK של BX24 ולכל ישויות CRM, תכנון שאילתות אצווה.
- פיתוח – כתיבת עטיפות callMethod ו-callBatch, רכיבי React, הגדרת אימות OAuth.
- בדיקות – בדיקת טיפוסים, פגינציה, טיפול בשגיאות, ביצועי אצווה.
- פריסה – הצבת האפליקציה ב-Bitrix24, הגדרת הרשאות גישה, תיעוד API.
מה כלול
- הקלדה מלאה של SDK של BX24 וישויות CRM בשימוש.
- עטיפות פגינציה אוטומטיות ל-callMethod.
- אופטימיזציית אצווה לבקשות בעומס גבוה.
- רכיבי React עם עיצוב רספונסיבי ל-iframe.
- תיעוד לאינטגרציה ו-API.
- תמיכה לשנה.
טעויות נפוצות ורשימת בדיקה
טעות נפוצה: שכחת טיפול ב-more() בפגינציה
ללא מעבר אוטומטי בין דפים, תקבלו רק את 50 הרשומות הראשונות. העטיפה שלנו פותרת את זה.- טיפוסים שגויים עבור
result.next(): שדות מוחזרים עשויים להיעדר בקריאה הראשונה – השתמשו ב-callMethod. - אין timeouts עבור
callBatch: ה-API עלול לנתק חיבור בבקשות מרובות – אנו מוסיפים ניסיונות חוזרים. - טיפוסים לא מלאים לשדות משתמש: שדות
export function callBatch<T extends Record<string, unknown>>( calls: Record<string, [string, Record<string, unknown>?]> ): Promise<T> { return new Promise((resolve, reject) => { BX24.callBatch(calls, (results) => { const output = {} as T; let hasError = false; for (const [key, result] of Object.entries(results)) { if (result.error()) { hasError = true; console.error(`Batch error for "${key}":`, result.error()); } else { (output as Record<string, unknown>)[key] = result.data(); } } if (hasError) reject(new Error('Batch had errors')); else resolve(output); }); }); } // Загрузка сделки со связанными данными за один запрос const data = await callBatch<{ deal: BX24Deal; contact: BX24Contact; history: BX24Activity[]; }>({ deal: ['crm.deal.get', { id: dealId }], contact: ['crm.contact.get', { id: contactId }], history: ['crm.activity.list', { filter: { OWNER_ID: dealId, OWNER_TYPE_ID: '2' } }], });דורשים תיאור ידני, אחרת הם נשארים// main.tsx import React from 'react'; import { createRoot } from 'react-dom/client'; import { App } from './App'; BX24.init(() => { const container = document.getElementById('app'); if (!container) return; const root = createRoot(container); root.render(<App />); const resizeObserver = new ResizeObserver(() => { BX24.resizeWindow( document.body.scrollWidth, document.body.scrollHeight ); }); resizeObserver.observe(document.body); });.
לוחות זמנים ועלות
| משימה | לוח זמנים |
|---|---|
| הגדרת TypeScript, טיפוסי SDK של BX24 וישויות CRM | 1–2 ימים |
| אפליקציית iframe פשוטה (צפייה/עריכת נתוני CRM) | 3–5 ימים |
| אפליקציית React מלאה ב-Bitrix24 | 2–4 שבועות |
| אפליקציית שרת Node.js/TypeScript עם OAuth | 1–2 שבועות |
עלות פרויקט טיפוסית נעה בין $2,000 עבור אפליקציות iframe פשוטות ועד $15,000 עבור פתרונות TypeScript מלאים. אנו עובדים עם Bitrix24 REST API כבר למעלה מ-5 שנים. נאמוד את הפרויקט שלכם תוך יומיים. צרו קשר לייעוץ והצעה מסחרית ללא התחייבות. הזמינו פיתוח אפליקציית REST עוד היום.

