or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

changes-monitoring.mddatabase-management.mddocument-operations.mdindex.mdplugins-adapters.mdreplication-sync.md

plugins-adapters.mddocs/

0

# Plugins and Adapters

1

2

Extensible architecture for adding functionality and storage backends to PouchDB. The plugin system allows extending PouchDB with additional features, while adapters provide different storage mechanisms for various environments.

3

4

## Capabilities

5

6

### Plugin System

7

8

Add functionality to PouchDB through plugins that extend the core API.

9

10

```javascript { .api }

11

/**

12

* Add a plugin to PouchDB

13

* @param plugin - Plugin object or function

14

* @returns PouchDB constructor with plugin installed

15

*/

16

PouchDB.plugin(plugin: Plugin): typeof PouchDB;

17

18

// Plugin can be a function that receives PouchDB constructor

19

type PluginFunction = (PouchDB: typeof PouchDB) => void;

20

21

// Plugin can be an object with methods to add to prototype

22

interface PluginObject {

23

[methodName: string]: Function;

24

}

25

26

type Plugin = PluginFunction | PluginObject;

27

```

28

29

**Usage Examples:**

30

31

```javascript

32

// Function-based plugin

33

function myPlugin(PouchDB) {

34

// Add static method

35

PouchDB.customStaticMethod = function() {

36

console.log('Custom static method called');

37

};

38

39

// Add instance method

40

PouchDB.prototype.customMethod = function() {

41

console.log('Custom method called on', this.name);

42

return this.info();

43

};

44

}

45

46

// Install the plugin

47

PouchDB.plugin(myPlugin);

48

49

// Use the plugin

50

PouchDB.customStaticMethod();

51

const db = new PouchDB('test');

52

await db.customMethod();

53

54

// Object-based plugin

55

const objectPlugin = {

56

customQuery: function(selector) {

57

// Custom query implementation

58

return this.allDocs({ include_docs: true })

59

.then(result => result.rows.filter(row =>

60

Object.keys(selector).every(key =>

61

row.doc[key] === selector[key]

62

)

63

));

64

},

65

66

bulkUpdate: function(updates) {

67

// Bulk update implementation

68

return Promise.all(

69

Object.keys(updates).map(docId =>

70

this.get(docId).then(doc =>

71

this.put({ ...doc, ...updates[docId] })

72

)

73

)

74

);

75

}

76

};

77

78

PouchDB.plugin(objectPlugin);

79

80

// Use object plugin methods

81

const results = await db.customQuery({ type: 'user', active: true });

82

await db.bulkUpdate({

83

'user1': { lastLogin: new Date().toISOString() },

84

'user2': { status: 'inactive' }

85

});

86

```

87

88

### Adapter Management

89

90

Register and configure different storage adapters for various environments.

91

92

```javascript { .api }

93

/**

94

* Register a new adapter

95

* @param name - Adapter name

96

* @param adapter - Adapter implementation

97

* @param addToPreferred - Whether to add to preferred adapters list

98

*/

99

PouchDB.adapter(name: string, adapter: Adapter, addToPreferred?: boolean): void;

100

101

/**

102

* Object containing all registered adapters

103

*/

104

PouchDB.adapters: { [name: string]: Adapter };

105

106

/**

107

* Array of preferred adapters in order of preference

108

*/

109

PouchDB.preferredAdapters: string[];

110

```

111

112

**Usage Examples:**

113

114

```javascript

115

// Check available adapters

116

console.log('Available adapters:', Object.keys(PouchDB.adapters));

117

console.log('Preferred adapters:', PouchDB.preferredAdapters);

118

119

// Register a custom adapter

120

const customAdapter = {

121

valid() {

122

return true;

123

},

124

125

async _info(callback) {

126

// Implementation

127

},

128

129

async _get(id, opts, callback) {

130

// Implementation

131

},

132

133

async _put(doc, opts, callback) {

134

// Implementation

135

}

136

137

// ... other required methods

138

};

139

140

PouchDB.adapter('custom', customAdapter, true);

141

142

// Use the custom adapter

143

const db = new PouchDB('test', { adapter: 'custom' });

144

```

145

