> ## Content Index
> Fetch the complete content index at: https://lintar.my.id/llms.txt
> Use this file to discover other available public pages before exploring further.

# Belajar SQL.js Step-by-Step: SQLite Langsung di Browser
- URL: https://lintar.my.id/belajar-sql-js-step-by-step-sqlite-langsung-di-browser/
- Published: 2025-09-10T07:13:52.000Z
- Updated: 2025-09-10T07:13:52.000Z
- Author: dev
- Tags: #Import 2026-08-27 23:08

Pernah kepikiran gak, kalau kita bisa main database **SQLite** langsung di browser?  
Nah, jawabannya ada di **SQL.js**.

SQL.js itu adalah **SQLite yang ditranslate jadi WebAssembly**, jadi bisa jalan langsung di browser. Serius, kita bisa bikin semacam **SQL Client** sendiri pakai HTML + JavaScript doang.

Di artikel ini, kita bakal bikin project sederhana:

✅ Upload file database SQLite (`.db`, `.sqlite`)  
✅ Jalankan query langsung di browser  
✅ Lihat hasil query dalam bentuk tabel  
✅ Bikin sample data (biar gampang coba-coba)  
✅ Download lagi database setelah diedit

## 1\. Inisialisasi SQL.js

Pertama, kita perlu load library **SQL.js** dari CDN. Caranya gampang, cukup tambahin ini di `<head>` file HTML:

```html
<script src="https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/sql-wasm.js"></script>
```

Lalu inisialisasi:

```html
async function initSQLJS() {
    const sqlPromise = initSqlJs({
        locateFile: file => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/${file}`
    });
    const SQL = await sqlPromise;
    const db = new SQL.Database(); // bikin DB kosong di memori
    console.log("SQL.js siap dipakai di browser!");
}

```

atau jika pakai Node.js / Bundler

Kalau kamu pakai project dengan Node.js (misalnya pakai Webpack, Vite, atau Next.js), install dulu package-nya:

```bash
npm install sql.js
```

Lalu import di kode JavaScript:

```javascript
const initSqlJs = require('sql.js');
const SQL = await initSqlJs({
  locateFile: file => `https://sql.js.org/dist/${file}`
});

const db = new SQL.Database();
```

### Kenapa ada `locateFile`?

SQL.js itu **JavaScript + WebAssembly (WASM)**.

- File `**sql-wasm.js**`  
kode JS buat API (supaya kita bisa pakai `db.run()`, `db.exec()`, dll).
- File **`sql-wasm.wasm`**  
mesin SQLite yang udah di-compile ke WebAssembly.

`locateFile` buat kasih tau “file `.wasm` ada di mana”.

### Contoh 

**Pakai dari Website Resmi (sql.js.org)**  
Kalau ngikut dokumentasi bawaan, biasanya diarahkan ke 

```html
const sqlPromise = initSqlJs({
    locateFile: file => `https://sql.js.org/dist/${file}`
});
```

**Atau pakai CDN** 

```cdn
https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/${file}
```

**Pakai File Lokal**  
Kalau kamu download `sql-wasm.wasm` dan taruh di folder `libs/`, cukup kasih path lokal:

## 2\. Jalankan query (executeQuery)

```javascript
const results = db.exec(queryText);
displayResults(results);

```

Penjelasan praktis:

- `db.exec(sql)` mengembalikan **array hasil** untuk statement yang menghasilkan rows (SELECT).
- Untuk statement non-SELECT (`CREATE`, `INSERT`, `UPDATE`, `DELETE`) `db.exec` tetap mengeksekusi tapi tidak mengembalikan rows → results kosong. Kode kamu sudah menangani ini dengan menampilkan "Query executed, no data returned."

## 3\. Upload file .db

```Javascript
const reader = new FileReader(); 
reader.onload = function(e) { 
  const uint8Array = new Uint8Array(e.target.result); 
  db = new SQL.Database(uint8Array); 
}; 
reader.readAsArrayBuffer(file);
```

### Penjelasannya

1. `**FileReader**`  
dipakai untuk membaca file yang dipilih user (misalnya file `.db` SQLite).
2. **`reader.onload`**  
fungsi callback yang dijalankan setelah file selesai dibaca.
3. `**new Uint8Array(e.target.result)**`  
hasil pembacaan file (ArrayBuffer) diubah menjadi `Uint8Array` supaya bisa diproses dalam bentuk biner.
4. `**new SQL.Database(uint8Array)**`  
membuat instance database SQLite di browser menggunakan `sql.js`, dengan data dari file `.db`.
5. **`reader.readAsArrayBuffer(file)`**  
memberitahu browser:  
"Baca file ini, tapi jangan diperlakukan sebagai teks atau gambar, melainkan kasih saya **isi mentah binernya**."
6. Hasilnya nanti berupa `**ArrayBuffer**` yang bisa diakses di `e.target.result` ketika `onload` dipanggil.

### Kenapa pakai `ArrayBuffer` di sini?

File `.db` (SQLite) bukan file teks, tapi **file biner**.  
Kalau dibaca pakai `readAsText()`, hasilnya bakal rusak (karena dianggap string).  
Dengan `readAsArrayBuffer()`, kita dapat isi asli file dalam bentuk byte-by-byte.

## 4\. Download atau Export .db

```javascript
 if (!db) {
    showStatus('Database belum dimuat!', 'error');
    return;
  }

  // Export database ke bentuk Uint8Array
  const data = db.export();

  // Buat Blob dari data biner
  const blob = new Blob([data], { type: "application/octet-stream" });

  // Buat link download
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = "database.db"; // nama file hasil download
  a.click();

  // Hapus URL object untuk menghemat memory
  URL.revokeObjectURL(url);
