database-js-sqlparser: SQL Parser for database-js Drivers
database-js-sqlparser (current stable version 1.0.0) provides common SQL parsing functionality for `database-js` drivers that interact with non-database backends, such as in-memory data structures, local files, or APIs. It is not a database itself; rather, it acts as an abstract base class, translating standard SQL (including CREATE/DROP TABLE, SELECT, INSERT, UPDATE, DELETE) into calls to an underlying storage mechanism. Developers must extend this class and implement several asynchronous methods (e.g., `ready`, `close`, `load`, `store`, `remove`, `create`) to provide the actual data persistence. The library supports a subset of SQL, including basic CRUD operations, inner/left/right joins, grouping, filtering, ordering, and limiting. Its release cadence is not explicitly stated, but as a foundational 1.0.0 library, it is likely stable with less frequent, more deliberate updates. A key differentiator is its focus on providing a SQL interface over arbitrary JavaScript storage, abstracting away the specifics of the backend.
Common errors
-
TypeError: this.load is not a function
cause An extending driver class failed to implement one of the required abstract methods (e.g., `load`, `store`, `remove`, `create`, `drop`, `ready`, `close`).fixImplement all seven abstract methods (`ready`, `close`, `load`, `store`, `remove`, `create`, `drop`) in your custom driver class that extends `SQLParser`. -
Error: SQLParseError: Syntax error near 'UNSUPPORTED_KEYWORD'
cause The SQL query contains syntax not supported by `database-js-sqlparser` (e.g., `FULL JOIN`, unsupported functions, or dialect-specific clauses).fixReview the supported SQL syntax in the `database-js-sqlparser` documentation and rewrite the query to use only supported features. Debug by simplifying the query to isolate the unsupported part. -
Promise { <pending> }cause Operations on the `SQLParser` or its extending driver methods are asynchronous, but the calling code did not `await` their results or handle the returned Promises correctly.fixAlways use `await` when calling asynchronous methods (like `ready()`, `load()`, `store()`, etc.) on your `SQLParser`-based driver, or chain `.then()` and `.catch()` to handle the Promise resolution.
Warnings
- gotcha This package is an abstract SQL parser and *does not provide any data storage or database connectivity itself*. It must be extended by a custom driver that implements the underlying storage mechanism.
- gotcha The SQL parser has limitations: it does not support `FULL JOIN` or `OUTER JOIN` clauses.
- gotcha When using aggregate functions like `SUM`, providing non-numeric columns may not throw an error but will result in an `undefined` return value for that aggregate.
- gotcha Column types `CHARACTER(n)` and `VARCHAR(n)` handle length constraints differently. `CHARACTER(n)` will pad or truncate to `n` length, while `VARCHAR(n)` will only truncate to `n` length without padding.
Install
-
npm install database-js-sqlparser -
yarn add database-js-sqlparser -
pnpm add database-js-sqlparser
Imports
- SQLParser
import { SQLParser } from 'database-js-sqlparser';import SQLParser from 'database-js-sqlparser';
- SQLParser (CommonJS)
const SQLParser = require('database-js-sqlparser'); - extending SQLParser
class MyDriver extends SQLParser { /* ... */ }
Quickstart
import SQLParser from 'database-js-sqlparser';
class InMemoryDriver extends SQLParser {
constructor() {
super();
this.tables = {}; // Simple in-memory storage
this.lastId = 0;
}
ready() {
console.log('InMemoryDriver: Ready.');
return Promise.resolve(true);
}
close() {
console.log('InMemoryDriver: Closing...');
this.tables = {};
this.lastId = 0;
return Promise.resolve(true);
}
async load(tableName) {
console.log(`InMemoryDriver: Loading from table '${tableName}'`);
return Promise.resolve(Object.values(this.tables[tableName] || {}));
}
async store(tableName, index, row) {
console.log(`InMemoryDriver: Storing in table '${tableName}', index: ${index}`);
if (!this.tables[tableName]) {
throw new Error(`Table '${tableName}' does not exist.`);
}
let id = index;
if (id === null || id === undefined) {
this.lastId++;
id = this.lastId;
row.id = id; // Assuming 'id' is a common primary key column
}
this.tables[tableName][id] = { ...row, id };
return Promise.resolve(id);
}
async remove(tableName, index) {
console.log(`InMemoryDriver: Removing from table '${tableName}', index: ${index}`);
if (!this.tables[tableName] || !this.tables[tableName][index]) {
throw new Error(`Row with index '${index}' not found in table '${tableName}'.`);
}
delete this.tables[tableName][index];
return Promise.resolve(index);
}
async create(tableName, definition) {
console.log(`InMemoryDriver: Creating table '${tableName}' with definition:`, definition);
if (this.tables[tableName]) {
throw new Error(`Table '${tableName}' already exists.`);
}
this.tables[tableName] = {}; // Initialize with an empty object for rows
// In a real scenario, 'definition' would be used to validate column types.
return Promise.resolve(true);
}
async drop(tableName) {
console.log(`InMemoryDriver: Dropping table '${tableName}'`);
if (!this.tables[tableName]) {
throw new Error(`Table '${tableName}' does not exist.`);
}
delete this.tables[tableName];
return Promise.resolve(true);
}
}
async function runExample() {
const driver = new InMemoryDriver();
await driver.ready();
// Simulate SQL operations by calling internal methods (in a real scenario, database-js would do this)
// This is for demonstration of implementing the driver methods
await driver.create('users', [
{ name: 'id', type: 'INTEGER' },
{ name: 'name', type: 'VARCHAR(255)' },
{ name: 'age', type: 'INTEGER' }
]);
let userId1 = await driver.store('users', null, { name: 'Alice', age: 30 });
let userId2 = await driver.store('users', null, { name: 'Bob', age: 25 });
console.log('All users after inserts:', await driver.load('users'));
await driver.store('users', userId1, { name: 'Alicia', age: 31, id: userId1 }); // Update
console.log('Users after update:', await driver.load('users'));
await driver.remove('users', userId2);
console.log('Users after delete:', await driver.load('users'));
await driver.drop('users');
console.log('Driver closing...');
await driver.close();
}
runExample().catch(console.error);