Document Stack
Document Stack
Docs

Next.js Integration

Generate PDFs from Next.js applications using server actions, API routes, and the Document Stack SDK.

Installation

Bash
npm install @document-stack/sdk-node

Add your API key to .env.local:

.env.local
DS_API_KEY=your_api_key_here

Server Actions (App Router)

The recommended approach for Next.js App Router is server actions. The API key stays on the server — never exposed to the client.

app/actions/generate-pdf.ts
"use server";

import { DocumentStack } from "@document-stack/sdk-node";

const client = new DocumentStack({
    apiKey: process.env.DS_API_KEY!,
});

export async function generateInvoice(data: {
    customerName: string;
    amount: number;
    items: Array<{ description: string; price: number }>;
}) {
    const pdf = await client.generate({
        templateId: "tmpl_invoice",
        data,
    });

    return { url: pdf.url };
}
app/invoices/page.tsx
"use client";

import { generateInvoice } from "@/app/actions/generate-pdf";
import { useState } from "react";

export default function InvoicePage() {
    const [pdfUrl, setPdfUrl] = useState<string | null>(null);
    const [loading, setLoading] = useState(false);

    async function handleGenerate() {
        setLoading(true);
        const result = await generateInvoice({
            customerName: "Acme Corp",
            amount: 499.99,
            items: [
                { description: "Consulting", price: 299.99 },
                { description: "Support", price: 200.00 },
            ],
        });
        setPdfUrl(result.url);
        setLoading(false);
    }

    return (
        <div>
            <button onClick={handleGenerate} disabled={loading}>
                {loading ? "Generating..." : "Generate Invoice"}
            </button>
            {pdfUrl && (
                <a href={pdfUrl} target="_blank" rel="noopener noreferrer">
                    Download PDF
                </a>
            )}
        </div>
    );
}

API Routes

Alternatively, use a Route Handler for REST-style endpoints:

app/api/generate/route.ts
import { DocumentStack } from "@document-stack/sdk-node";
import { NextRequest, NextResponse } from "next/server";

const client = new DocumentStack({
    apiKey: process.env.DS_API_KEY!,
});

export async function POST(req: NextRequest) {
    const body = await req.json();

    const pdf = await client.generate({
        templateId: body.templateId,
        data: body.data,
    });

    return NextResponse.json({ url: pdf.url });
}

Authentication

Always protect your API routes with authentication. The example above is simplified — add session checks or middleware before allowing PDF generation.

Streaming Download

For direct PDF downloads without storing URLs:

app/api/download/route.ts
import { DocumentStack } from "@document-stack/sdk-node";
import { NextRequest } from "next/server";

const client = new DocumentStack({
    apiKey: process.env.DS_API_KEY!,
});

export async function GET(req: NextRequest) {
    const templateId = req.nextUrl.searchParams.get("templateId")!;

    const pdf = await client.generate({
        templateId,
        data: { date: new Date().toISOString() },
    });

    const pdfResponse = await fetch(pdf.url);
    const pdfBuffer = await pdfResponse.arrayBuffer();

    return new Response(pdfBuffer, {
        headers: {
            "Content-Type": "application/pdf",
            "Content-Disposition": 'attachment; filename="document.pdf"',
        },
    });
}

Best Practices

  • Keep the SDK client in a shared utility file to reuse the connection
  • Use Server Actions instead of API routes for simpler code
  • Add error handling with try/catch around all SDK calls
  • Cache generated PDFs when the same data produces the same output
  • Store DS_API_KEY in environment variables, never in code

Next Steps