@ninjadoc-ai/sdk
Version:
TypeScript SDK for document processing with zero-friction framework adapters. Features intelligent coordinate handling, semantic regions, React UI overlays, and automatic route detection for Remix and Next.js. Transform raw bounding boxes into interactive
360 lines (250 loc) • 11.3 kB
Markdown
# @ninjadoc-ai/sdk
## Description
Ninjadoc AI is the first Document Q&A Platform that lets you ask questions like "What's the total?" to build extraction schemas, then use our API to get structured data with exact coordinates as proof. No training required.
This SDK provides TypeScript/JavaScript integration for document processing with a zero-boilerplate API handler, intelligent coordinate handling, and React UI overlays.
## Features
- **Zero-Boilerplate BFF**: Use our `createApiHandler` to create a secure API proxy in your backend with a single function call. No manual endpoint creation needed.
- **Natural Language Q&A**: Ask questions like "What's the total?" or use technical field names.
- **Coordinate Precision**: Get exact bounding box coordinates for every extracted field.
- **React Integration**: Built-in `HighlightOverlay` component for interactive document viewing.
- **Developer-Friendly**: Clean, type-safe clients for both browser and server.
- **Simple Schema Setup**: Build extraction schemas by asking natural language questions, no training required.
## Installation
```bash
npm install @ninjadoc-ai/sdk
```
## Framework Adapters: Zero-Friction Integration
The SDK now includes framework-specific adapters that reduce integration friction to almost zero. These adapters handle framework-specific patterns, environment variables, and error handling automatically.
### Remix Adapter
For Remix applications, use the framework adapter for seamless integration:
```typescript
// app/routes/api.ninjadoc.$.tsx
import { createRemixApiRoute } from '@ninjadoc-ai/sdk/frameworks/remix';
// Zero-friction setup - API key automatically read from NINJADOC_API_KEY
const { loader, action } = createRemixApiRoute();
export { loader, action };
```
That's it! The adapter automatically:
- Reads your API key from environment variables
- Handles Remix's loader/action pattern
- Provides framework-appropriate error responses
- Manages request/response conversion
### Zero-Friction Browser Client
The browser client now automatically detects your route setup:
```typescript
// No configuration needed - works out of the box!
const client = createBrowserClient();
// The client automatically:
// 1. Tries framework adapter routes (/api/ninjadoc/*)
// 2. Falls back to legacy routes (/api/extract, /api/jobs/*/status)
// 3. Remembers the working pattern for future requests
```
This means developers can just call `createBrowserClient()` with no parameters and it will work with both new framework adapters and existing legacy setups!
### Next.js Adapter
For Next.js App Router applications:
```typescript
// app/api/ninjadoc/[...path]/route.ts
import { createNextApiRoute } from '@ninjadoc-ai/sdk/frameworks/next';
// Zero-friction setup - API key automatically read from NINJADOC_API_KEY
const handler = createNextApiRoute();
export { handler as GET, handler as POST, handler as PUT, handler as DELETE };
```
The Next.js adapter automatically:
- Reads your API key from environment variables
- Handles NextRequest/NextResponse objects
- Provides framework-appropriate error responses
- Manages dynamic route parameters
### Advanced Configuration
Both adapters support advanced configuration:
```typescript
const { loader, action } = createRemixApiRoute({
apiKey: 'your-explicit-api-key', // or omit to use env var
apiKeyEnv: 'CUSTOM_ENV_VAR', // default: 'NINJADOC_API_KEY'
onError: (error, context) => {
// Custom error handling
return new Response(JSON.stringify({ error: error.message }), {
status: 500
});
}
});
```
## Quick Start: Zero-Friction Integration
The easiest way to get started is using our framework adapters that handle everything automatically.
### 1. Add Framework Adapter Route
**For Remix:**
```typescript
// app/routes.ts - Add this line:
route("/api/ninjadoc/*", "routes/api.ninjadoc.$.tsx"),
// app/routes/api.ninjadoc.$.tsx
import { createRemixApiRoute } from '@ninjadoc-ai/sdk/frameworks/remix';
const { loader, action } = createRemixApiRoute();
export { loader, action };
```
**For Next.js:**
```typescript
// app/api/ninjadoc/[...path]/route.ts
import { createNextApiRoute } from '@ninjadoc-ai/sdk/frameworks/next';
const handler = createNextApiRoute();
export { handler as GET, handler as POST, handler as PUT, handler as DELETE };
```
### 2. Use the Client (Zero Configuration)
```typescript
// Your React Component
import { createBrowserClient } from '@ninjadoc-ai/sdk';
import { useState } from 'react';
function MyUploader() {
const [jobId, setJobId] = useState<string | null>(null);
const [document, setDocument] = useState<any | null>(null);
const handleFileChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (!file) return;
// Zero configuration - automatically detects your route setup
const client = createBrowserClient();
// 1. Extract document via your app's API handler
const job = await client.extractDocument(file, 'your-processor-uuid');
setJobId(job.id);
// 2. Wait for completion and get the processed document
const processedDoc = await client.processDocument(job.id);
setDocument(processedDoc);
// 3. Find specific data
const totalRegion = client.findRegion(processedDoc.regions, 'invoice_total');
console.log(totalRegion?.metadata?.extractedText);
};
return <input type="file" onChange={handleFileChange} />;
}
```
**That's it!** The framework adapter automatically:
- Reads your `NINJADOC_API_KEY` environment variable
- Handles framework-specific request/response patterns
- Provides appropriate error handling
- The browser client auto-detects your route setup
### Manual Setup (Legacy)
If you prefer manual control, you can still use the original approach:
## React Integration
Use the `<HighlightOverlay />` component to visualize the extracted data on top of your document viewer.
```typescript
import { createBrowserClient } from '@ninjadoc-ai/sdk';
import { HighlightOverlay } from '@ninjadoc-ai/sdk/react';
import { useState, useEffect } from 'react';
import type { ProcessedDocument } from '@ninjadoc-ai/sdk';
function DocumentViewer({ jobId }: { jobId: string }) {
const [document, setDocument] = useState<ProcessedDocument | null>(null);
// Zero configuration - automatically detects your route setup
const client = createBrowserClient();
useEffect(() => {
// The processDocument method polls for job completion and returns
// the final document with regions and page dimensions.
client.processDocument(jobId).then(setDocument);
}, [jobId]);
if (!document) return <div>Loading...</div>;
return (
<div style={{ position: 'relative' }}>
{/* Your PDF viewer here */}
<HighlightOverlay
document={document}
containerSize={{ width: 800, height: 600 }}
onRegionClick={(region) => {
console.log(`Clicked ${region.label}:`, region.metadata?.extractedText);
}}
/>
</div>
);
}
```
## API Reference
### Browser Client (`@ninjadoc-ai/sdk`)
The browser client is designed to talk to your application's backend (the API handler), not directly to the Ninjadoc AI API. It now features automatic route detection for zero-configuration setup.
#### `createBrowserClient(options)`
```typescript
const client = createBrowserClient({
baseUrl?: string; // Optional: auto-detects if not provided
credentials?: RequestCredentials; // default: 'include'
autoDetectRoutes?: boolean; // default: true - automatically detects route patterns
});
```
**Zero-Configuration Usage:**
```typescript
// Automatically detects route patterns with zero configuration
const client = createBrowserClient();
// Automatically detects:
// - Framework adapter routes (/api/ninjadoc/*)
// - Legacy routes (/api/extract, /api/jobs/*/status)
// - Remembers working pattern for performance
```
#### `client.extractDocument(file, processorId)`
Creates a new extraction job by POSTing to your `/api/ninjadoc/extract` endpoint.
#### `client.getJobStatus(jobId)`
Gets the status of a job by GETting from your `/api/ninjadoc/jobs/{jobId}/status` endpoint.
#### `client.processDocument(jobId)`
A convenience method that polls `getJobStatus` until the job is complete, then returns the fully processed document, including regions and page dimensions.
#### `client.getRegions(jobStatus)`
Transforms a completed job status object into an array of `Region` objects.
#### `client.findRegion(regions, label)`
Finds a specific region by its label from an array of regions.
### Server Handler (`@ninjadoc-ai/sdk/server`)
The server package contains tools for your backend.
#### `createApiHandler(options)`
Creates a framework-agnostic request handler that securely proxies calls to the Ninjadoc AI API.
```typescript
import { createApiHandler } from '@ninjadoc-ai/sdk/server';
const handler = createApiHandler({
apiKey: string; // Your secret Ninjadoc AI API key
});
// Usage:
// await handler(request, subpath);
```
#### `createServerClient(options)`
If you need more control and want to build your own backend logic without the pre-built handler, you can use the `createServerClient` directly.
```typescript
import { createServerClient } from '@ninjadoc-ai/sdk/server';
const serverClient = createServerClient({
apiKey: string; // Your secret Ninjadoc AI API key
});
// Now you can call methods like serverClient.extractDocument(...)
```
### HighlightOverlay Component
The props for the React component remain the same.
```typescript
interface HighlightOverlayProps {
document: ProcessedDocument;
containerSize: { width: number; height: number };
onRegionClick?: (region: Region) => void;
activeRegionId?: string;
}
```
## Framework Adapters API Reference
### Remix Adapter
#### `createRemixApiRoute(options?)`
Creates a complete Remix API route with loader and action functions.
```typescript
import { createRemixApiRoute } from '@ninjadoc-ai/sdk/frameworks/remix';
const { loader, action } = createRemixApiRoute({
apiKey?: string; // Optional: API key (auto-read from env if omitted)
apiKeyEnv?: string; // Optional: Env var name (default: 'NINJADOC_API_KEY')
onError?: (error, context) => Response; // Optional: Custom error handler
});
```
#### `createRemixHandler(options?)`
Creates individual loader and action handlers with more control.
```typescript
import { createRemixHandler } from '@ninjadoc-ai/sdk/frameworks/remix';
const { loader, action } = createRemixHandler(options);
```
### Next.js Adapter
#### `createNextApiRoute(options?)`
Creates a Next.js API route handler for all HTTP methods.
```typescript
import { createNextApiRoute } from '@ninjadoc-ai/sdk/frameworks/next';
const handler = createNextApiRoute({
apiKey?: string; // Optional: API key (auto-read from env if omitted)
apiKeyEnv?: string; // Optional: Env var name (default: 'NINJADOC_API_KEY')
onError?: (error, context) => NextResponse; // Optional: Custom error handler
});
export { handler as GET, handler as POST, handler as PUT, handler as DELETE };
```
#### `createNextHandler(options?)`
Creates a Next.js handler with more control.
```typescript
import { createNextHandler } from '@ninjadoc-ai/sdk/frameworks/next';
const handler = createNextHandler(options);
```