- Strong-typed documents and queries.
- Easy to embed as it does not require an HTTP server to run.
- Uses JSON documents.
- Tiny and 0 depedencies.
leaf-db
is meant as a simple database that allows for basic querying over JSON data without needing to set up a database server / connection like with MongoDB or SQLite.
Node does support working with SQLite directly, if you prefer a more stable, feature-complete database.
npm i leaf-db
Create a database using file storage with strong-typed documents:
import LeafDB from 'leaf-db';
type Document = {
title: string
name: string
}
const db = new LeafDB<Document>({ name: 'db', dir: process.cwd() });
await db.open();
const drafts = [
{ title: 'Lady', name: 'Mipha' },
{ title: 'Young Rito Warrior', name: 'Tulin' }
]
await Promise.all(drafts.map(async draft => db.insert(draft)));
// [{ _id: <string>, title: 'Young Rito Warrior', name: 'Tulin' }]
const characters = db.query({ name: 'Tulin' });
const tulin = characters[0];
tulin.title = 'Rito Warrior';
await db.update(tulin); // Overwrite existing document
await db.close();
Leaf-db stores data as JSON documents and saves them inside a JSONL file.
Document keys must be of type string
and cannot start with $
.
Every document is required to have an _id
field. Leaf-db automatically creates an _id
if the field does not exist on insertion. _id
is required to be unique when inserting documents.
Leaf-db only supports JSON values, which is defined as:
type Json =
string |
number |
boolean |
null |
Json[] |
{ [key: string]: Json };
Leaf-db stores the database in memory by default. To make use of persistence, simply provide a path in the constructor and open the database.
import LeafDB from 'leaf-db';
/**
* Create a new database under process.cwd()
* This will create `db.jsonl` in process.cwd()
*/
const db = new LeafDB({ name: 'db', dir: process.cwd() });
await db.open();
When opening a database from storage, leaf-db will return any documents that are corrupt. These documents will be deleted once opened.
import LeafDB from 'leaf-db';
const db = new LeafDB({ name: 'db', dir: process.cwd() });
const corrupt = await db.open(); // Corrupt[]
type Corrupt = {
raw: string;
error: Error;
};
Leaf-db supports both literal values and operators. Example:
/**
* Literal query where value must equal the query value
* { name: 'tulin' } // No match
* { name: 'Mipha' } // No match
*/
const a = { name: 'Tulin' };
/**
* Objects and arrays match on partial matches
* { eras: [] } // Match
* { eras: ['era of the wilds'] } // No match
* { eras: [Sky Era'] } // No Match
*/
const b = { eras: ['Era of the Wilds'] }
Operators allow for more complex queries. Operators must always be used in combination with values.
Is greater than
const query = { a: { $gt: 3 } };
const a = { a: 2 }; // false
const b = { a: 3 }; // false
const c = { a: 4 }; // true
Is greater than or equal to
const query = { a: { $gte: 3 } };
const a = { a: 2 }; // false
const b = { a: 3 }; // true
const c = { a: 4 }; // true
Is less than
const query = { a: { $lt: 3 } };
const a = { a: 2 }; // true
const b = { a: 3 }; // false
const c = { a: 4 }; // false
Is less than or equal to
const query = { a: { $lte: 3 } };
const a = { a: 2 }; // true
const b = { a: 3 }; // true
const c = { a: 4 }; // false
Matches strings against RegExp
const query = { a: { $regexp: /\w+/g } }
const a = { a: '' }; // false
const b = { a: '0' }; // false
const c = { a: 'a' }; // true
Equal to length
const query = { a: { $length: 3 } }
const a = { a: [] }; // false
const b = { a: [1, 2, 3] }; // true
const c = { a: [1, 2, 3, 4] }; // false
Has value in array. Does not partial match on arrays or objects.
const query = { a: { $includes: 3 } };
const a = { a: [] }; // false
const b = { a: [1, 2, 3] }; // true
const query = { b: { $includes: [3] } };
const a = { b: [ [3] ] }; // true
const b = { b: [ [3, 4] ] }; // false
Invert query
const query = { $not: { a: { $lt: 3 } } };
const a = { a: 2 }; // false
const b = { a: 4 }; // true
Must match all queries
const query = { $and: [{ a: 2 }, { b: { $lt: 3 } }] };
const a = { a: 2, b: 2 }; // true
const b = { a: 2, b: 4 }; // false
Matches any query
const query = { $and: [{ a: 2 }, { b: { $lt: 3 } }] };
const a = { a: 2, b: 2 }; // true
const b = { a: 2, b: 4 }; // true
Generate a new, unique id.
import LeafDB from 'leaf-db';
const id = LeafDB.id();
Get all documents
const docs = db.docs // Doc<T>[]
Open persistent storage.
import LeafDB from 'leaf-db';
const db = new LeafDB({ name: 'db', dir: process.cwd() });
const corrupted = await db.open(); // Corrupt[]
Close persistent storage.
await db.close();
Get document by id
db.get('a'); // { _id: 'a' }
Insert document(s) into the database. Will throw an error if duplicate _id
's are found.
const drafts = [{ name: 'Tulin', }, { name: 'Mipha' }];
// [{ _id: <string>, name: 'Tulin' }, { _id: <string>, name: 'Mipha' }]
const docs = await Promise.all(drafts.map(async draft => draft.insert(draft)));
Find document(s) based by query.
// Return docs where `name` is equal to `Mipha`
const docs = db.query({ name: 'Mipha' });
// Return docs where `name` is equal to `Mipha` or where `name` is equal to `Tulin`
const docs = db.query({ $or: [{ name: 'Mipha' }, { name: 'Tulin' }] });
Update existing document. Throws if document does not exist
// Update document `a` with new name `Tulin`
const docs = db.update({ _id: 'a', name: 'Tulin' });
Delete document by _id
// Delete document `a`
await db.delete('a');
Delete all documents in the database.
await db.drop();
- Icon made by Freepik from www.flaticon.com
- This project is inspired by louischatriot/nedb