146

### Built-in Adapters

147

148

PouchDB comes with several built-in adapters for different environments.

149

150

```javascript { .api }

151

// Common built-in adapters:

152

// - 'idb': IndexedDB adapter for browsers

153

// - 'leveldb': LevelDB adapter for Node.js

154

// - 'http'/'https': HTTP adapter for remote CouchDB

155

// - 'memory': In-memory adapter (via plugin)

156

// - 'websql': WebSQL adapter for older browsers (deprecated)

157

```

158

159

**Usage Examples:**

160

161

```javascript

162

// Explicitly specify adapter

163

const idbDb = new PouchDB('browser-db', { adapter: 'idb' });

164

const levelDb = new PouchDB('node-db', { adapter: 'leveldb' });

165

const remoteDb = new PouchDB('http://localhost:5984/db', { adapter: 'http' });

166

167

// Let PouchDB choose best adapter

168

const autoDb = new PouchDB('auto-db'); // Uses first available from preferredAdapters

169

170

// Check which adapter was chosen

171

console.log('Using adapter:', autoDb.adapter);

172

```

173

174

### Default Configuration

175

176

Create PouchDB constructors with predefined default options.

177

178

```javascript { .api }

179

/**

180

* Create a PouchDB constructor with default options

181

* @param options - Default options to apply to all instances

182

* @returns New PouchDB constructor with defaults

183

*/

184

PouchDB.defaults(options: PouchDBOptions): typeof PouchDB;

185

```

186

187

**Usage Examples:**

188

189

```javascript

190

// Create constructor with memory adapter default

191

const MemoryPouchDB = PouchDB.defaults({

192

adapter: 'memory'

193

});

194

195

// All instances use memory adapter by default

196

const memDb1 = new MemoryPouchDB('db1');

197

const memDb2 = new MemoryPouchDB('db2');

198

199

// Can still override defaults

200

const levelDb = new MemoryPouchDB('db3', { adapter: 'leveldb' });

201

202

// Create constructor for remote databases

203

const RemotePouchDB = PouchDB.defaults({

204

adapter: 'http',

205

auth: {

206

username: 'user',

207

password: 'pass'

208

}

209

});

210

211

const remoteDb = new RemotePouchDB('http://localhost:5984/mydb');

212

```

213

214

### Popular Third-Party Plugins

215

216

Examples of commonly used PouchDB plugins and their installation.

217

218

```javascript

219

// PouchDB Find (Mango queries)

220

const PouchDBFind = require('pouchdb-find');

221

PouchDB.plugin(PouchDBFind);

222

223

// Usage

224

await db.createIndex({ index: { fields: ['name', 'age'] } });

225

const result = await db.find({

226

selector: { name: 'Alice', age: { $gt: 18 } }

227

});

228

229

// PouchDB Authentication

230

const PouchDBAuth = require('pouchdb-authentication');

231

PouchDB.plugin(PouchDBAuth);

232

233

// Usage

234

await db.signUp('username', 'password', {

235

metadata: { email: 'user@example.com' }

236

});

237

await db.logIn('username', 'password');

238

239

// PouchDB Load (bulk loading)

240

const PouchDBLoad = require('pouchdb-load');

241

PouchDB.plugin(PouchDBLoad);

242

243

// Usage

244

await db.load('http://example.com/dump.json');

245

246

// PouchDB Debug (debugging utilities)

247

const PouchDBDebug = require('pouchdb-debug');

248

PouchDB.plugin(PouchDBDebug);

249

250

// Enable debug mode

251

PouchDB.debug.enable('pouchdb:*');

252

```

253

254

### Custom Plugin Development

255

256

Create your own plugins to extend PouchDB functionality.

257

258

