> ## Documentation Index
> Fetch the complete documentation index at: https://docs.markpdf.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Next.js

> Route Handler seguro para no exponer la API key.

# Next.js

Usa un Route Handler para que la API key no llegue al navegador.

```ts theme={null}
// app/api/convert/route.ts
export const runtime = "nodejs";
export const maxDuration = 300;

export async function POST(request: Request) {
  const form = await request.formData();
  const file = form.get("file");

  if (!(file instanceof File)) {
    return Response.json({ error: "Missing file" }, { status: 400 });
  }

  const res = await fetch(
    `${process.env.FLASH_MD_API_URL}/convert/raw?filename=${encodeURIComponent(file.name)}&mode=fast`,
    {
      method: "POST",
      headers: {
        "x-api-key": process.env.FLASH_MD_API_KEY!,
        "content-type": file.type || "application/octet-stream"
      },
      body: file
    }
  );

  const text = await res.text();
  return new Response(text, {
    status: res.status,
    headers: { "content-type": res.headers.get("content-type") || "text/markdown" }
  });
}
```

Para archivos grandes, sube primero a tu storage y llama a `/convert/from-url`.

## Manejo de 202 (auto-async)

Si la API devuelve `202`, el Route Handler puede pollear y devolver el resultado cuando esté listo:

```ts theme={null}
// app/api/convert/route.ts
export const runtime = "nodejs";
export const maxDuration = 300;

async function pollJob(jobId: string): Promise<string> {
  const key = process.env.FLASH_MD_API_KEY!;
  const url = process.env.FLASH_MD_API_URL!;

  while (true) {
    await new Promise((r) => setTimeout(r, 5000));
    const res = await fetch(`${url}/jobs/${jobId}`, {
      headers: { "x-api-key": key },
    });
    const data = await res.json();
    if (data.status === "completed") return data.body;
    if (data.status === "failed") throw new Error(data.error);
  }
}

export async function POST(request: Request) {
  const form = await request.formData();
  const file = form.get("file");

  if (!(file instanceof File)) {
    return Response.json({ error: "Missing file" }, { status: 400 });
  }

  const res = await fetch(
    `${process.env.FLASH_MD_API_URL}/convert/raw?filename=${encodeURIComponent(file.name)}&mode=fast`,
    {
      method: "POST",
      headers: {
        "x-api-key": process.env.FLASH_MD_API_KEY!,
        "content-type": file.type || "application/octet-stream",
      },
      body: file,
    }
  );

  if (res.status === 202) {
    const job = await res.json();
    const markdown = await pollJob(job.job_id);
    return new Response(markdown, {
      headers: { "content-type": "text/markdown; charset=utf-8" },
    });
  }

  return new Response(await res.text(), {
    status: res.status,
    headers: { "content-type": res.headers.get("content-type") || "text/markdown" },
  });
}
```
