NeverChange is a database solution for web applications using SQLite WASM and OPFS.
This package is currently in alpha stage. The interface and method names may change with each update. Please use with caution.
- Installation
- Supported Browsers
- About Application Deployment
- Requirements
- Usage
- For Developers
- License
npm install neverchange
All end-to-end tests are conducted with Playwright in Chrome, Edge, Chromium, and Firefox.
- Google Chrome
- Microsoft Edge
- Firefox
- Safari
- (However, there are restrictions. See About Application Deployment for more information.)
Our recommendation is to deploy with Netlify for better compatibility. See below for Netlify and GitHub Pages deployment instructions.
A full deployment guide is available in the Deployment Documentation, including specific configuration examples.
Netlify is recommended for deploying apps with persistent data on the web front end using NeverChange.
The following statement in the _headers
file can be used for this purpose.
/*
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
If you want to host on GitHub Pages, you will need coi-serviceworker.js.
But, Safari does not function properly in this case. Refer to the following issue for more details.
#6
example:
<!DOCTYPE html>
<html>
<head>
<title>NeverChange Example</title>
<!-- If you want to host on GitHub, you will need coi-serviceworker.js -->
<script src="/coi-serviceworker.js"></script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx">
</body>
</html>
A full deployment guide is available in the Deployment Documentation, including specific configuration examples.
- Node.js (version 20 or higher recommended)
- npm (usually comes with Node.js)
Here’s how to use NeverChange with some practical examples.
If you’re interested in writing SQL efficiently with NeverChange, you may also want to check out sqlc-gen-typescript-for-neverchange, which generates TypeScript code using sqlc.
Here's a basic example of how to use NeverChangeDB to create a database, insert data, and query it:
import { NeverChangeDB } from 'neverchange';
async function main() {
// Initialize the database
const db = new NeverChangeDB('myDatabase');
await db.init();
// Create a table
await db.execute(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
)
`);
// Insert data
await db.execute(
'INSERT INTO users (name, email) VALUES (?, ?)',
['John Doe', 'john@example.com']
);
// Query data
const users = await db.query('SELECT * FROM users');
console.log('Users:', users);
// Close the database connection
await db.close();
}
main().catch(console.error);
NeverChangeDB supports database migrations, allowing you to evolve your database schema over time. Here's an example of how to define and use migrations:
import { NeverChangeDB } from 'neverchange';
// Define migrations
const migrations = [
{
version: 1,
up: async (db) => {
await db.execute(`
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL
)
`);
}
},
{
version: 2,
up: async (db) => {
await db.execute(`
ALTER TABLE users ADD COLUMN email TEXT
`);
}
}
];
async function main() {
// Initialize the database with migrations
const db = new NeverChangeDB('myDatabase', { isMigrationActive: true });
db.addMigrations(migrations);
await db.init();
// The database will now have the latest schema
const tableInfo = await db.query('PRAGMA table_info(users)');
console.log('Users table schema:', tableInfo);
await db.close();
}
main().catch(console.error);
NeverChangeDB offers two modes for database dump and import: Optimized Mode and SQLite Compatibility Mode.
In the optimized mode, the dump output does not include transaction control statements or PRAGMA settings. This mode is designed for:
- Flexibility: Allows for custom transaction control during import.
- Consistency: Ensures the entire import process is wrapped in a single transaction.
- Error Handling: Facilitates easy rollback in case of import errors.
- Performance: Enables fine-tuned control over transaction size and checkpoints for large datasets.
- Platform Independence: Improves compatibility between different SQLite implementations.
This mode generates dump output that closely resembles the standard SQLite .dump
command, including transaction control statements and PRAGMA settings. Use this mode when:
- Compatibility with standard SQLite tools is required.
- You need to use the dump with other systems expecting standard SQLite dump format.
// Dumping a Database
const db = new NeverChangeDB('myDatabase');
await db.init();
// Optimized Mode (default)
const optimizedDump = await db.dumpDatabase();
// SQLite Compatibility Mode
const compatibleDump = await db.dumpDatabase({ compatibilityMode: true });
// Importing a Database
// Optimized Mode (default)
await db.importDump(dumpContent);
// SQLite Compatibility Mode
await db.importDump(dumpContent, { compatibilityMode: true });
When using the dump and import features, special attention should be paid to BLOB (Binary Large Object) data:
-
Dumping BLOB Data: BLOB data is serialized into a special string format during the dump process. This ensures that binary data is correctly represented in the dump output.
-
Importing BLOB Data: When importing, the special string format for BLOB data is automatically detected and converted back into the appropriate binary format.
-
Working with BLOB Data: After importing, BLOB data may be represented as an object with numeric keys (e.g.,
{"0":1,"1":2,"2":3}
). To work with this data as aUint8Array
, you may need to convert it:
const convertToUint8Array = (obj) => {
if (obj && typeof obj === 'object' && !Array.isArray(obj)) {
return new Uint8Array(Object.values(obj));
}
return obj;
};
// Usage
const blobData = convertToUint8Array(row.blobColumn);
- Large Databases: When working with large databases, consider the memory limitations of the browser environment. For very large datasets, you may need to implement chunking strategies for dump and import operations.
- Complex Data Types: While NeverChangeDB handles most SQLite data types seamlessly, complex types like JSON or custom data structures may require additional processing when dumping or importing.
- Cross-Browser Compatibility: Although the core functionality is designed to work across modern browsers, some advanced features or performance optimizations may vary between different browser environments. Always test thoroughly in your target browsers.
NeverChangeDB also supports CSV export and import functionality, which is useful for integrating with spreadsheet applications and external data sources.
You can export a table to a CSV format using the dumpTableToCSV
method:
const db = new NeverChangeDB('myDatabase');
await db.init();
/* We will assume that you have added tables and information */
const csvContent = await db.dumpTableToCSV('your_table');
console.log('CSV Export:', csvContent);
await db.close();
This will export the contents of your_table
to a CSV string.
You can import CSV content into a table using the importCSVToTable
method:
const db = new NeverChangeDB('myDatabase');
await db.init();
const csvContent = `id,name,email\n1,John Doe,john@example.com\n2,Jane Smith,jane@example.com`;
await db.importCSVToTable('your_table', csvContent);
await db.close();
This will insert the CSV data into the your_table
table. Ensure the table is created beforehand and the columns match the CSV headers.
-
Clone the repository:
git clone https://github.com/shinshin86/neverchange.git cd neverchange
-
Install dependencies:
npm install
-
Install browsers for Playwright for e2e test:
npx playwright install
npm run build
: Build the project.npm run dev:e2e
: Start the development server for E2E tests.npm run e2e
: Run E2E tests using Playwright.
- @sqlite.org/sqlite-wasm: SQLite WASM implementation
- Vite: Fast frontend build tool
- TypeScript: Typed superset of JavaScript
- Playwright: Modern web testing and automation framework
Run E2E tests:
npm run e2e
Code Format:
npm run fmt
This project is released under the MIT License.