Blog

Převod dokumentů do Markdownu s Node.js

14. srpna 2026 · 6 min čtení


Pokud vyvíjíte aplikaci v Node.js, která přijímá dokumenty nahrané uživateli, pravděpodobně jste už hleděli na složku plnou PDF, souborů DOCX a prezentací PPTX a říkali si: jak to všechno normalizovat do podoby, kterou moje LLM pipeline skutečně dokáže použít?

Tento článek ukazuje práci s REST API AnyMD z Node.js — pomocí fetch, axios, typů TypeScript, streamingu a vzorů Express middleware pro produkční konverzi dokumentů do Markdownu.

Proč Markdown z Node.js?

Aplikace Node.js přirozeně zvládají nahrávání souborů — Multer, Busboy, Express middleware. Převod těchto souborů na čistý strukturovaný text pro RAG pipeline nebo AI trénovací data však obvykle znamená těžké nativní bindingy (PyMuPDF, LibreOffice, pandoc), které do JS runtime nepatří.

AnyMD nabízí bezstavové HTTP API. Jedno volání, jedna odpověď, čistý Markdown. Žádné nativní závislosti, žádné serverové knihovny formátů, žádné Python subprocessy v aplikaci Node.js.

Rychlý start s fetch (bez závislostí)

Node.js 18+ poskytuje fetch globálně. Nejjednodušší jednorázová konverze:

const fs = require("fs");
const { readFile } = require("fs/promises");

async function convertToMarkdown(filePath, apiKey) {
  const form = new FormData();
  form.set("file", new Blob([await readFile(filePath)]), filePath);

  const resp = await fetch("https://anymd.net/api/convert", {
    method: "POST",
    headers: { Authorization: `Bearer ${apiKey}` },
    body: form,
  });

  if (!resp.ok) {
    throw new Error(`AnyMD error ${resp.status}: ${await resp.text()}`);
  }

  return await resp.text();
}

// Usage
const md = await convertToMarkdown("report.docx", "your-api-key");
console.log(md);

A je to. AnyMD automaticky rozpozná formát podle přípony souboru a vrátí GitHub-Flavored Markdown.

TypeScript s typovanými odpověďmi

V projektech TypeScript přidejte správné návratové typy a ošetření chyb:

interface ConversionResult {
  markdown: string;
  format: string;
  pages: number;
  duration: number;
}

interface ConversionError {
  status: number;
  message: string;
}

async function convertToMarkdownTyped(
  filePath: string,
  apiKey: string
): Promise<ConversionResult> {
  const form = new FormData();
  const buffer = await fs.promises.readFile(filePath);
  form.set("file", new Blob([buffer]), filePath);

  const start = performance.now();
  const resp = await fetch("https://anymd.net/api/convert", {
    method: "POST",
    headers: { Authorization: `Bearer ${apiKey}` },
    body: form,
  });
  const duration = performance.now() - start;

  if (!resp.ok) {
    const err: ConversionError = {
      status: resp.status,
      message: await resp.text(),
    };
    throw err;
  }

  return {
    markdown: await resp.text(),
    format: filePath.split(".").pop() || "unknown",
    pages: parseInt(resp.headers.get("X-Pages") || "0", 10),
    duration: Math.round(duration),
  };
}

Express middleware pro upload endpointy

Takto zapojíte AnyMD do Express serveru s Multerem pro nahrávání souborů:

import express from "express";
import multer from "multer";
import fs from "fs/promises";

const app = express();
const upload = multer({ dest: "/tmp/uploads/" });
const ANYMD_API = "https://anymd.net/api/convert";

app.post("/upload", upload.single("file"), async (req, res) => {
  try {
    const apiKey = req.headers["authorization"]?.replace("Bearer ", "");
    if (!apiKey) return res.status(401).json({ error: "Missing API key" });

    const form = new FormData();
    const buffer = await fs.readFile(req.file.path);
    form.set("file", new Blob([buffer]), req.file.originalname);

    const anymdResp = await fetch(ANYMD_API, {
      method: "POST",
      headers: { Authorization: `Bearer ${apiKey}` },
      body: form,
    });

    if (!anymdResp.ok) {
      return res.status(anymdResp.status).json({
        error: await anymdResp.text(),
      });
    }

    const markdown = await anymdResp.text();
    res.json({ markdown, pages: anymdResp.headers.get("X-Pages") });
  } catch (err) {
    res.status(500).json({ error: err.message });
  } finally {
    if (req.file) fs.unlink(req.file.path).catch(() => {});
  }
});