```

### Penjelasan

1. `**db.export()**`  
fungsi bawaan `sql.js` untuk mengubah database ke `Uint8Array` (isi file biner `.db`).
2. `**new Blob([...])**`  
bikin file virtual dari data biner, dengan MIME type `application/octet-stream` (umum untuk file biner).
3. **`URL.createObjectURL(blob)`**  
bikin link sementara untuk file virtual tadi.
4. **`a.download = "database.db"`**  
kasih nama file saat diunduh.
5. **`a.click()`**  
otomatis trigger download.
6. **`URL.revokeObjectURL(url)`**  
bersihkan URL setelah dipakai

## contoh lengkapnya : 

```html
<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SQL.js Client</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/sql-wasm.js"></script>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 20px auto;
            padding: 20px;
            background: #f5f5f5;
        }
        
        .section {
            background: white;
            padding: 20px;
            margin: 15px 0;
            border-radius: 5px;
            border: 1px solid #ddd;
        }
        
        h1, h2 {
            color: #333;
            margin: 0 0 15px 0;
        }
        
        .controls {
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
            margin: 15px 0;
        }
        
        button {
            background: #007cba;
            color: white;
            border: none;
            padding: 8px 15px;
            border-radius: 3px;
            cursor: pointer;
        }
        
        button:hover {
            background: #005a87;
        }
        
        button.danger {
            background: #dc3545;
        }
        
        button.danger:hover {
            background: #c82333;
        }
        
        textarea {
            width: 100%;
            height: 200px;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 3px;
            font-family: monospace;
            font-size: 14px;
            resize: vertical;
        }
        
        input[type="file"] {
            padding: 5px;
            border: 1px solid #ddd;
            border-radius: 3px;
        }
        
        .status {
            padding: 10px;
            margin: 10px 0;
            border-radius: 3px;
        }
        
        .status.success {
            background: #d4edda;
            color: #155724;
            border: 1px solid #c3e6cb;
        }
        
        .status.error {
            background: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
        }
        
        .status.info {
            background: #cce7ff;
            color: #004085;
            border: 1px solid #b8daff;
        }
        
        table {
            width: 100%;
            border-collapse: collapse;
            margin: 15px 0;
        }
        
        th, td {
            border: 1px solid #ddd;
            padding: 8px;
            text-align: left;
        }
        
        th {
            background: #f8f9fa;
            font-weight: bold;
        }
        
        .sample-queries {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
            gap: 5px;
            margin: 15px 0;
        }
        
        .sample-btn {
            background: white;
            color: #333;
            border: 1px solid #ddd;
            padding: 5px 10px;
            border-radius: 3px;
            cursor: pointer;
            font-size: 12px;
        }
        
        .sample-btn:hover {
            background: #f8f9fa;
        }
    </style>
</head>
<body>
    <h1>SQL.js Client</h1>
    
    <div id="status" class="status info">Loading SQL.js...</div>

    <div class="section">
        <h2>Database</h2>
        <input type="file" id="fileInput" accept=".db,.sqlite,.sqlite3">
        <div class="controls">
            <button onclick="downloadDB()">Download DB</button>
            <button onclick="createSampleData()">Create Sample</button>
            <button class="danger" onclick="clearDB()">Clear DB</button>
        </div>
    </div>

    <!-- Query Section -->
    <div class="section">
        <h2>SQL Query</h2>
        <textarea id="sqlQuery" placeholder="-- Enter SQL query here
