BlogInvoice Parsing with Node.js — Complete Integration Guide

Invoice Parsing with Node.js — Complete Integration Guide

2026-05-24 · 5 min read

This guide covers adding invoice and receipt parsing to a Node.js application using DocuParseAPI. You'll find working code for raw Node.js, Express, Next.js App Router, and TypeScript patterns.

Node 18+
native fetch
TS
types included
~3s
extraction time
Free
20 documents/month — free forever
Setup

Prerequisites

const fs = require("fs");
async function parseInvoice(filePath) {
  const form = new FormData();
  form.append("file", new Blob([fs.readFileSync(filePath)]), "invoice.pdf");
  const res = await fetch("https://docuparseapi.com/api/v1/extract", {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.DOCUPARSE_API_KEY}` },
    body: form,
  });
  const data = await res.json();
  if (!data.success) throw new Error(data.error.message);
  return data;
}
Test the API before writing a line of code
Upload any PDF invoice. Get back the JSON your Node.js app will receive.
Open Live Demo →
const invoice = await parseInvoice("invoice.pdf");
console.log(invoice.merchant); // "Acme Corp"
console.log(invoice.total); // "1320.00"
Free tier · 20 documents/month — free forever · No credit card · No account needed for the demo
Quick Start

The Minimal Request

Node.js 18+ has fetch built in, so no HTTP library is required:

javascript · 27 lines
const fs = require("fs");

async function parseInvoice(filePath) {
  const fileBuffer = fs.readFileSync(filePath);
  const blob = new Blob([fileBuffer]);

  const form = new FormData();
  form.append("file", blob, require("path").basename(filePath));

  const response = await fetch("https://docuparseapi.com/api/v1/extract", {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.DOCUPARSE_API_KEY}` },
    body: form,
  });

  const data = await response.json();

  if (!data.success) {
    throw new Error(`[${data.error.code}] ${data.error.message}`);
  }

  return data;
}

// Usage
const invoice = await parseInvoice("./invoice.pdf");
console.log(`${invoice.merchant}: $${invoice.total}`);
TypeScript

TypeScript Types

typescript · 65 lines
interface LineItem {
  description: string | null;
  quantity: number | null;
  unit_price: string | null;
  total: string | null;
}

interface ExtractedDocument {
  success: boolean;
  document_id: string;
  document_type: "invoice" | "receipt";
  merchant: string | null;
  date: string | null;
  due_date: string | null;
  currency: string | null;
  subtotal: string | null;
  tax: string | null;
  tax_rate: string | null;
  total: string | null;
  invoice_id: string | null;
  receipt_id: string | null;
  payment_method: string | null;
  line_items: LineItem[];
  processing_time_ms: number;
}

interface ApiError {
  success: false;
  error: {
    code: 
      | "MISSING_API_KEY"
      | "INVALID_API_KEY"
      | "REVOKED_API_KEY"
      | "LIMIT_EXCEEDED"
      | "UNSUPPORTED_FILE_TYPE"
      | "FILE_TOO_LARGE"
      | "NO_FILE_PROVIDED"
      | "EXTRACTION_FAILED"
      | "INTERNAL_ERROR";
    message: string;
  };
}

type ApiResponse = ExtractedDocument | ApiError;

