@development-team/bg-remover
v1.2.0
Published
Express middleware for background removal and OCR text extraction with HTTP multipart upload and real-time SSE streaming
Downloads
112
Maintainers
Readme
@development-team/bg-remover
A production-ready Express middleware suite for AI-powered image background removal and real-time OCR text extraction.
v1.2.0 — Image is always sent as normal
multipart/form-data(PNG/JPG). No WebSocket binary conversion required. Output available as a complete HTTP response or a real-time SSE stream.
Features
| Feature | Description |
|---|---|
| Background Removal | Remove image backgrounds via HTTP multipart upload |
| OCR — Buffered | Upload image as PNG/JPG, get complete text result in req.ocrResult |
| OCR — Live Stream | Upload image as PNG/JPG, receive OCR chunks in real-time via SSE |
| Fail-Safe | Errors never crash your middleware chain — always calls next() |
| Retry Logic | Configurable retry attempts; smart — skips retry on 4xx errors |
| Backward Compat | wsUrl still accepted — auto-converted to https:// internally |
Requirements
- Node.js
>= 16.0.0 multer(peer dependency for file uploads)
Installation
npm install @development-team/bg-remover multerTable of Contents
- Background Removal
- OCR — Buffered (Complete Result)
- OCR — Live SSE Stream
- Options Reference
- Request Object Properties
- Frontend Integration
- Error Handling
- Environment Variables
- Changelog
1. Background Removal
removeBgMiddleware(options)
Uploads the image to a background removal REST API and attaches the result to req.processedImage. Always calls next() — even on failure.
Basic Example
const express = require('express');
const multer = require('multer');
const { removeBgMiddleware } = require('@development-team/bg-remover');
const app = express();
const upload = multer({ storage: multer.memoryStorage() });
app.post(
'/remove-background',
upload.single('image'),
removeBgMiddleware({ timeout: 15000, retries: 2 }),
(req, res) => {
if (req.bgError) {
return res.status(500).json({ error: req.bgError.message });
}
// req.processedImage.buffer → Buffer (transparent PNG)
// req.processedImage.mimetype → 'image/png'
res.set('Content-Type', req.processedImage.mimetype);
res.send(req.processedImage.buffer);
}
);Replace Original File In-Place
app.post(
'/upload',
upload.single('image'),
removeBgMiddleware({ replaceOriginal: true }),
(req, res) => {
if (req.bgError) return res.status(500).json({ error: 'Processing failed' });
// req.file.buffer → processed image
// req.file.mimetype → 'image/png'
res.json({ size: req.file.size });
}
);Options
| Option | Type | Default | Description |
|---|---|---|---|
| apiUrl | string | (built-in) | Full URL of the background removal endpoint |
| timeout | number | 10000 | Request timeout in ms |
| retries | number | 2 | Retry attempts on failure |
| replaceOriginal | boolean | false | Overwrite req.file with the processed result |
| fieldName | string | "file" | Form-data field name sent to the API |
2. OCR — Buffered (Complete Result)
ocrMiddleware(options)
Uploads the image as normal multipart/form-data (PNG/JPG) to POST /api/ocr, waits for the complete result, then attaches it to req.ocrResult and calls next().
Use this when you want a standard request/response flow and don't need real-time streaming.
Basic Example
const express = require('express');
const multer = require('multer');
const { ocrMiddleware } = require('@development-team/bg-remover');
const app = express();
const upload = multer({ storage: multer.memoryStorage() });
app.post(
'/ocr',
upload.single('image'), // accepts PNG, JPG, WEBP, etc.
ocrMiddleware({
apiUrl: process.env.OCR_API_URL, // e.g. "https://your-server.hf.space"
timeout: 30000,
retries: 1
}),
(req, res) => {
if (req.ocrError) {
return res.status(500).json({ error: req.ocrError.message });
}
res.json({
full_text: req.ocrResult.full_text, // complete extracted text
total_lines: req.ocrResult.total_lines,
lines: req.ocrResult.lines // [{text, confidence}, ...]
});
}
);req.ocrResult Shape
{
"full_text": "Paracetamol 500mg\nTake 2 times a day.",
"total_lines": 2,
"lines": [
{ "text": "Paracetamol 500mg", "confidence": 0.985 },
{ "text": "Take 2 times a day.", "confidence": 0.912 }
]
}Options
| Option | Type | Default | Description |
|---|---|---|---|
| apiUrl | string | OCR_API_URL env var | HTTP(S) base URL of the OCR server (e.g. https://your-space.hf.space) |
| wsUrl | string | OCR_WS_URL env var | Backward compat — auto-converted to https:// |
| timeout | number | 30000 | Request timeout in ms |
| retries | number | 1 | Retry attempts on failure (skipped on 4xx errors) |
| fieldName | string | "file" | Form-data field name sent to the API |
Note:
ocrRestMiddlewareis an alias ofocrMiddleware— both are identical.
3. OCR — Live SSE Stream
ocrStreamHandler(options)
Uploads the image as normal multipart/form-data (PNG/JPG) to POST /api/ocr/stream and pipes the OCR results back to the client as Server-Sent Events in real-time — line by line, as they are extracted.
This is a route handler, not a middleware — it sends the full HTTP response itself. Do not add another handler after it.
Basic Example
const express = require('express');
const multer = require('multer');
const { ocrStreamHandler } = require('@development-team/bg-remover');
const app = express();
const upload = multer({ storage: multer.memoryStorage() });
// This single line is the complete route — no extra handler needed.
app.post(
'/ocr/stream',
upload.single('image'), // accepts PNG, JPG, WEBP, etc.
ocrStreamHandler({
apiUrl: process.env.OCR_API_URL,
timeout: 30000
})
);SSE Event Stream Format
The client receives a text/event-stream response with these events:
data: {"event":"status","data":{"message":"📷 Image received","stage":"received"}}
data: {"event":"status","data":{"message":"🔧 Processing image…","stage":"preprocessing"}}
data: {"event":"status","data":{"message":"📝 Reading text…","stage":"ocr_started"}}
data: {"event":"ocr_chunk","data":"Paracetamol 500mg","index":1,"confidence":0.985,"bbox":[[...]]}
data: {"event":"ocr_chunk","data":"Take 2 times a day.","index":2,"confidence":0.912,"bbox":[[...]]}
data: {"event":"ocr_complete","data":{"message":"✅ Done","total_lines":2}}| Event | Key Fields | Description |
|---|---|---|
| status | data.message, data.stage | Processing stage update |
| ocr_chunk | data (text), index, confidence, bbox | One detected line, streamed live |
| ocr_complete | data.total_lines | All lines sent — connection closes |
| error | message | Error from OCR server or timeout |
Options
| Option | Type | Default | Description |
|---|---|---|---|
| apiUrl | string | OCR_API_URL env var | HTTP(S) base URL of the OCR server |
| wsUrl | string | OCR_WS_URL env var | Backward compat — auto-converted to https:// |
| timeout | number | 30000 | Request timeout in ms |
| fieldName | string | "file" | Form-data field name sent to the API |
4. Options Reference (All Middlewares)
removeBgMiddleware(options)
| Option | Type | Default | Description |
|---|---|---|---|
| apiUrl | string | (internal) | Full URL of the remove-bg endpoint |
| timeout | number | 10000 | HTTP request timeout (ms) |
| retries | number | 2 | Retry attempts on failure |
| replaceOriginal | boolean | false | Overwrite req.file with processed result |
| fieldName | string | "file" | Form-data field name sent to the API |
ocrMiddleware(options) / ocrRestMiddleware(options)
| Option | Type | Default | Description |
|---|---|---|---|
| apiUrl | string | process.env.OCR_API_URL | HTTP(S) base URL of the OCR server |
| wsUrl | string | process.env.OCR_WS_URL | Backward compat — auto-converted to https:// |
| timeout | number | 30000 | Request timeout (ms) |
| retries | number | 1 | Retry attempts (skipped on 4xx errors) |
| fieldName | string | "file" | Form-data field name |
ocrStreamHandler(options)
| Option | Type | Default | Description |
|---|---|---|---|
| apiUrl | string | process.env.OCR_API_URL | HTTP(S) base URL of the OCR server |
| wsUrl | string | process.env.OCR_WS_URL | Backward compat — auto-converted to https:// |
| timeout | number | 30000 | Request timeout (ms) |
| fieldName | string | "file" | Form-data field name |
5. Request Object Properties
removeBgMiddleware
| Property | Type | Set When |
|---|---|---|
| req.processedImage.buffer | Buffer | Success |
| req.processedImage.mimetype | string | Success |
| req.bgError | Error | All retries failed |
ocrMiddleware / ocrRestMiddleware
| Property | Type | Set When |
|---|---|---|
| req.ocrResult.full_text | string | Success |
| req.ocrResult.lines | Array<{text, confidence}> | Success |
| req.ocrResult.total_lines | number | Success |
| req.ocrChunks | Array<{text, confidence}> | Success (shallow copy of lines) |
| req.ocrError | Error | All retries failed |
All middlewares silently skip and call
next()ifreq.fileis missing or the file is not an image.
6. Frontend Integration
OCR Buffered — Fetch API
const extractText = async (file) => {
const formData = new FormData();
formData.append('image', file); // PNG, JPG, WEBP — any image format
const res = await fetch('/ocr', { method: 'POST', body: formData });
const data = await res.json();
console.log('Full text:', data.full_text);
data.lines.forEach(line => {
console.log(`"${line.text}" (${(line.confidence * 100).toFixed(1)}%)`);
});
};OCR Live Stream — fetch with ReadableStream
const streamOcr = async (file, onChunk, onComplete, onError) => {
const formData = new FormData();
formData.append('image', file); // PNG, JPG, WEBP — any image format
const res = await fetch('/ocr/stream', { method: 'POST', body: formData });
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const frames = buffer.split('\n\n');
buffer = frames.pop(); // keep incomplete last frame
for (const frame of frames) {
if (!frame.startsWith('data: ')) continue;
const event = JSON.parse(frame.slice(6));
if (event.event === 'ocr_chunk') {
onChunk(event.data, event.confidence); // event.data = extracted text
} else if (event.event === 'ocr_complete') {
onComplete(event.data.total_lines);
} else if (event.event === 'error') {
onError(event.message);
}
}
}
};
// Usage
streamOcr(
imageFile,
(text, conf) => console.log(`Line: "${text}" (${(conf * 100).toFixed(1)}%)`),
(total) => console.log(`Done — ${total} lines extracted`),
(err) => console.error('OCR error:', err)
);React Hook — Live OCR
import { useState } from 'react';
function useOcrStream(endpoint = '/ocr/stream') {
const [lines, setLines] = useState([]);
const [done, setDone] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const runOcr = async (file) => {
setLines([]);
setDone(false);
setError(null);
setLoading(true);
const formData = new FormData();
formData.append('image', file);
const res = await fetch(endpoint, { method: 'POST', body: formData });
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done: streamDone, value } = await reader.read();
if (streamDone) break;
buffer += decoder.decode(value, { stream: true });
const frames = buffer.split('\n\n');
buffer = frames.pop();
for (const frame of frames) {
if (!frame.startsWith('data: ')) continue;
const event = JSON.parse(frame.slice(6));
if (event.event === 'ocr_chunk') {
setLines((prev) => [...prev, { text: event.data, confidence: event.confidence }]);
} else if (event.event === 'ocr_complete') {
setDone(true);
setLoading(false);
} else if (event.event === 'error') {
setError(event.message);
setLoading(false);
}
}
}
};
return { lines, done, loading, error, runOcr };
}
// Usage in component:
// const { lines, loading, runOcr } = useOcrStream();
// <input type="file" onChange={e => runOcr(e.target.files[0])} />
// {lines.map((l, i) => <p key={i}>{l.text}</p>)}7. Error Handling
All middlewares follow a fail-safe design — they never throw or crash your server.
Background Removal
removeBgMiddleware({ retries: 2 }),
(req, res) => {
if (req.bgError) {
return res.status(502).json({ error: 'Image processing service unavailable' });
}
// proceed normally
}OCR Buffered
ocrMiddleware({ retries: 1 }),
(req, res) => {
if (req.ocrError) {
// Includes server error detail when available, e.g.:
// "OCR failed after 1 attempt(s). Last error: Request failed with status code 422 (File too large)"
return res.status(502).json({ error: req.ocrError.message });
}
// proceed normally
}OCR Stream
The stream handler automatically sends an SSE error event and closes the connection. Always listen for it on the client:
if (event.event === 'error') {
console.error('OCR error:', event.message);
// show error UI, stop spinner, etc.
}8. Environment Variables
| Variable | Used By | Description |
|---|---|---|
| OCR_API_URL | ocrMiddleware, ocrStreamHandler | HTTP(S) base URL of the OCR server. Recommended. |
| OCR_WS_URL | ocrMiddleware, ocrStreamHandler | Legacy: WebSocket URL — auto-converted to https:// |
Set in your .env:
# Recommended (v1.2.0+)
OCR_API_URL=https://your-ocr-server.hf.space
# Or use the legacy variable (still works — auto-converted to https://)
OCR_WS_URL=wss://your-ocr-server.hf.spaceIf both are set,
OCR_API_URLtakes precedence.
9. Changelog
v1.2.0
- Breaking (internal): OCR transport changed from WebSocket binary to HTTP multipart/form-data
- Images are now sent as normal PNG/JPG files — no binary or base64 conversion needed
ocrMiddleware→ usesPOST /api/ocr(complete JSON result)ocrStreamHandler→ usesPOST /api/ocr/stream(real-time SSE chunks)
- Added
apiUrloption to both OCR functions (direct HTTP URL) - Added
ocrRestMiddlewareexport (alias ofocrMiddleware) - Added
fieldNameoption toocrStreamHandler - Added
res.flush()calls for immediate SSE event delivery - Added smart retry: 4xx errors are not retried (only 5xx)
- Added upstream error detail in
req.ocrError.message - Changed
req.ocrChunksis now a shallow copy (not an alias) ofreq.ocrResult.lines - Removed
ws(WebSocket) dependency — no longer needed - Updated Node.js requirement:
>= 16.0.0(was>= 14.0.0) - Backward compat:
wsUrlstill accepted — auto-converted tohttps:///http://
v1.1.0
- Added
ocrMiddleware— buffered OCR over WebSocket - Added
ocrStreamHandler— live OCR via Server-Sent Events - Added
wsdependency for WebSocket client - Added
OCR_WS_URLenvironment variable support
v1.0.1
- Made Hugging Face endpoint internal
- Package renamed to
@development-team/bg-remover
v1.0.0
- Initial release:
removeBgMiddlewarefor background removal
License
MIT © Development Team