SELECT * FROM sqlite_master WHERE type='table';"></textarea>
        
        <div class="controls">
            <button onclick="executeQuery()">Execute</button>
            <button onclick="clearQuery()">Clear</button>
        </div>

        <div class="sample-queries">
            <button class="sample-btn" onclick="insertSampleQuery('TABLES')">Show Tables</button>
            <button class="sample-btn" onclick="insertSampleQuery('CREATE')">Create Table</button>
            <button class="sample-btn" onclick="insertSampleQuery('INSERT')">Insert Data</button>
            <button class="sample-btn" onclick="insertSampleQuery('SELECT')">Select All</button>
            <button class="sample-btn" onclick="insertSampleQuery('UPDATE')">Update</button>
            <button class="sample-btn" onclick="insertSampleQuery('DELETE')">Delete</button>
        </div>
    </div>

    <!-- Results -->
    <div id="results" class="section" style="display: none;">
        <h2>Results</h2>
        <div id="resultsContent"></div>
    </div>

    <script>
        let db = null;
        let SQL = null;

        const sampleQueries = {
            'TABLES': "SELECT name FROM sqlite_master WHERE type='table';",
            'CREATE': `CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    email TEXT UNIQUE
);`,
            'INSERT': "INSERT INTO users (name, email) VALUES ('aaa', 'aaa@email.com');",
            'SELECT': "SELECT * FROM users;",
            'UPDATE': "UPDATE users SET name = 'aa1' WHERE id = 1;",
            'DELETE': "DELETE FROM users WHERE id = 1;"
        };

        // Initialize SQL.js
        async function initSQLJS() {
            try {
                const sqlPromise = initSqlJs({
                    locateFile: file => `https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.8.0/${file}`
                });
                SQL = await sqlPromise;
                db = new SQL.Database();
                showStatus('SQL.js loaded successfully!', 'success');
                document.getElementById('fileInput').addEventListener('change', handleFileSelect);
            } catch (error) {
                showStatus('Error loading SQL.js: ' + error.message, 'error');
            }
        }

        function handleFileSelect(event) {
            const file = event.target.files[0];
            if (!file) return;

            const reader = new FileReader();
            reader.onload = function(e) {
                try {
                    const uint8Array = new Uint8Array(e.target.result);
                    db = new SQL.Database(uint8Array);
                    showStatus('Database loaded: ' + file.name, 'success');
                } catch (error) {
                    showStatus('Error reading file: ' + error.message, 'error');
                }
            };
            reader.readAsArrayBuffer(file);
        }

        function executeQuery() {
            const queryText = document.getElementById('sqlQuery').value.trim();
            if (!queryText) {
                showStatus('Please enter a query!', 'error');
                return;
            }

            if (!db) {
                showStatus('Database not ready!', 'error');
                return;
            }

            try {
                const results = db.exec(queryText);
                displayResults(results);
                showStatus('Query executed successfully!', 'success');
            } catch (error) {
                showStatus('Query error: ' + error.message, 'error');
                document.getElementById('results').style.display = 'none';
            }
        }

        function displayResults(results) {
            const resultsDiv = document.getElementById('results');
            const resultsContent = document.getElementById('resultsContent');
            
            resultsDiv.style.display = 'block';
            
            if (!results || results.length === 0) {
                resultsContent.innerHTML = '<p>Query executed, no data returned.</p>';
                return;
            }

            let html = '';
            results.forEach(result => {
                if (result.values && result.values.length > 0) {
                    html += '<table>';
                    html += '<tr>';
                    result.columns.forEach(col => {
                        html += `<th>${col}</th>`;
                    });
                    html += '</tr>';
                    
                    result.values.forEach(row => {
                        html += '<tr>';
                        row.forEach(cell => {
                            html += `<td>${cell !== null ? cell : 'NULL'}</td>`;
                        });
                        html += '</tr>';
                    });
                    html += '</table>';
                    html += `<p>${result.values.length} rows</p>`;
                }
            });
            
            resultsContent.innerHTML = html || '<p>No data found.</p>';
        }

        function showStatus(message, type = 'info') {
            const statusDiv = document.getElementById('status');
            statusDiv.textContent = message;
            statusDiv.className = `status ${type}`;
        }

        function downloadDB() {
            if (!db) {
                showStatus('No database to download!', 'error');
                return;
            }

            try {
                const data = db.export();
                const blob = new Blob([data]);
                const url = URL.createObjectURL(blob);
                
                const a = document.createElement('a');
                a.href = url;
                a.download = 'database.db';
                a.click();
                URL.revokeObjectURL(url);
                
                showStatus('Database downloaded!', 'success');
            } catch (error) {
                showStatus('Download error: ' + error.message, 'error');
            }
        }

        function clearDB() {
            if (confirm('Clear all database data?')) {
                db = new SQL.Database();
                document.getElementById('results').style.display = 'none';
                showStatus('Database cleared!', 'success');
            }
        }

        function clearQuery() {
            document.getElementById('sqlQuery').value = '';
        }

        function insertSampleQuery(type) {
            document.getElementById('sqlQuery').value = sampleQueries[type];
        }

        function createSampleData() {
            if (!db) {
                showStatus('Database not ready!', 'error');
                return;
            }

            try {
                db.run(`CREATE TABLE IF NOT EXISTS users (
                    id INTEGER PRIMARY KEY,
                    name TEXT NOT NULL,
                    email TEXT UNIQUE
                )`);

                db.run(`INSERT OR REPLACE INTO users (name, email) VALUES 
                    ('aaa', 'aaa@email.com'),
                    ('bbb', 'bbb@email.com'),
                    ('ccc', 'ccc@email.com')`);

                showStatus('Sample data created!', 'success');
            } catch (error) {
                showStatus('Error creating sample data: ' + error.message, 'error');
            }
        }

        window.addEventListener('load', initSQLJS);
    </script>
</body>
</html>
```