# Next.js Integration Guide Guide for integrating the IDP API with your Next.js frontend hosted on Vercel. ## Overview The IDP API provides a RESTful interface for document processing. Your Next.js app will: 1. Allow users to upload documents (PDF/images) 2. Send files to the API via POST request 3. Display extracted document data with confidence scores 4. Handle loading states and errors ## API Endpoints ### Base URL ``` https://YOUR_USERNAME-idp-api.hf.space ``` Replace `YOUR_USERNAME-idp-api` with your actual Space name. ### Endpoints #### GET /health Health check endpoint. **Response:** ```json { "status": "ok", "models_loaded": true, "version": "1.0.0" } ``` #### POST /process Process a document and extract structured data. **Request:** - Method: `POST` - Content-Type: `multipart/form-data` - Body: `file` (PDF or image file) - Optional params: - `adaptive_threshold`: boolean (default: false) - `page_number`: number (for PDFs, process specific page) **Response:** ```typescript { file_type: "image" | "pdf"; total_pages: number; processed_pages: number; filename: string; file_size_kb: number; pages: Array<{ document_type: "INVOICE" | "RECEIPT" | "FORM" | "OTHER"; classification_confidence: number; classification_probabilities: { INVOICE: number; RECEIPT: number; FORM: number; OTHER: number; }; fields: { [key: string]: { value: string; confidence: number; bbox?: [number, number, number, number]; source: "ner" | "regex"; normalized?: boolean; currency?: string; numeric_value?: number; }; }; processing_time: { preprocessing: number; ocr: number; classification: number; ner: number; postprocessing: number; total: number; }; raw_ocr_text: string; page_number?: number; }>; } ``` ## Implementation ### 1. Create TypeScript Types Create `types/idp.ts`: ```typescript export interface IDPField { value: string; confidence: number; bbox?: [number, number, number, number]; source: "ner" | "regex"; normalized?: boolean; currency?: string; numeric_value?: number; } export interface IDPPage { document_type: "INVOICE" | "RECEIPT" | "FORM" | "OTHER"; classification_confidence: number; classification_probabilities: { INVOICE: number; RECEIPT: number; FORM: number; OTHER: number; }; fields: Record; processing_time: { preprocessing: number; ocr: number; classification: number; ner: number; postprocessing: number; total: number; }; raw_ocr_text: string; page_number?: number; } export interface IDPResponse { file_type: "image" | "pdf"; total_pages: number; processed_pages: number; filename: string; file_size_kb: number; pages: IDPPage[]; } export interface HealthResponse { status: string; models_loaded: boolean; version: string; } ``` ### 2. Create API Client Create `lib/idp-api.ts`: ```typescript const API_BASE_URL = process.env.NEXT_PUBLIC_IDP_API_URL || 'https://YOUR_USERNAME-idp-api.hf.space'; export class IDPAPIError extends Error { constructor( message: string, public statusCode?: number, public details?: any ) { super(message); this.name = 'IDPAPIError'; } } export async function checkHealth(): Promise { try { const response = await fetch(`${API_BASE_URL}/health`); if (!response.ok) { throw new IDPAPIError( 'Health check failed', response.status ); } return await response.json(); } catch (error) { if (error instanceof IDPAPIError) throw error; throw new IDPAPIError('Network error: ' + (error as Error).message); } } export async function processDocument( file: File, options?: { adaptiveThreshold?: boolean; pageNumber?: number; onProgress?: (progress: number) => void; } ): Promise { const formData = new FormData(); formData.append('file', file); if (options?.adaptiveThreshold !== undefined) { formData.append('adaptive_threshold', String(options.adaptiveThreshold)); } if (options?.pageNumber !== undefined) { formData.append('page_number', String(options.pageNumber)); } try { const response = await fetch(`${API_BASE_URL}/process`, { method: 'POST', body: formData, }); if (!response.ok) { const error = await response.json().catch(() => ({})); throw new IDPAPIError( error.detail || 'Document processing failed', response.status, error ); } return await response.json(); } catch (error) { if (error instanceof IDPAPIError) throw error; throw new IDPAPIError('Network error: ' + (error as Error).message); } } ``` ### 3. Create Document Upload Component Create `components/DocumentUpload.tsx`: ```typescript 'use client'; import { useState, useCallback } from 'react'; import { processDocument, IDPResponse, IDPAPIError } from '@/lib/idp-api'; export default function DocumentUpload() { const [file, setFile] = useState(null); const [loading, setLoading] = useState(false); const [result, setResult] = useState(null); const [error, setError] = useState(null); const handleFileChange = (e: React.ChangeEvent) => { const selectedFile = e.target.files?.[0]; if (selectedFile) { setFile(selectedFile); setResult(null); setError(null); } }; const handleUpload = useCallback(async () => { if (!file) return; setLoading(true); setError(null); try { const response = await processDocument(file); setResult(response); } catch (err) { if (err instanceof IDPAPIError) { setError(err.message); } else { setError('An unexpected error occurred'); } } finally { setLoading(false); } }, [file]); return (