async function parseInvoiceTyped(filePath: string): Promise<ExtractedDocument> {
  const fileBuffer = await fs.promises.readFile(filePath);
  const blob = new Blob([fileBuffer]);
  const form = new FormData();
  form.append("file", blob, path.basename(filePath));

  const response = await fetch("https://docuparseapi.com/api/v1/extract", {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.DOCUPARSE_API_KEY!}` },
    body: form,
  });

  const data: ApiResponse = await response.json();

  if (!data.success) {
    throw new Error(`[${data.error.code}] ${data.error.message}`);
  }

  return data;
}
Express

Express Middleware

javascript · 59 lines
// middleware/parseReceipt.js
const multer = require("multer");

const upload = multer({
  storage: multer.memoryStorage(),
  limits: { fileSize: 10 * 1024 * 1024 },
  fileFilter: (req, file, cb) => {
    const allowed = ["application/pdf", "image/jpeg", "image/png"];
    cb(null, allowed.includes(file.mimetype));
  },
});

/**
 * Express middleware: parses an uploaded document and attaches
 * the extracted data to req.extractedDocument
 */
async function parseUploadedDocument(req, res, next) {
  if (!req.file) {
    return res.status(400).json({ error: "No file provided" });
  }

  try {
    const blob = new Blob([req.file.buffer], { type: req.file.mimetype });
    const form = new FormData();
    form.append("file", blob, req.file.originalname);

    const response = await fetch("https://docuparseapi.com/api/v1/extract", {
      method: "POST",
      headers: { Authorization: `Bearer ${process.env.DOCUPARSE_API_KEY}` },
      body: form,
    });

    const data = await response.json();

    if (!data.success) {
      if (data.error.code === "LIMIT_EXCEEDED") {
        return res.status(429).json({ error: "Document limit reached" });
      }
      return res.status(422).json({ error: data.error.message });
    }

    req.extractedDocument = data;
    next();
  } catch (err) {
    next(err);
  }
}

// Usage in route
app.post(
  "/api/invoices/upload",
  upload.single("invoice"),
  parseUploadedDocument,
  (req, res) => {
    const doc = req.extractedDocument;
    // Create your invoice record from doc.merchant, doc.total, doc.line_items, etc.
    res.json({ invoice: doc });
  }
);
Next.js

Next.js App Router API Route

typescript · 58 lines
// app/api/invoices/upload/route.ts
import { NextRequest, NextResponse } from "next/server";

export async function POST(request: NextRequest): Promise<NextResponse> {
  const apiKey = process.env.DOCUPARSE_API_KEY;
  if (!apiKey) {
    return NextResponse.json({ error: "Server misconfiguration" }, { status: 500 });
  }

  // Parse the incoming form data
  let formData: FormData;
  try {
    formData = await request.formData();
  } catch {
    return NextResponse.json({ error: "Invalid form data" }, { status: 400 });
  }

  const file = formData.get("file");
  if (!file || !(file instanceof File)) {
    return NextResponse.json({ error: "No file provided" }, { status: 400 });
  }

  // Validate type
  const allowedTypes = ["application/pdf", "image/jpeg", "image/png"];
  if (!allowedTypes.includes(file.type)) {
    return NextResponse.json(
      { error: "Only PDF, JPG, and PNG files are supported" },
      { status: 400 }
    );
  }

  // Forward to DocuParseAPI
  const upstream = new FormData();
  upstream.append("file", file);

  try {
    const response = await fetch("https://docuparseapi.com/api/v1/extract", {
      method: "POST",
      headers: { Authorization: `Bearer ${apiKey}` },
      body: upstream,
    });

    const data = await response.json();

    if (!data.success) {
      return NextResponse.json(
        { error: data.error.message, code: data.error.code },
        { status: response.status }
      );
    }

    return NextResponse.json(data);

  } catch (error) {
    console.error("DocuParseAPI error:", error);
    return NextResponse.json({ error: "Extraction service error" }, { status: 502 });
  }
}
Your Node.js app is ready for this.
Get your API key in 60 seconds. Works in any Node.js environment.
Errors

Error Handling Reference

javascript · 25 lines
async function safeParseInvoice(filePath) {
  try {
    const result = await parseInvoice(filePath);
    return { success: true, data: result };
  } catch (error) {
    // Parse error code from message
    const match = error.message.match(/\[(\w+)\]/);
    const code = match?.[1];

    switch (code) {
      case "LIMIT_EXCEEDED":
        return { success: false, reason: "Monthly document limit reached" };
      case "UNSUPPORTED_FILE_TYPE":
        return { success: false, reason: "File type not supported" };
      case "FILE_TOO_LARGE":
        return { success: false, reason: "File exceeds 10MB" };
      case "EXTRACTION_FAILED":
        return { success: false, reason: "Document could not be parsed" };
      case "INVALID_API_KEY":
        return { success: false, reason: "Invalid API key" };
      default:
        return { success: false, reason: "Unexpected error" };
    }
  }
}
Node.js 18+ · TypeScript · Express · Next.js

Your Node.js app needs one more thing.

20 documents/month — free forever. No credit card. Works everywhere.

More from the blog