PouchDB example: PouchDB <—-> CouchDB

Written by:

This is a simple example on how to save data to PouchDB and / or save or load data to CouchDB

See the html code at the bottom of this post

In this example I started with retrieving data from a CouchDB

Create a new document and view it

✅ Document retrieved successfully:

{
  "value": "James Bond",
  "lastModified": "2025-11-23T20:53:10.797Z",
  "description": "This i James Bond - Agent 007",
  "_id": "demo_007",
  "_rev": "1-fb22f16b8dd6ea5821472092fa7f71eb"
}

Raw value (unescaped):
James Bond

Press button Sync2CouchDB – and you will find the document in coucdb.

Where is PouchDB stored?
Google Chrome / Microsoft Edge:

Inspect Database and Object Stores: Click on the PouchDB database you want to inspect. You can then view its object stores and the data within them.

Open DevTools: Right-click on the webpage and select “Inspect” or press Ctrl + Shift + I (Windows/Linux) / Cmd + Option + I (macOS).

Navigate to Application Tab: In the DevTools panel, select the “Application” tab.

Expand IndexedDB: In the left-hand sidebar under “Storage,” expand the “IndexedDB” section. You will see a list of databases, including those created by PouchDB. 

Code: remember to change your couchdb settings the the file.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>PouchDB Interactive Demo</title>
    <style>
        body { font-family: sans-serif; line-height: 1.6; padding: 20px; max-width: 700px; margin: 0 auto; }
        input[type="text"], textarea { width: 100%; padding: 8px; margin-bottom: 10px; box-sizing: border-box; font-family: monospace; }
        button { padding: 10px 15px; margin-right: 10px; cursor: pointer; }
        button:disabled { cursor: not-allowed; background-color: #ccc; }
        #output { background-color: #f4f4f4; border: 1px solid #ddd; padding: 15px; white-space: pre-wrap; word-wrap: break-word; min-height: 50px; margin-top: 20px; }
        .section { border: 1px solid #ccc; padding: 20px; margin-top: 20px; border-radius: 5px; }
        h2 { margin-top: 0; }
        small { color: #555; }
    </style>
    https://cdn.jsdelivr.net/npm/pouchdb@9.0.0/dist/pouchdb.min.js
    https://cdn.jsdelivr.net/npm/jsonata/jsonata.min.js
    
    
</head>
</head>
<body>

    <h1>Header 1 PouchDB Interactive Demo</h1>
    <p>Store, retrieve, list, and query JSON objects from a local in-browser database using JSONata.</p>

    <div class="section">
        <h2>1. Store Data</h2>
        <label for="docIdStore">Document ID (_id)</label>
        <input type="text" id="docIdStore" placeholder="e.g., user_profile_001">
        
    <label for="docValue">Data (will be stored as `value` property)</label>
    <input type="text" id="docValue" placeholder="e.g., Charlie Day">

    <label for="docDescription">Description (optional)</label>
    <input type="text" id="docDescription" placeholder="e.g., This is a test document">
        
        <button onclick="storeData()" disabled>Store/Update Document</button>
    </div>

    <div class="section">
        <h2>2. Retrieve Data</h2>
        <label for="docIdRetrieve">Document ID (_id) to retrieve</label>
        <input type="text" id="docIdRetrieve" placeholder="Enter an ID to fetch">
        <button onclick="retrieveData()" disabled>Retrieve Document</button>
    </div>

    <div class="section">
        <h2>3. List Documents</h2>
        <label for="filterId">Filter by ID (optional prefix)</label>
        <input type="text" id="filterId" placeholder="e.g., user_">
        <button onclick="listDocuments()" disabled>List Document IDs</button>
    </div>

    <div class="section">
        <h2>4. Query with JSONata</h2>
        <label for="jsonataQuery">JSONata Query</label>
        <textarea id="jsonataQuery" rows="4" placeholder="e.g., **[value='Rum Ham 🥓']"></textarea>
        <p><small>This query runs against an array of all documents in the database. Example: to find all docs where the 'value' property is 'test', use <code>**[value='test']</code></small></p>
        <button onclick="queryWithJsonata()" disabled>Run Query</button>
    </div>

    <button id="syncButton" disabled>Synk2CouchDB</button>
    <button id="replicateButton" disabled>Repliker fra CouchDB</button>
    <h2>Output</h2>
    <pre id="output">Loading libraries...</pre>

    <script>
        let db;
        let outputDiv;

        // Wait for the DOM and all scripts to be fully loaded.
        window.addEventListener('load', () => {
            outputDiv = document.getElementById('output');
            const allButtons = document.querySelectorAll('button');

            // Check if libraries loaded correctly
            if (typeof PouchDB === 'undefined' || typeof jsonata === 'undefined') {
                outputDiv.textContent = '❌ CRITICAL ERROR: A database library (PouchDB or JSONata) failed to load. Please check your network connection and browser console, then refresh the page.';
                console.error("PouchDB or JSONata is not defined. Buttons will remain disabled.");
                return; // Keep buttons disabled
            }

            // Initialize database
            db = new PouchDB('user_data_db');
            outputDiv.textContent = "Database and libraries loaded successfully. Ready.";
            
            // Enable all buttons
            allButtons.forEach(button => {
                button.disabled = false;
            });
            document.getElementById('syncButton').disabled = false;
            document.getElementById('replicateButton').disabled = false;
        // --- REPLIKERING FRA COUCHDB ---
        document.getElementById('replicateButton').addEventListener('click', async function() {
            outputDiv.textContent = 'Starter replikering fra CouchDB...';
            try {
                const remoteDb = new PouchDB('http://admin:admin@localhost:5984/mypouchdb');
                const result = await db.replicate.from(remoteDb);
                outputDiv.textContent = '✅ Replikering fullført!\n\n' + JSON.stringify(result, null, 2);
            } catch (err) {
                outputDiv.textContent = '❌ Feil under replikering:\n\n' + err.toString();
            }
        });
        // --- SYNKRONISERING ---
        document.getElementById('syncButton').addEventListener('click', async function() {
            outputDiv.textContent = 'Starter synkronisering til CouchDB...';
            try {
                // Bruk brukernavn og passord i URL
                const remoteDb = new PouchDB('http://admin:admin@localhost:5984/mypouchdb');
                const result = await db.replicate.to(remoteDb);
                outputDiv.textContent = '✅ Synkronisering fullført!\n\n' + JSON.stringify(result, null, 2);
            } catch (err) {
                outputDiv.textContent = '❌ Feil under synkronisering:\n\n' + err.toString();
            }
        });
        });

        // --- STORING DATA ---
        async function storeData() {
            const docId = document.getElementById('docIdStore').value;
            const docValue = document.getElementById('docValue').value;
            const docDescription = document.getElementById('docDescription').value;

            // Normalize input: handle cases where the user pasted a quoted JSON string
            // or a string with escaped quotes (e.g. "*.{\"id\": _id}"). We'll try to
            // unquote/unescape first, then if the result looks like a JSON object/array
            // parse it so we store an actual object instead of a JSON string.

            let valueToStore = docValue;
            try {
                // Unwrap up to 3 times if the value is a quoted JSON string
                let attempts = 0;
                while (typeof valueToStore === 'string' && /^\s*"[\s\S]*"\s*$/.test(valueToStore) && attempts < 3) {
                    try {
                        valueToStore = JSON.parse(valueToStore);
                        attempts++;
                    } catch (e) {
                        break;
                    }
                }

                // Replace common escaped-quote sequences if present (heuristic)
                if (typeof valueToStore === 'string' && /\\"/.test(valueToStore)) {
                    valueToStore = valueToStore.replace(/\\"/g, '"');
                }

                // If the (possibly unquoted/unescaped) value now looks like JSON object/array,
                // parse it to store as a real object/array instead of a string.
                if (typeof valueToStore === 'string' && /^\s*[\{\[]/.test(valueToStore)) {
                    try {
                        valueToStore = JSON.parse(valueToStore);
                    } catch (e) {
                        // leave as string
                    }
                }
            } catch (err) {
                valueToStore = docValue;
            }

            console.log('Storing document value. original:', docValue, 'stored as:', valueToStore);

            if (!docId || !docValue) {
                outputDiv.textContent = '❌ Error: Both Document ID and Data fields are required to store a document.';
                return;
            }

            const doc = {
                _id: docId,
                value: valueToStore,
                lastModified: new Date().toISOString(),
                description: docDescription
            };

            outputDiv.textContent = `[STORE] Attempting to store document with _id: ${docId}...`;

            try {
                const existingDoc = await db.get(docId);
                console.warn(`⚠️ Document with _id '${docId}' already exists. Updating it.`);
                const updatedDoc = { ...doc, _rev: existingDoc._rev };
                const response = await db.put(updatedDoc);
                outputDiv.textContent = '✅ Document updated successfully:\n\n' + JSON.stringify(response, null, 2);
            } catch (err) {
                if (err.status === 404) {
                    try {
                        const response = await db.put(doc);
                        outputDiv.textContent = '✅ Document stored successfully:\n\n' + JSON.stringify(response, null, 2);
                    } catch (putErr) {
                        outputDiv.textContent = '❌ Error storing new document:\n\n' + putErr.toString();
                    }
                } else {
                    outputDiv.textContent = '❌ Error checking for document:\n\n' + err.toString();
                }
            }
        }

        // --- RETRIEVING DATA ---
        async function retrieveData() {
            const docId = document.getElementById('docIdRetrieve').value;
            if (!docId) {
                outputDiv.textContent = '❌ Error: Document ID is required to retrieve a document.';
                return;
            }
            outputDiv.textContent = `[RETRIEVE] Attempting to retrieve document with _id: ${docId}...`;
            try {
                const doc = await db.get(docId);
                // Show both the full document JSON and the raw value (if it's a string)
                let displayText = '✅ Document retrieved successfully:\n\n' + JSON.stringify(doc, null, 2);
                if (typeof doc.value === 'string') {
                    displayText += '\n\nRaw value (unescaped):\n' + doc.value;
                }
                outputDiv.textContent = displayText;
            } catch (err) {
                if (err.status === 404) {
                    outputDiv.textContent = `❌ Document with _id '${docId}' not found.`;
                } else {
                    outputDiv.textContent = '❌ Error retrieving document:\n\n' + err.toString();
                }
            }
        }

        // --- LISTING DOCUMENTS ---
        async function listDocuments() {
            const filterValue = document.getElementById('filterId').value;
            outputDiv.textContent = 'Fetching document list...';
            const options = { include_docs: false };
            if (filterValue) {
                options.startkey = filterValue;
                options.endkey = filterValue + '\ufff0';
            }
            try {
                const result = await db.allDocs(options);
                let docListText = 'No documents found matching the filter.';
                if (result.rows.length > 0) {
                    const ids = result.rows.map(row => row.id);
                    docListText = `Found ${result.rows.length} document(s):\n\n- ${ids.join('\n- ')}`;
                }
                outputDiv.textContent = docListText;
            } catch (err) {
                outputDiv.textContent = '❌ Error listing documents:\n\n' + err.toString();
            }
        }

        // --- QUERYING WITH JSONATA ---
        async function queryWithJsonata() {
            const jsonataQueryString = document.getElementById('jsonataQuery').value;
            if (!jsonataQueryString) {
                outputDiv.textContent = '❌ Error: JSONata Query cannot be empty.';
                return;
            }
            if (typeof jsonata === 'undefined') {
                outputDiv.textContent = '❌ Error: JSONata library not loaded. Check the script tag.';
                return;
            }
            outputDiv.textContent = 'Fetching all documents and running JSONata query...';
            try {
                const allDocsResult = await db.allDocs({ include_docs: true });
                const docs = allDocsResult.rows.map(row => row.doc);
                if (docs.length === 0) {
                    outputDiv.textContent = 'Database is empty. Nothing to query.';
                    return;
                }
                const expression = jsonata(jsonataQueryString);
                const result = await expression.evaluate(docs);
                outputDiv.textContent = '✅ JSONata query executed successfully:\n\n' + JSON.stringify(result, null, 2);
            } catch (err) {
                outputDiv.textContent = '❌ Error executing JSONata query:\n\n' + err.toString();
            }
        }
    </script>

</body>
</html>


Discover more from Node-RED LoRaWAN CouchDB and more

Subscribe to get the latest posts sent to your email.

Leave a comment