pdfapi

PDF в веб-приложении

Пользователь нажимает «Скачать счёт», ваш сервис делает PDF через pdfapi и отдаёт его пользователю. Ключ при этом остаётся на сервере.

Браузер обращается к вашему серверу. Сервер с ключом вызывает pdfapi и возвращает браузеру готовый PDF. Ключ из сервера никуда не уходит.
Не вызывайте pdfapi из браузера

Запрос из браузера раскроет ключ любому пользователю.

Обработчик#

Библиотека возвращает PDF в памяти, поэтому сохранять его на диск не нужно — отдайте байты сразу в ответ. Заголовок Content-Disposition: inline откроет PDF во вкладке браузера, а attachment предложит скачать файл.

Python
# FastAPI: один асинхронный клиент на всё приложение.
@asynccontextmanager
async def lifespan(app: FastAPI):
    async with AsyncPdfApi() as client:
        app.state.pdfapi = client
        yield

app = FastAPI(lifespan=lifespan)

@app.get("/invoice/{number}")
async def invoice(number: int, request: Request) -> Response:
    pdf = await request.app.state.pdfapi.html(render_invoice(number))
    return Response(pdf.content, media_type="application/pdf",
                    headers={"Content-Disposition": f'inline; filename="invoice-{number}.pdf"'})

Экранирование#

При наличии данных пользователя в HTML рекомендуется пропускать их через шаблоны с автоэкранированием или явное экранирование. Иначе пользователь вставит в свой счёт чужую разметку. Подробнее — в Безопасности.

Кеш#

Если документ не меняется (например, счёт за закрытый месяц), сохраните PDF у себя после первого запроса. Повторная генерация тратит страницы и время на обработку.