app.listen(3000);

Dávkové zpracování s axios a opakováním

Když potřebujete zpracovat stovky dokumentů, sekvenční volání jsou příliš pomalá. Zde je souběžný dávkový procesor s axios:

import axios from "axios";
import FormData from "form-data";
import fs from "fs";
import path from "path";
import pLimit from "p-limit";

const API = "https://anymd.net/api/convert";

async function convertOne(filePath, apiKey) {
  const form = new FormData();
  form.append("file", fs.createReadStream(filePath), path.basename(filePath));

  for (let attempt = 0; attempt < 3; attempt++) {
    try {
      const resp = await axios.post(API, form, {
        headers: {
          ...form.getHeaders(),
          Authorization: `Bearer ${apiKey}`,
        },
        maxContentLength: 50 * 1024 * 1024,
        timeout: 30_000,
      });

      const outPath = filePath.replace(path.extname(filePath), ".md");
      fs.writeFileSync(outPath, resp.data);
      return { file: path.basename(filePath), ok: true };
    } catch (err) {
      if (attempt === 2) return { file: path.basename(filePath), ok: false, error: err.message };
      const delay = Math.pow(1.5, attempt) * 1000;
      await new Promise(r => setTimeout(r, delay));
    }
  }
}

async function batchConvert(dir, apiKey, concurrency = 10) {
  const files = fs.readdirSync(dir).filter(f =>
    /\.(docx|pdf|pptx|xlsx|epub|odt|rtf)$/i.test(f)
  );
  const limit = pLimit(concurrency);
  const results = await Promise.all(
    files.map(f => limit(() => convertOne(path.join(dir, f), apiKey)))
  );

  const ok = results.filter(r => r.ok).length;
  console.log(`${ok}/${results.length} converted`);
  return results;
}

await batchConvert("./documents", "your-api-key", 10);

Streamování velkých dokumentů

Pro dokumenty nad 10 MB použijte streamingový endpoint, abyste nemuseli držet celý výstup Markdown v paměti.

Porovnání s lokálními knihovnami Node.js

FormátKnihovna Node.jsAnyMD
DOCXmammoth — bez podpory PPTX/PDF/XLSXJedno API, více než 15 formátů
PDFpdf-parse — ztrácí tabulky a nadpisyGFM, zachovává strukturu
PPTXpptx2md — pouze CLI, bez APIREST API, asynchronní pipeline
XLSXxlsx — export CSV, bez MarkdownuTabulky → GFM tabulky
EPUBepub2md — neudržované od roku 2022Aktivní, verzované API

Výkon: Node.js komunikuje s Rustem

AnyMD běží na backendu Rust/Axum.

FormátVelikost dokumentuAnyMD (ms)Lokální knihovna (ms)
DOCX50 KB (10 stran)280620 (mammoth)
PDF120 KB (15 stran)4102,100 (pdf-parse)
PPTX200 KB (20 snímků)5201,800 (pptx2md)
XLSX80 KB (5 listů)340950 (xlsx + ruční formátování)

Od Markdownu k RAG pipeline

Jakmile má vaše Node.js aplikace čistý Markdown, dalším krokem je chunkování a embedding pro vektorové vyhledávání. Výstup AnyMD je ideálním vstupem pro sémantické chunkování — hranice nadpisů, struktura seznamů a záhlaví tabulek zůstávají zachovány.

Ceny

TarifStránek/měsícMaximální velikost souboruCena
Zdarma10010 MB$0
Starter50025 MB$19
Pro5,00050 MB$99
EnterpriseNa míruNa míruNa míru

Typický dokument se převede za méně než 0,5 sekundy. 100 stránek zdarma měsíčně — bez platební karty.

Získejte bezplatný API klíč a začněte ještě dnes převádět dokumenty do Markdownu z Node.js.


← Číst dále →