Next.js Upload Guardrail
Scan browser uploads in a Next.js Route Handler before storage, OCR, extraction, or review.
Goal
Use this page when a browser uploads a file to your Next.js app.
This page proves one product flow:
browser upload -> Next.js server route -> Mighty scan -> store, quarantine, or rejectThe point is not Next.js itself. The point is keeping the API key server-side and making sure files are scanned before storage, OCR, AI extraction, or review queues trust them.
When To Use This
Use it for:
- Claim packet uploads.
- Invoice or repair estimate uploads.
- Damage photos uploaded through a web form.
- Support attachments.
- Any file that will later go to OCR, AI extraction, search, or workflow automation.
Architecture
- Browser posts the file to your Next.js route.
- The route forwards the file to Mighty with server-side auth.
- Mighty returns ALLOW, WARN, or BLOCK.
- The route stores, quarantines, or rejects the file.
- Downstream OCR or AI only runs after routing.
Route Handler
export const runtime = "nodejs";
export async function POST(request: Request) {
const incoming = await request.formData();
const file = incoming.get("file");
if (!(file instanceof File)) {
return Response.json({ error: "file is required" }, { status: 400 });
}
const mightyForm = new FormData();
mightyForm.append("file", file);
mightyForm.append("content_type", "auto");
mightyForm.append("scan_phase", "input");
mightyForm.append("mode", "secure");
mightyForm.append("focus", "both");
mightyForm.append("data_sensitivity", "tolerant");
const scanResponse = await fetch("https://gateway.trymighty.ai/v1/scan", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.MIGHTY_API_KEY}`,
},
body: mightyForm,
});
if (!scanResponse.ok) {
return Response.json(
{ error: "upload scan failed" },
{ status: scanResponse.status },
);
}
const scan = await scanResponse.json();
if (scan.action === "BLOCK") {
return Response.json(
{ error: "upload blocked", scan_id: scan.scan_id },
{ status: 400 },
);
}
if (scan.action === "WARN") {
await saveUploadForReview(file, scan);
return Response.json({ status: "review", scan_id: scan.scan_id });
}
await saveUploadForProcessing(file, scan);
return Response.json({ status: "accepted", scan_id: scan.scan_id });
}Routing Logic
async function saveUploadForReview(file: File, scan: { scan_id: string }) {
// Store in quarantine or a restricted bucket.
}
async function saveUploadForProcessing(file: File, scan: { scan_id: string }) {
// Store normally and enqueue OCR or extraction.
}Common Mistake
Do not call Mighty directly from the browser. The upload route should proxy the file so your API key stays server-side.
Do not store first and scan later unless the storage location is quarantine-only. The safer default is scan first, then store normally only after routing.
Acceptance Criteria
- Missing file returns
400. - Mighty BLOCK prevents normal storage.
- Mighty WARN stores to review or quarantine.
- Mighty ALLOW stores and continues processing.
- Logs include
scan_id.
Ready to scan real traffic?
Create an API key, keep it on your server, then wire Mighty into the workflow that handles untrusted material.
AI-Agent Prompt
Paste this into Cursor, Codex, Claude Code, or Windsurf.
Add Mighty to a Next.js App Router upload endpoint.
Requirements:
- Use runtime=nodejs.
- Parse request.formData().
- Require a file field.
- Forward the file to POST https://gateway.trymighty.ai/v1/scan with multipart form data.
- Use content_type=auto, scan_phase=input, mode=secure, focus=both, data_sensitivity=tolerant.
- Route BLOCK to reject or quarantine.
- Route WARN to quarantine and review.
- Route ALLOW to normal storage and processing.
- Store scan_id and scan_group_id with the upload record.
Acceptance criteria:
- API key only exists server-side.
- Tests cover missing file, ALLOW, WARN, BLOCK, and Mighty error status.