bx-rest
v1.1.0
Published
Bitrix24 REST API client and TypeScript SDK for Angular, Vue and browser apps
Downloads
1,804
Maintainers
Readme
Русский | English
bx-rest — REST API клиент Bitrix24 для TypeScript, Angular, Vue и React
bx-rest — TypeScript SDK для работы с REST API Bitrix24. Предоставляет типизированный интерфейс для вызова REST-методов, управления авторизацией и токенами (sessid, OAuth 2.0), обработки пагинации и преобразования данных с помощью мапперов.
Содержание
- Установка
- Требования
- Быстрый старт
- Аутентификация и OAuth2
- Обработка ошибок
- Использование в Angular
- SessionKeyServices
- Использование в Vue
- Использование в React
- Демо
- Кастомные REST-методы
- Специфичные методы
- Парсинг дат
- Планы на будущее
- Лицензия
Установка
npm install bx-restТребования
Формат модулей:
bx-restпоставляется только в формате ES Modules. Сборка CommonJS и поддержкаrequire()не предусмотрены. Используйте пакет через ESM-совместимый бандлер (Angular CLI, Vite, webpack 5+, Next.js и др.) или нативныйimport().TypeScript: требуется TypeScript 5.5 или выше. Рекомендуется включить режим
strict, поскольку типизация SDK рассчитана на работу со строгой проверкой типов.RxJS:
rxjsверсии^7.8является обычной зависимостью пакета и поставляется вместе сbx-rest, а не объявляется какpeerDependency. Поэтому установка отдельной версии RxJS на стороне приложения не требуется. Все REST-запросы возвращаютObservable.Фреймворки:
bx-restне зависит от конкретного frontend-фреймворка и не объявляет Angular, Vue или React в качествеpeerDependencies. Примеры интеграции рассчитаны на Angular 18/19, Vue 3 и React 18+ — эти версии используются при сборке и тестировании библиотеки.Окружение:
bx-restориентирован преимущественно на браузерное окружение. Источники авторизацииcookies,localStorage,sessidиSessionKeyServicesиспользуют браузерные API (window,document,localStorage). При использовании SSR (Angular Universal, Next.js и др.) такой код должен выполняться только на стороне клиента. Например, можно использовать проверкиisPlatformBrowser/typeof window !== 'undefined'либо источник авторизации, не зависящий от браузерных глобальных объектов:source: () => accessTokenили
source: 'off'.
Быстрый старт
Один раз настройте URL портала и источник access-токена, затем создайте клиент и подпишитесь на возвращённый RxJS Observable. Указывайте полный URL, включая https://.
import { firstValueFrom } from 'rxjs'
import { BXRestNavvy, BXRestSettings } from 'bx-rest'
const accessToken = 'YOUR_ACCESS_TOKEN'
BXRestSettings.update({
auth: {
source: () => accessToken,
key: 'auth'
},
urls: {
source: 'string',
key: 'https://example.bitrix24.com',
additional_part: 'rest'
}
})
const bxRest = new BXRestNavvy()
const deals = await firstValueFrom(
bxRest.crm.deal.list({
select: ['ID', 'TITLE'],
filter: {},
start: 0
}).res()
)
console.log(deals)Не хардкодьте и не коммитьте боевой access-токен. Передавайте его из потока авторизации вашего приложения, ответа сервера или защищённой конфигурации времени выполнения.
Режимы результата
REST-вызовы ленивые: запрос начинается, когда на возвращённый Observable подписываются или передают его в firstValueFrom.
| Метод | Результат |
| --- | --- |
| .res(options?) | Разворачивает ответ Bitrix и применяет настроенный маппер. Для пагинированных методов возвращает текущую страницу. |
| .resAll() | Загружает и объединяет все страницы. Доступен для пагинированных Navvy-хелперов. |
| .resVanilla() | Возвращает сырой ответ Bitrix, включая такие поля, как result, total и next. |
| .mapForVanilla() | Сохраняет исходную обёртку ответа, но применяет маппер к её result. |
.res() принимает необязательный объект опций:
| Опция | По умолчанию | Эффект |
| --- | --- | --- |
| throwOnApiError | false | При true ошибка API-уровня Bitrix приводит к тому, что возвращённый Observable завершается ошибкой BXRestApiError вместо резолва в undefined. См. Обработка ошибок. |
Аутентификация и OAuth2
auth.source определяет, откуда читаются учётные данные. auth.key определяет имя параметра запроса, за исключением специального режима учётных данных через куки OAuth2.
| Сценарий | auth.source | auth.key | Поведение |
| --- | --- | --- | --- |
| Access-токен из состояния приложения | () => accessToken | auth | Вызывает функцию перед каждым запросом и отправляет её значение в параметре auth. Рекомендуется для OAuth2 access-токенов, которыми управляет приложение. |
| Access-токен в local storage | localStorage | auth | Читает localStorage["auth"] и отправляет его как auth. Значение полностью доступно любому JS на странице (в том числе через XSS) — годится для локальной разработки/прототипирования, для продакшена предпочтительнее источник-функция, управляемый приложением. |
| Access-токен в куке | cookies | auth | Читает куку auth через document.cookie и отправляет её как auth. Куку читает сам JS-код, поэтому она не может быть HttpOnly — с точки зрения защиты от XSS это не безопаснее localStorage. Как и предыдущий вариант, в первую очередь удобен для локальной разработки. |
| Встроенная страница Bitrix / локальная сессия | cookies | sessid | Пробует window.BX.bitrix_sessid(), query-параметр sessid, localStorage.sessid, затем куку auth. Отправляет полученное значение как sessid. |
| Входящий вебхук, URL с авторизацией или собственный прокси | off | Любое значение | Пропускает поиск учётных данных и не добавляет параметр авторизации — используется и для вебхука (секрет уже в URL), и для запроса через собственный бэкенд-прокси (секрет остаётся на сервере, браузер токен не видит вообще). Для второго случая, если прокси авторизует браузер собственной cookie-сессией, см. настройку withCredentials ниже. |
| Запрос учётных данных через куки | Любой источник, возвращающий непустое значение | OAuth2 | Не добавляет полученное значение в параметры запроса и по умолчанию включает withCredentials в Axios (если не переопределено настройкой withCredentials, см. ниже). Используйте только если портал и политика CORS поддерживают куки с учётными данными — учтите также, что современные браузеры всё активнее блокируют сторонние (third-party) куки по умолчанию. |
Для обычного OAuth2 access_token Bitrix используйте auth.key: 'auth' и передавайте текущий токен через функцию или localStorage. Специальный режим auth.key: 'OAuth2' предназначен для куки с учётными данными; сам по себе он не отправляет OAuth access-токен.
withCredentials
По умолчанию axios-опция withCredentials (прикладывать ли браузерные куки к запросу) включается только при auth.key: 'OAuth2'. Если нужен другой случай — например, auth.source: 'off' указывает на собственный прокси, а браузер должен приложить куку сессии этого прокси (не Bitrix24) — задайте withCredentials явно в настройках, независимо от auth.key:
BXRestSettings.update({
auth: {
source: 'off',
key: 'auth'
},
urls: {
source: 'string',
key: 'https://your-proxy.example.com/bitrix-proxy',
additional_part: ''
},
withCredentials: true
})Явное значение всегда имеет приоритет над правилом «включать только для OAuth2» — можно и принудительно выключить withCredentials при auth.key: 'OAuth2', если он не нужен.
Для URL входящего вебхука сохраните полный путь вебхука в urls.key, установите additional_part в пустую строку и отключите дополнительную авторизацию:
BXRestSettings.update({
auth: {
source: 'off',
key: 'auth'
},
urls: {
source: 'string',
key: 'https://example.bitrix24.com/rest/1/WEBHOOK_CODE',
additional_part: ''
}
})Обработка ошибок
bx-rest показывает два разных вида сбоев, и .res() реагирует только на один из них.
| Сбой | Когда происходит | Как проявляется в .res() |
| --- | --- | --- |
| Ошибки транспорта / конфигурации — сбой сети, отсутствие авторизации, отсутствие базового URL | До или во время HTTP-вызова | Возвращённый Observable завершается ошибкой. Обрабатывайте её через RxJS catchError или колбэк error у subscribe(). |
| Ошибки API Bitrix — { error, error_description }, например INSUFFICIENT_SCOPE, expired_token | HTTP-ответ 200, тело которого не является успешным payload'ом | По умолчанию .res() резолвится обычным образом со значением undefined — исключение не выбрасывается. Передайте { throwOnApiError: true }, чтобы вместо этого выбрасывалось BXRestApiError. |
Поскольку ошибка уровня Bitrix по умолчанию резолвится как undefined, а не как ошибка Observable, не полагайтесь только на голый .res(), чтобы отличить «нет записей» от «запрос не удался». Есть два варианта.
Вариант 1 — включить выброс исключения через .res({ throwOnApiError: true }). Observable завершается ошибкой BXRestApiError, предоставляющей error и error_description, поэтому её можно обработать вместе с транспортными ошибками в одном колбэке catchError/error:
import { BXRestNavvy, BXRestApiError } from 'bx-rest'
const bxRest = new BXRestNavvy()
bxRest.crm.deal.list({
select: ['ID', 'TITLE'],
filter: {},
start: 0
}).res({ throwOnApiError: true }).subscribe({
next: deals => console.log(deals),
error: err => {
if (err instanceof BXRestApiError) {
console.error('Bitrix API error:', err.error, err.error_description)
} else {
console.error('Request failed:', err.message)
}
}
})Вариант 2 — оставить поведение по умолчанию и проверять сырой ответ. Вызовите .resVanilla() и проверьте ответ с помощью экспортируемого type guard isBXRestAnswerSuccess():
import { firstValueFrom } from 'rxjs'
import { BXRestNavvy, isBXRestAnswerSuccess } from 'bx-rest'
const bxRest = new BXRestNavvy()
const answer = await firstValueFrom(
bxRest.crm.deal.list({
select: ['ID', 'TITLE'],
filter: {},
start: 0
}).resVanilla()
)
if (!answer || !isBXRestAnswerSuccess(answer)) {
console.error('Bitrix API error:', answer?.error, answer?.error_description)
} else {
console.log(answer.result)
}Независимо от throwOnApiError, ошибки транспортного уровня всегда всплывают как ошибки Observable и могут быть перехвачены обычным способом RxJS:
bxRest.crm.deal.list({
select: ['ID', 'TITLE'],
filter: {},
start: 0
}).res().subscribe({
next: deals => console.log(deals),
error: err => console.error('Request failed:', err.message)
})Использование в Angular
Зарегистрируйте публичные клиенты и Navvy, когда нужно внедрять их в сервисы или компоненты Angular:
import { ApplicationConfig, Provider } from '@angular/core'
import {
BXRestMap,
BXRestNavvy,
BXRestRequest,
Navvy
} from 'bx-rest'
export const BXRestNavvyProvider: Provider = {
provide: BXRestNavvy,
useFactory: () => new BXRestNavvy()
}
export const BXRestMapProvider: Provider = {
provide: BXRestMap,
useFactory: () => new BXRestMap()
}
export const BXRestRequestProvider: Provider = {
provide: BXRestRequest,
useFactory: () => new BXRestRequest()
}
export const NavvyProvider: Provider = {
provide: Navvy,
useFactory: () => new Navvy()
}
export const appConfig: ApplicationConfig = {
providers: [
BXRestNavvyProvider,
BXRestMapProvider,
BXRestRequestProvider,
NavvyProvider
]
}Пример компонента:
import { Component, inject } from '@angular/core'
import { BXRestNavvy } from 'bx-rest'
@Component({
selector: 'app-list',
template: ''
})
export class ListComponent {
private readonly bxRest = inject(BXRestNavvy)
readonly listElements$ = this.bxRest.lists.element.get({
IBLOCK_TYPE_ID: 'lists',
IBLOCK_ID: 150,
FILTER: {
'>ID': 0
}
}).res()
}SessionKeyServices
SessionKeyServices доступен из публичного API и может использоваться, когда нужно проверить текущие данные авторизации перед выполнением REST-запросов.
import { BXRestSettings, SessionKeyServices, SessionKeyError } from 'bx-rest'
BXRestSettings.update({
auth: {
source: 'cookies',
key: 'auth'
},
urls: {
source: 'string',
key: 'https://example.bitrix24.com',
additional_part: 'rest'
}
})
const session = new SessionKeyServices()
const auth = session.getAuthParams()
const authKey = session.getKeyAuth()
if (!auth && session.getCheckAuthParamsIsOn()) {
const error = session.getAuthError('manual auth check')
if (error instanceof SessionKeyError) {
console.error(error.message)
}
throw error
}
session.getBaseUrl().subscribe((baseUrl) => {
if (!baseUrl) {
throw session.getBaseUrlError('manual base url check')
}
console.log({ authKey, auth, baseUrl })
})Использование в Vue
import type { Plugin } from 'vue'
import { BXRestSettings, BXRestMap, BXRestNavvy, BXRestRequest } from 'bx-rest'
const bxRestPlugin: Plugin = {
install(app) {
BXRestSettings.update({
auth: {
source: 'cookies',
key: 'auth'
},
urls: {
source: 'string',
key: 'https://example.bitrix24.com',
additional_part: 'rest'
}
})
app.config.globalProperties.$bxRestNavvy = new BXRestNavvy()
app.config.globalProperties.$bxRestMap = new BXRestMap()
app.config.globalProperties.$bxRestRequest = new BXRestRequest()
}
}
export default bxRestPluginimport './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
import bxRestPlugin from './bxRestPlugin'
const app = createApp(App)
app.use(bxRestPlugin)
app.mount('#app')Использование в React
Создавайте клиент вне компонента и отписывайтесь при размонтировании компонента:
import { useEffect, useState } from 'react'
import { BXRestNavvy } from 'bx-rest'
const bxRest = new BXRestNavvy()
export function Deals() {
const [deals, setDeals] = useState<unknown[]>([])
useEffect(() => {
const subscription = bxRest.crm.deal.list({
select: ['ID', 'TITLE'],
filter: {},
start: 0
}).res().subscribe({
next: value => setDeals(value ?? []),
error: error => console.error(error)
})
return () => subscription.unsubscribe()
}, [])
return <pre>{JSON.stringify(deals, null, 2)}</pre>
}Демо
В projects/ есть три независимых демо-приложения — Angular, Vue и React
(demo-angular, demo-vue, demo-react). Каждое: форма подключения,
покрывающая все варианты из раздела «Аутентификация и OAuth2» выше (а не
только access-токен и вебхук), редактор кода на Monaco с
автодополнением по типам bx-rest (а не текстовое поле или конструктор
запроса) и панель результата, которая реально выполняет написанный код
против вашего портала. Интерфейс переключается между русским и английским
автоматически по языку браузера/ОС. Общая логика (типы для Monaco,
выполнение кода, готовые примеры, i18n) вынесена в projects/demo-shared —
разница между тремя демо в первую очередь в способе интеграции (DI-провайдер
в Angular, плагин в Vue, синглтон+хук в React — см. разделы выше) и, для
Angular, в UI-библиотеке (Angular Material — идиоматичный выбор для этого
фреймворка).
Запуск из корня репозитория:
npm run demo:bootstrapДальше в любой из projects/demo-angular|demo-vue|demo-react: npm run dev
(для Angular — npm start). Подробности — в README каждого демо.
Кастомные REST-методы
Navvy экспортируется из bx-rest. В Angular зарегистрируйте показанный выше NavvyProvider и внедрите Navvy в ваш кастомный API-сервис. URL метода представлен массивом и перед отправкой запроса соединяется точками.
Для регистрации кастомных REST-методов см. документацию Bitrix24.
Mapper
Маппер — это обычная функция. Ей не нужно наследоваться от BXRestMap.
import {
iBXRestCustomBlogGet,
iBXRestCustomBlogGetHttp
} from '../type/blog'
export class BXCustomBlogMap {
static get(
value: iBXRestCustomBlogGetHttp[] | undefined
): iBXRestCustomBlogGet[] {
return (value ?? []).map(item => ({
id: Number(item.ID),
title: item.TITLE
}))
}
}Кастомный метод с simple()
Используйте simple(), когда метод возвращает один ответ без автоматической пагинации:
import { inject, Injectable } from '@angular/core'
import { Navvy, partNameMethods as PNM } from 'bx-rest'
import { BXCustomBlogMap } from '../map/blog'
import {
iBXRestCustomBlogGet,
iBXRestCustomBlogGetHttp,
iBXRestCustomParamBlogGet
} from '../type/blog'
@Injectable({
providedIn: 'root'
})
export class BXRestNavvyCustomApiBlog {
private readonly url = {
get: ['blog', PNM.$get]
}
private readonly navvy = inject(Navvy)
get(param: iBXRestCustomParamBlogGet) {
return this.navvy.simple<
iBXRestCustomBlogGetHttp[],
iBXRestCustomBlogGet[],
iBXRestCustomParamBlogGet
>(
this.url.get,
param,
BXCustomBlogMap.get
)
}
}Возвращённый хелпер поддерживает .res(), .resVanilla() и .mapForVanilla().
Пагинированный кастомный метод с pagNav()
Используйте pagNav(), когда ответ Bitrix содержит массив и стандартные поля пагинации start, next и total. Тип параметра должен поддерживать start?: number.
import { inject, Injectable } from '@angular/core'
import { Navvy, partNameMethods as PNM } from 'bx-rest'
import type { iBXRestPagination } from 'bx-rest/typification/base'
import { BXCustomBlogMap } from '../map/blog'
import {
iBXRestCustomBlogGet,
iBXRestCustomBlogGetHttp
} from '../type/blog'
export interface iBXRestCustomParamBlogList extends iBXRestPagination {
filter?: Record<string, unknown>
}
@Injectable({
providedIn: 'root'
})
export class BXRestNavvyCustomApiBlogList {
private readonly url = {
list: ['blog', PNM.$list]
}
private readonly navvy = inject(Navvy)
list(param: iBXRestCustomParamBlogList) {
return this.navvy.pagNav<
iBXRestCustomBlogGetHttp,
iBXRestCustomBlogGet,
iBXRestCustomParamBlogList
>(
this.url.list,
param,
BXCustomBlogMap.get
)
}
}Возвращённый хелпер дополнительно поддерживает .resAll() для загрузки и объединения всех страниц.
Специфичные методы
tasks.task.list
Первый generic-аргумент выбирает встроенные поля задачи. Второй добавляет пользовательские поля, специфичные для проекта:
import { BXRestNavvy } from 'bx-rest'
import type { iBXRestTaskFieldsName } from 'bx-rest/typification/tasks'
type SelectedTaskField = Extract<
iBXRestTaskFieldsName,
'ID' | 'TITLE' | 'GROUP_ID' | 'TIME_ESTIMATE' | 'CREATED_DATE' | 'CLOSED_DATE' | 'DURATION_FACT' | 'TIME_SPENT_IN_LOGS'
>
type CustomTaskFields = {
ufListDiscipline: string
ufListSubdiscipline: string
ufList1: string
}
const selectedTaskFields: SelectedTaskField[] = [
'ID',
'TITLE',
'GROUP_ID',
'CREATED_DATE'
]
const selectedCustomFields = [
'UF_LIST_DISCIPLINE',
'UF_LIST_SUBDISCIPLINE',
'UF_LIST_1'
] as const
const bxRest = new BXRestNavvy()
const tasks$ = bxRest.tasks.task.list<
SelectedTaskField[],
CustomTaskFields
>({
order: {
ID: 'DESC'
},
filter: {
CREATED_BY: [1]
},
select: [...selectedTaskFields, ...selectedCustomFields],
start: 0
}).res()Парсинг дат
Некоторые REST-поля Bitrix24 (в частности calendar.section.get, calendar.event.get и crm.calllist.*) возвращают даты в виде обычных строк, отформатированных согласно собственным настройкам даты/времени портала (Настройки → формат даты и времени), а не в фиксированном формате ISO. Поскольку этот формат — настройка конкретного портала, bx-rest не может знать его заранее, поэтому BXBaseServices.toDate() (используется этими мапперами внутренне) разрешает неоднозначную строку в три шага, по порядку:
- Формат, который вы указали. Если вы (или маппер) передаёте явный
format(например,'dd.MM.yyyy HH:mm:ss'), он проверяется первым, токен за токеном (yyyy,MM,dd,HH,mm,ssи их однобуквенные варианты) с буквальными разделителями из строки формата. - Небольшой список известных форматов Bitrix24 (
'dd.MM.yyyy HH:mm:ss','yyyy-MM-dd HH:mm:ss','dd.MM.yyyy','yyyy-MM-dd') — проверяется, если шаг 1 не подошёл, поскольку разные порталы обычно присылают один из этих форматов. - Позиционная эвристика как последний вариант — она находит год по его однозначной 4-значной длине (независимо от того, на каком конце даты он стоит), считает средний сегмент месяцем (это верно и для ISO, и для европейского формата даты) и распознаёт любой разделитель (
.,-,/, пробел...). Она не пытается угадать в единственном действительно неоднозначном случае — чисто числовой порядокMM/ddпротивdd/MM, когда оба сегмента ≤ 12, — и проверяет диапазоны значений (месяц 1-12, день 1-31, час/минута/секунда), а не позволяетDateмолча перевести некорректное значение на следующий месяц/год.
Если вы точно знаете формат вашего портала, передавайте его явно вместо того, чтобы полагаться на автоопределение — он всегда проверяется первым и полностью пропускает шаги 2-3. Мапперы календаря и списка звонков принимают его как единственный необязательный завершающий аргумент dateTimeFormat, и он также передаётся через соответствующие методы BXRestNavvy. Там, где у маппера есть ещё и поля только с датой (EXDATE/RRULE.UNTIL у calendar.event), их формат выводится из dateTimeFormat отбрасыванием HH:mm:ss — порядок день/месяц/год всегда одинаков для обоих на конкретном портале, поэтому передавать дополнительно нечего:
import { BXRestNavvy } from 'bx-rest'
const bxRest = new BXRestNavvy()
// Формат даты/времени конкретного портала, например из Настройки → формат даты и времени.
const dateTimeFormat = 'yyyy-MM-dd HH:mm:ss'
bxRest.calendar.event.get({ FROM: '01.01.2026', TO: '31.01.2026' }, dateTimeFormat).res()
bxRest.calendar.section.get({}, dateTimeFormat).res()
bxRest.crm.callList.get({ ID: 1 }, dateTimeFormat).res()Планы на будущее
- Автоматическое получение токена авторизации
Есть предложение по функциональности или нашли баг? Посмотрите существующие issues или создайте новый.
Лицензия
MIT © Aricatofuka