Document Processing

{/* File Upload */}
{/* Upload Button */} {/* Error Display */} {error && (

{error}

)} {/* Results Display */} {result && (

Results

{result.pages.map((page, idx) => (
Document Type:{' '} {page.document_type} Confidence: {(page.classification_confidence * 100).toFixed(1)}%

Extracted Fields:

{Object.entries(page.fields).map(([key, field]) => (
{key.replace(/_/g, ' ')}:
{field.value}
Confidence: {(field.confidence * 100).toFixed(1)}%
))}
Processing time: {page.processing_time.total.toFixed(2)}s
))}
)}
); } ``` ### 4. Environment Variables Create `.env.local`: ```env NEXT_PUBLIC_IDP_API_URL=https://YOUR_USERNAME-idp-api.hf.space ``` ### 5. Add to Your Page In `app/page.tsx`: ```typescript import DocumentUpload from '@/components/DocumentUpload'; export default function Home() { return (
); } ``` ## Advanced Features ### Health Check on Mount ```typescript useEffect(() => { checkHealth() .then(health => { if (!health.models_loaded) { setWarning('API is starting up, please wait...'); } }) .catch(() => setError('API is unavailable')); }, []); ``` ### File Preview ```typescript const [preview, setPreview] = useState(null); useEffect(() => { if (file && file.type.startsWith('image/')) { const url = URL.createObjectURL(file); setPreview(url); return () => URL.revokeObjectURL(url); } }, [file]); ``` ### Progress Indicator ```typescript const [progress, setProgress] = useState(0); await processDocument(file, { onProgress: (p) => setProgress(p) }); ``` ## Deployment to Vercel 1. Push your Next.js code to GitHub 2. Go to [Vercel](https://vercel.com) 3. Import your repository 4. Add environment variable: - `NEXT_PUBLIC_IDP_API_URL`: Your HF Space URL 5. Deploy ## Error Handling Common errors and solutions: | Error | Cause | Solution | |-------|-------|----------| | CORS error | API not allowing origin | Check CORS config in `api_server.py` | | 503 Service Unavailable | Models not loaded | Wait for API to finish starting | | 413 Payload Too Large | File > 10MB | Compress file or increase limit | | 400 Unsupported file type | Wrong file format | Check file extension | ## Performance Tips - **Debounce uploads**: Prevent rapid re-uploads - **Show progress**: Use loading states for better UX - **Validate files**: Check size/type before uploading - **Cache results**: Store processed documents if re-processing same file - **Error boundaries**: Wrap component in error boundary ## Example: Full Featured Component See `components/AdvancedDocumentUpload.tsx` in the example repository for a production-ready component with: - Drag & drop support - File preview - Progress tracking - Error handling - Result visualization - Download processed JSON ## Support For integration issues: - Check browser console for errors - Test API directly with `curl` first - Verify CORS configuration - Check network tab in DevTools