PouchDB adapter using HTTP for remote CouchDB connections and database operations
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
File attachment management with support for binary data, proper content types, and efficient streaming for documents stored in remote CouchDB databases.
Retrieves a document attachment by its ID and returns the binary data.
/**
* Get a document attachment by ID
* @param docId - Document ID containing the attachment
* @param attachmentId - Attachment ID within the document
* @param opts - Retrieval options including revision
* @param callback - Callback function receiving attachment data
*/
api.getAttachment(docId, attachmentId, opts, callback): void;Usage Examples:
// Get attachment from latest document revision
db.getAttachment('user:john', 'profile-photo.jpg', (err, blob) => {
if (err) {
console.error('Attachment not found:', err);
return;
}
console.log('Attachment size:', blob.size);
console.log('Content type:', blob.type);
// In browser - create download link
if (typeof window !== 'undefined') {
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'profile-photo.jpg';
link.click();
}
// In Node.js - save to file
if (typeof process !== 'undefined' && !process.browser) {
const fs = require('fs');
fs.writeFileSync('downloaded-photo.jpg', blob);
}
});
// Get attachment from specific revision
db.getAttachment('user:john', 'profile-photo.jpg', {
rev: '2-abc123'
}, (err, blob) => {
if (err) {
console.error('Attachment not found in revision:', err);
return;
}
console.log('Retrieved attachment from specific revision');
});Adds a new attachment to a document or updates an existing attachment.
/**
* Add or update a document attachment
* @param docId - Document ID to attach to
* @param attachmentId - Attachment ID within the document
* @param rev - Document revision (optional for new attachments)
* @param blob - Attachment data (Blob, Buffer, or base64 string)
* @param type - MIME content type
* @param callback - Callback function receiving save result
*/
api.putAttachment(docId, attachmentId, rev, blob, type, callback): void;Usage Examples:
// Add attachment to existing document
db.get('user:john', (err, doc) => {
if (err) {
console.error('Document not found:', err);
return;
}
// In browser - from file input
const fileInput = document.getElementById('photoInput');
const file = fileInput.files[0];
db.putAttachment('user:john', 'profile-photo.jpg', doc._rev, file, file.type, (err, result) => {
if (err) {
console.error('Attachment upload failed:', err);
return;
}
console.log('Attachment saved, new revision:', result.rev);
});
});
// Add attachment from base64 string
const base64Image = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==';
db.get('user:jane', (err, doc) => {
if (err) {
console.error('Document not found:', err);
return;
}
db.putAttachment('user:jane', 'avatar.png', doc._rev, base64Image, 'image/png', (err, result) => {
if (err) {
console.error('Attachment upload failed:', err);
return;
}
console.log('Base64 attachment saved:', result.rev);
});
});
// Add attachment to new document (without revision)
const textData = new Blob(['Hello, world!'], { type: 'text/plain' });
db.putAttachment('note:greeting', 'content.txt', textData, 'text/plain', (err, result) => {
if (err) {
console.error('Attachment upload failed:', err);
return;
}
console.log('Attachment saved to new document:', result.rev);
});Removes an attachment from a document.
/**
* Remove an attachment from a document
* @param docId - Document ID containing the attachment
* @param attachmentId - Attachment ID to remove
* @param rev - Current document revision
* @param callback - Callback function receiving deletion result
*/
api.removeAttachment(docId, attachmentId, rev, callback): void;Usage Examples:
// Remove attachment
db.get('user:john', (err, doc) => {
if (err) {
console.error('Document not found:', err);
return;
}
// Check if attachment exists
if (!doc._attachments || !doc._attachments['profile-photo.jpg']) {
console.log('Attachment not found in document');
return;
}
db.removeAttachment('user:john', 'profile-photo.jpg', doc._rev, (err, result) => {
if (err) {
console.error('Attachment removal failed:', err);
return;
}
console.log('Attachment removed, new revision:', result.rev);
});
});
// Remove multiple attachments
db.get('document:with-attachments', (err, doc) => {
if (err) {
console.error('Document not found:', err);
return;
}
const attachmentIds = Object.keys(doc._attachments || {});
// Remove attachments sequentially
let currentRev = doc._rev;
const removeNext = (index) => {
if (index >= attachmentIds.length) {
console.log('All attachments removed');
return;
}
db.removeAttachment(doc._id, attachmentIds[index], currentRev, (err, result) => {
if (err) {
console.error(`Failed to remove ${attachmentIds[index]}:`, err);
return;
}
currentRev = result.rev;
console.log(`Removed ${attachmentIds[index]}`);
removeNext(index + 1);
});
};
removeNext(0);
});// Handle file input in browsers
function handleFileUpload(event) {
const file = event.target.files[0];
if (!file) return;
const docId = 'upload:' + Date.now();
db.putAttachment(docId, file.name, file, file.type, (err, result) => {
if (err) {
console.error('Upload failed:', err);
return;
}
console.log('File uploaded successfully:', result.rev);
});
}
// Create download link for attachment
function downloadAttachment(docId, attachmentId) {
db.getAttachment(docId, attachmentId, (err, blob) => {
if (err) {
console.error('Download failed:', err);
return;
}
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = attachmentId;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
});
}const fs = require('fs');
const path = require('path');
// Upload file from filesystem
function uploadFile(filePath, docId) {
const fileName = path.basename(filePath);
const fileBuffer = fs.readFileSync(filePath);
// Determine content type based on extension
const ext = path.extname(fileName).toLowerCase();
const contentType = {
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.png': 'image/png',
'.pdf': 'application/pdf',
'.txt': 'text/plain'
}[ext] || 'application/octet-stream';
db.putAttachment(docId, fileName, fileBuffer, contentType, (err, result) => {
if (err) {
console.error('File upload failed:', err);
return;
}
console.log('File uploaded:', result.rev);
});
}
// Save attachment to filesystem
function saveAttachment(docId, attachmentId, outputPath) {
db.getAttachment(docId, attachmentId, (err, buffer) => {
if (err) {
console.error('Download failed:', err);
return;
}
fs.writeFileSync(outputPath, buffer);
console.log('Attachment saved to:', outputPath);
});
}// Attachment data types
type AttachmentData = Blob | Buffer | string;
// Get attachment options
interface GetAttachmentOptions {
rev?: string;
}
// Attachment info in document
interface AttachmentInfo {
content_type: string;
revpos: number;
digest: string;
length: number;
stub: boolean;
data?: string; // base64 data when fetched
}
// Document with attachments
interface DocumentWithAttachments extends PouchDoc {
_attachments?: {
[attachmentId: string]: AttachmentInfo;
};
}
// Attachment operation result
interface AttachmentResult {
ok: boolean;
id: string;
rev: string;
}
// Content type constants
type ContentType =
| 'image/jpeg'
| 'image/png'
| 'image/gif'
| 'image/webp'
| 'application/pdf'
| 'text/plain'
| 'text/html'
| 'application/json'
| 'application/octet-stream'
| string;Install with Tessl CLI
npx tessl i tessl/npm-pouchdb-adapter-http