Экспорт компаний на Node + Hook

Экспорт компаний Bitrix24 в CSV-файл из чистого Node.js-скрипта через B24Hook.

Типичная batch / cron-задача: экспорт компаний в CSV одним вебхуком. Не превышает лимиты частоты и использует fetchList, поэтому память не растёт независимо от размера набора данных.

Структура проекта

my-export-job/
├── dist/
├── src/
│   └── process-company-list.ts
├── out/                            # сгенерированные *.csv файлы
├── .env.local                      # B24_HOOK=...
├── package.json
└── tsconfig.json

package.json

{
  "name": "my-export-job",
  "type": "module",
  "scripts": {
    "build": "tsc",
    "start": "node --env-file=.env.local dist/process-company-list.js"
  },
  "devDependencies": {
    "@types/node": "^22",
    "typescript": "^5.6"
  },
  "dependencies": {
    "@bitrix24/b24jssdk": "latest",
    "chalk": "^5"
  }
}

src/process-company-list.ts

// @check-ignore: node:fs not available in docs typecheck context

import { writeFileSync } from 'node:fs'
import {
  B24Hook,
  EnumCrmEntityTypeId,
  LoggerFactory,
  Text,
  type ISODate
} from '@bitrix24/b24jssdk'

const $logger = LoggerFactory.createForBrowser('export', process.env.NODE_ENV === 'development')

if (!process.env.B24_HOOK) {
  $logger.error('B24_HOOK is not set in .env.local')
  process.exit(1)
}

const $b24 = B24Hook.fromWebhookUrl(process.env.B24_HOOK)
$logger.info(`Portal: ${$b24.getTargetOrigin()}`)

type Company = { id: number, title: string, createdTime: ISODate }

async function main() {
  const generator = $b24.actions.v2.fetchList.make<Company>({
    method: 'crm.item.list',
    params: {
      entityTypeId: EnumCrmEntityTypeId.company,
      select: ['id', 'title', 'createdTime']
    },
    idKey: 'id',
    requestId: 'export:companies'
  })

  const rows: string[] = ['id,title,createdTime']
  for await (const chunk of generator) {
    for (const c of chunk) {
      rows.push([
        c.id,
        JSON.stringify(c.title ?? ''),
        Text.toDateTime(c.createdTime).toFormat('yyyy-LL-dd HH:mm:ss')
      ].join(','))
    }
    $logger.info(`Loaded ${chunk.length} (total ${rows.length - 1})`)
  }

  const out = `out/companies-${Date.now()}.csv`
  writeFileSync(out, rows.join('\n'), 'utf8')
  $logger.notice(`Wrote ${out}`)

  $b24.destroy()
}

main().catch((error) => {
  $logger.error('export failed', { error })
  process.exit(1)
})

Почему fetchList?

  • Память не растёт: каждая итерация for await отдаёт чанк из 50 элементов, поэтому набор в 50 000 строк никогда не материализуется в RAM.
  • Встроенная пагинация: не пишите свой цикл start: nextStart.
  • Аккуратно обрабатывает rate / operating лимитеры SDK между страницами.

Для полноценных массовых операций (удалить-затем-создать, массовые импорты) сочетайте экспорт с actions.v2.batchByChunk.

Полный исходник

bitrix24/b24sdk-examples → js/05-node-hook.