Обработка PDF: async генерация и извлечение данных с объектным хранилищем
Puppeteer или pdfkit в Python: генерируйте PDF асинхронно, сохраняйте в S3, возвращайте pre-signed URL. Без HTTP-таймаута, без передачи бинаря в ответе.
Last updated: 2026-04-20
Answer first
Direct answer
Обработка PDF: async генерация и извлечение данных с объектным хранилищем. HTTP-функция принимает параметры, возвращает `{ jobId }`. Пайплайн рендерит PDF (puppeteer в Node.js или reportlab в Python), загружает в S3, возвращает pre-signed URL.
When it fits
- Генерация занимает > 2 секунд
- Документ нужно хранить и скачивать несколько раз
- Batch PDF generation (N invoices, N reports) with fan-out parallelism
Tradeoffs
- Синхронная генерация с puppeteer в Lambda: cold start браузера + рендер HTML + сохранение = часто за пределами таймаута.
- Встроить binary в JSON-ответ: base64 раздувает payload, клиент не может начать скачивание до получения всего тела.
Нагрузка и где ломается
Почему PDF-генерация ломается в HTTP
- PDF generation from complex templates takes 2–30 seconds—hits gateway timeouts
- Text extraction from uploaded PDFs with OCR can take minutes for image-heavy files
- Binary response handling (base64 encoding large PDFs) adds memory pressure to synchronous handlers
Puppeteer или WeasyPrint занимают секунды. HTTP-ответ с PDF-файлом блокирует соединение на всё это время — таймаут шлюза ломает генерацию.
Где костыли не спасают
Где ломаются простые подходы
Синхронная генерация с puppeteer в Lambda: cold start браузера + рендер HTML + сохранение = часто за пределами таймаута.
Встроить binary в JSON-ответ: base64 раздувает payload, клиент не может начать скачивание до получения всего тела.
Как помогает Inquir
Async PDF на Inquir
HTTP-функция принимает параметры, возвращает `{ jobId }`. Пайплайн рендерит PDF (puppeteer в Node.js или reportlab в Python), загружает в S3, возвращает pre-signed URL.
Puppeteer через слой: нативный хром-бинарь доступен в контейнере Node.js 22 без custom Dockerfile.
Что получаете
Что нужно для надёжной PDF-обработки
Async split
HTTP возвращает jobId; пайплайн генерирует PDF вне HTTP-окна.
Объектное хранилище
PDF сохраняется в S3/GCS; клиент получает pre-signed URL — не бинарный blob в ответе.
Нативные зависимости через слои
Puppeteer, chromium, reportlab, WeasyPrint через слои без Dockerfile.
Traceability
jobId → история прогона с логами каждого шага; проще дебажить ошибки рендера.
Что делать дальше
Как организовать PDF-генерацию
Принять запрос, вернуть jobId
HTTP-функция валидирует параметры, запускает `global.durable.startNew()`, возвращает `{ jobId }` за <100 мс.
Сгенерировать PDF в пайплайне
Puppeteer рендерит HTML или reportlab строит документ. Сохранить в S3.
Вернуть pre-signed URL
Пайплайн сохраняет URL в БД или отправляет вебхук. Клиент получает ссылку и скачивает напрямую из S3.
Пример кода
Invoice PDF generation pipeline
HTTP handler triggers async generation; pipeline step renders HTML to PDF, uploads, and notifies. Client polls job status.
export async function handler(event) { const { invoiceId, customerId } = JSON.parse(event.body || '{}'); if (!invoiceId) return { statusCode: 400, body: JSON.stringify({ error: 'invoiceId required' }) }; const existing = await db.invoicePdfs.find(invoiceId); if (existing?.url) return { statusCode: 200, body: JSON.stringify({ url: existing.url }) }; await global.durable.startNew('render-invoice-pdf', undefined, { invoiceId, customerId }); return { statusCode: 202, body: JSON.stringify({ invoiceId, status: 'generating' }) }; }
import puppeteer from 'puppeteer-core'; export async function handler(event) { const { invoiceId, customerId } = event.payload ?? {}; const invoice = await db.invoices.findById(invoiceId); const html = renderInvoiceTemplate(invoice); const browser = await puppeteer.launch({ executablePath: '/usr/bin/chromium' }); const page = await browser.newPage(); await page.setContent(html, { waitUntil: 'networkidle0' }); const pdfBytes = await page.pdf({ format: 'A4', printBackground: true }); await browser.close(); const url = await storage.upload(pdfBytes, `invoices/${invoiceId}.pdf`); await db.invoicePdfs.upsert({ invoiceId, url, generatedAt: new Date() }); return { invoiceId, url }; }
Когда подходит
Когда нужен async PDF
Когда это уместно
- Генерация занимает > 2 секунд
- Документ нужно хранить и скачивать несколько раз
- Batch PDF generation (N invoices, N reports) with fan-out parallelism
Когда лучше не трогать
- Простой text/plain-to-PDF < 1 страницы без изображений — sync достаточно
FAQ
Вопросы и ответы
Как добавить Puppeteer в Inquir?
Слой с puppeteer-core + chromium-min; подключить к Node.js 22 функции. Puppeteer доступен через `import puppeteer from "puppeteer-core"`.
Python или Node.js для PDF?
Node.js: puppeteer для HTML-рендера. Python: reportlab или WeasyPrint для программной генерации. Оба через слои.