```javascript

259

// Advanced plugin example with validation

260

function validationPlugin(PouchDB) {

261

const originalPut = PouchDB.prototype.put;

262

const originalPost = PouchDB.prototype.post;

263

const originalBulkDocs = PouchDB.prototype.bulkDocs;

264

265

// Schema storage

266

const schemas = new Map();

267

268

// Add schema registration method

269

PouchDB.prototype.setSchema = function(docType, schema) {

270

schemas.set(docType, schema);

271

return this;

272

};

273

274

// Validation function

275

function validateDoc(doc) {

276

if (!doc.type) return; // Skip docs without type

277

278

const schema = schemas.get(doc.type);

279

if (!schema) return; // Skip docs without schema

280

281

// Basic validation example

282

for (const [field, rules] of Object.entries(schema)) {

283

if (rules.required && !doc[field]) {

284

throw new Error(`Field '${field}' is required for type '${doc.type}'`);

285

}

286

287

if (rules.type && doc[field] && typeof doc[field] !== rules.type) {

288

throw new Error(`Field '${field}' must be of type '${rules.type}'`);

289

}

290

}

291

}

292

293

// Override put method with validation

294

PouchDB.prototype.put = function(doc, options, callback) {

295

if (typeof options === 'function') {

296

callback = options;

297

options = {};

298

}

299

300

try {

301

validateDoc(doc);

302

return originalPut.call(this, doc, options, callback);

303

} catch (error) {

304

if (callback) {

305

return callback(error);

306

}

307

return Promise.reject(error);

308

}

309

};

310

311

// Override post method with validation

312

PouchDB.prototype.post = function(doc, options, callback) {

313

if (typeof options === 'function') {

314

callback = options;

315

options = {};

316

}

317

318

try {

319

validateDoc(doc);

320

return originalPost.call(this, doc, options, callback);

321

} catch (error) {

322

if (callback) {

323

return callback(error);

324

}

325

return Promise.reject(error);

326

}

327

};

328

329

// Override bulkDocs with validation

330

PouchDB.prototype.bulkDocs = function(docs, options, callback) {

331

if (typeof options === 'function') {

332

callback = options;

333

options = {};

334

}

335

336

try {

337

docs.forEach(validateDoc);

338

return originalBulkDocs.call(this, docs, options, callback);

339

} catch (error) {

340

if (callback) {

341

return callback(error);

342

}

343

return Promise.reject(error);

344

}

345

};

346

}

347

348

// Install and use the validation plugin

349

PouchDB.plugin(validationPlugin);

350

351

const db = new PouchDB('validated-db');

352

353

// Set schema for user documents

354

db.setSchema('user', {

355

name: { required: true, type: 'string' },

356

email: { required: true, type: 'string' },

357

age: { type: 'number' }

358

});

359

360

// This will pass validation

361

await db.put({

362

_id: 'user1',

363

type: 'user',

364

name: 'Alice',

365

email: 'alice@example.com',

366

age: 30

367

});

368

369

// This will fail validation (missing required field)

370

try {

371

await db.put({

372

_id: 'user2',

373

type: 'user',

374

email: 'bob@example.com'

375

});

376

} catch (error) {

377

console.error('Validation error:', error.message);

378

}

379

```

380

381

### Adapter Development

382

383

Create custom adapters for specialized storage needs.

384

385

```javascript

386

// Custom adapter example (simplified)

387

function createCustomAdapter() {

388

return {

389

// Indicates adapter is valid/available

390

valid() {

391

return typeof localStorage !== 'undefined';

392

},

393

394

// Whether adapter uses prefix for database names

395

use_prefix: false,

396

397

// Get database information

398

async _info(callback) {

399

const info = {

400

db_name: this.name,

401

doc_count: 0,

402

update_seq: 0

403

};

404

405

// Count documents in localStorage

406

for (let i = 0; i < localStorage.length; i++) {

407

const key = localStorage.key(i);

408

if (key.startsWith(this.name + '_')) {

409

info.doc_count++;

410

}

411

}

412

413

callback(null, info);

414

},

415

416

// Get a document

417

async _get(id, opts, callback) {

418

const key = this.name + '_' + id;

419

const docString = localStorage.getItem(key);

420

421

if (!docString) {

422

const error = new Error('Document not found');

423

error.status = 404;

424

error.name = 'not_found';

425

return callback(error);

426

}

427

428

const doc = JSON.parse(docString);

429

callback(null, doc);

430

},

431

432

// Store a document

433

async _put(doc, opts, callback) {

434

const key = this.name + '_' + doc._id;

435

436

// Generate revision ID (simplified)

437

if (!doc._rev) {

438

doc._rev = '1-' + Math.random().toString(36).substr(2);

439

} else {

440

// Increment revision

441

const revNum = parseInt(doc._rev.split('-')[0]) + 1;

442

doc._rev = revNum + '-' + Math.random().toString(36).substr(2);

443

}

444

445

localStorage.setItem(key, JSON.stringify(doc));

446

447

callback(null, {

448

ok: true,

449

id: doc._id,

450

rev: doc._rev

451

});

452

},

453

454

// Remove a document

455

async _remove(doc, opts, callback) {

456

const key = this.name + '_' + doc._id;

457

localStorage.removeItem(key);

458

459

callback(null, {

460

ok: true,

461

id: doc._id,

462

rev: doc._rev

463

});

464

},

465

466

// Get all documents

467

async _allDocs(opts, callback) {

468

const result = {

469

total_rows: 0,

470

offset: 0,

471

rows: []

472

};

473

474

for (let i = 0; i < localStorage.length; i++) {

475

const key = localStorage.key(i);

476

if (key.startsWith(this.name + '_')) {

477

const docString = localStorage.getItem(key);

478

const doc = JSON.parse(docString);

479

480

result.rows.push({

481

id: doc._id,

482

key: doc._id,

483

value: { rev: doc._rev },

484

doc: opts.include_docs ? doc : undefined

485

});

486

}

487

}

488

489

result.total_rows = result.rows.length;

490

callback(null, result);

491

},

492

493

// Close database

494

async _close(callback) {

495

callback();

496

},

497

498

// Destroy database

499

async _destroy(opts, callback) {

500

// Remove all documents for this database

501

const keysToRemove = [];

502

for (let i = 0; i < localStorage.length; i++) {

503

const key = localStorage.key(i);

504

if (key.startsWith(this.name + '_')) {

505

keysToRemove.push(key);

506

}

507

}

508

509

keysToRemove.forEach(key => localStorage.removeItem(key));

510

callback();

511

}

512

};

513

}

514

515

// Register the custom adapter

516

PouchDB.adapter('localstorage', createCustomAdapter);

517

518

// Use the custom adapter

519

const db = new PouchDB('my-db', { adapter: 'localstorage' });

520

```

521

522

## Plugin Best Practices

523

524

### Error Handling in Plugins

525

526

```javascript

527

function robustPlugin(PouchDB) {

528

PouchDB.prototype.safeOperation = function(operation) {

529

return new Promise((resolve, reject) => {

530

try {

531

// Validate inputs

532

if (typeof operation !== 'function') {

533

throw new Error('Operation must be a function');

534

}

535

536

// Execute operation with error handling

537

Promise.resolve(operation(this))

538

.then(resolve)

539

.catch(reject);

540

541

} catch (error) {

542

reject(error);

543

}

544

});

545

};

546

}

547

```

548

549

### Plugin Testing

550

551

```javascript

552

// Example plugin test

553

function testPlugin() {

554

const db = new PouchDB('test', { adapter: 'memory' });

555

556

// Test plugin functionality

557

return db.safeOperation(async (database) => {

558

await database.put({ _id: 'test', value: 'hello' });

559

const doc = await database.get('test');

560

return doc.value === 'hello';

561

}).then(result => {

562

console.log('Plugin test passed:', result);

563

}).catch(error => {

564

console.error('Plugin test failed:', error);

565

});

566

}

567

```

568

569

## Adapter Selection Strategy

570

571

PouchDB automatically selects the best available adapter:

572

573

```javascript

574

// Check adapter selection logic

575

function checkAdapterPreference() {

576

console.log('Preferred adapters:', PouchDB.preferredAdapters);

577

console.log('Available adapters:', Object.keys(PouchDB.adapters));

578

579

// Create database and see which adapter was chosen

580

const db = new PouchDB('test');

581

console.log('Selected adapter:', db.adapter);

582

583

return db.destroy(); // Clean up

584

}

585

586

checkAdapterPreference();

587

```

588

589

The adapter system enables PouchDB to work seamlessly across different environments while maintaining a consistent API surface.