listerineDB is a lightweight, powerful, persistent data store for Node-based applications. It provides a powerful querying system with MongoDB-like syntax for inserting, updating, querying, and removing documents with automatic persistence to the file system.
- 📁 File-based storage with automatic JSON persistence
- 🔍 Powerful querying using the
listerinelibrary - ⏰ Automatic timestamp management (createdDate, updatedDate)
- 🆔 UUID generation for document IDs
- 📊 Collection statistics and metadata
- 🔄 Bulk operations support
- 🛡️ Error handling and validation
npm install listerine-dbimport { listerineDB, BaseDocumentT } from './listerine-db'
// Define your document type
interface User extends BaseDocumentT {
name: string
email: string
age: number
}
// Create a collection
const users = listerineDB.createCollection<User>({
filePath: './data/users.json',
defaultData: [],
})
// Insert a document
const result = users.insert({
name: 'John Doe',
email: 'john@example.com',
age: 30,
})
// Query documents
const allUsers = users.find()
const johnDoe = users.findOne({ name: 'John Doe' })All documents must extend BaseDocumentT:
interface BaseDocumentT {
id: string // Unique identifier (auto-generated UUID)
createdDate?: number // Timestamp when document was created
updatedDate?: number // Timestamp when document was last updated
}interface CollectionOptionsT<DataT extends BaseDocumentT> {
filePath: string // Path to the JSON file for persistence
defaultData?: DataT[] // Default documents to create if file doesn't exist
}These methods don't trigger file writes and are safe for frequent use:
Returns all documents matching the query. If no query is provided, returns all documents.
// Get all documents
const allUsers = users.find()
// Query with conditions
const adults = users.find({ age: { $gte: 18 } })
const johnDoe = users.find({ name: 'John Doe' })Finds a single document by its ID.
const user = users.findById('123e4567-e89b-12d3-a456-426614174000')Finds multiple documents by their IDs.
const selectedUsers = users.findByIds(['id1', 'id2', 'id3'])Returns the first document matching the query.
const firstAdmin = users.findOne({ role: 'admin' })Returns the number of documents matching the query.
const totalUsers = users.count()
const adminCount = users.count({ role: 'admin' })These methods modify the collection and automatically save changes to disk:
Inserts a single document. ID, createdDate, and updatedDate are automatically added.
const result = users.insert({
name: 'Jane Smith',
email: 'jane@example.com',
age: 25,
})
if (result.success) {
console.log('Inserted:', result.document)
} else {
console.error('Error:', result.error)
}Return Type:
interface InsertResultT<DataT> {
success: boolean
document: DataT
error?: string
}Inserts multiple documents in a single operation.
const result = users.insertMany([
{ name: 'Alice', email: 'alice@example.com', age: 28 },
{ name: 'Bob', email: 'bob@example.com', age: 32 },
])
console.log(`Inserted ${result.insertedCount} documents`)
if (result.errors) {
console.log('Errors:', result.errors)
}Return Type:
interface BulkInsertResultT<DataT> {
success: boolean
insertedCount: number
documents: DataT[]
errors?: string[]
}Updates documents by ID. Can accept a single update object or an array.
// Single update
const result = users.update({
id: 'user-id-123',
age: 31,
})
// Multiple updates
const result = users.update([
{ id: 'user-id-123', age: 31 },
{ id: 'user-id-456', name: 'Updated Name' },
])Return Type:
interface UpdateResultT<DataT> {
success: boolean
matchedCount: number
modifiedCount: number
documents: DataT[]
error?: string
}Updates all documents matching the query with the provided updates.
const result = users.updateMany(
{ age: { $lt: 18 } }, // Query: users under 18
{ category: 'minor' }, // Updates to apply
)Removes all documents matching the query.
const result = users.remove({ age: { $lt: 13 } })
console.log(`Deleted ${result.deletedCount} documents`)Return Type:
interface RemoveResultT {
success: boolean
deletedCount: number
error?: string
}Removes a single document by ID.
const result = users.removeById('user-id-123')Removes multiple documents by their IDs.
const result = users.removeByIds(['id1', 'id2', 'id3'])Removes all documents from the collection.
const result = users.clear()
console.log(`Cleared ${result.deletedCount} documents`)Reloads the collection data from the file, discarding any unsaved changes in memory.
users.reload()Returns statistics and metadata about the collection.
const stats = users.getStats()
console.log(`Documents: ${stats.documentCount}`)
console.log(`File: ${stats.filePath}`)
console.log(`Size: ${stats.fileSize} bytes`)
console.log(`Last modified: ${stats.lastModified}`)Return Type:
interface Stats {
documentCount: number
filePath: string
fileSize: number
lastModified: Date | null
}The main database instance for managing multiple collections.
Creates a new collection with the specified options.
const users = listerineDB.createCollection<User>({
filePath: './data/users.json',
defaultData: [{ id: '1', name: 'Admin', email: 'admin@example.com', age: 30 }],
})Retrieves an existing collection by its file path.
const existingUsers = listerineDB.getCollection<User>('./data/users.json')Closes the database and clears all collection references.
listerineDB.close()listerineDB uses the listerine library for querying. Here are common query patterns:
users.find({ name: 'John' })
users.find({ age: 30, active: true })users.find({ age: { $gt: 18 } }) // Greater than
users.find({ age: { $gte: 18 } }) // Greater than or equal
users.find({ age: { $lt: 65 } }) // Less than
users.find({ age: { $lte: 65 } }) // Less than or equal
users.find({ age: { $ne: 30 } }) // Not equalusers.find({ age: { $in: [18, 21, 25] } }) // In array
users.find({ age: { $nin: [18, 21, 25] } }) // Not in arrayusers.find({
$or: [{ age: { $lt: 18 } }, { age: { $gt: 65 } }],
})
users.find({
$and: [{ age: { $gte: 18 } }, { active: true }],
})All mutation operations return result objects with success flags and error messages:
const result = users.insert({ name: 'Test User' })
if (!result.success) {
console.error('Insert failed:', result.error)
return
}
console.log('Inserted successfully:', result.document)- Define TypeScript Interfaces: Always define proper interfaces extending
BaseDocumentT - Handle Errors: Check the
successproperty in operation results - Use Appropriate Queries: Use specific queries instead of loading all data when possible
- File Path Management: Use absolute paths or consistent relative paths for collections
- Backup Strategy: Consider implementing backups for important data files
interface User extends BaseDocumentT {
username: string
email: string
role: 'admin' | 'user'
lastLogin?: number
}
const users = listerineDB.createCollection<User>({
filePath: './data/users.json',
})
// Create admin user
users.insert({
username: 'admin',
email: 'admin@example.com',
role: 'admin',
})
// Find all administrators
const admins = users.find({ role: 'admin' })
// Update last login time
users.updateMany({ username: 'admin' }, { lastLogin: Date.now() })interface Product extends BaseDocumentT {
name: string
price: number
category: string
inStock: boolean
tags: string[]
}
const products = listerineDB.createCollection<Product>({
filePath: './data/products.json',
})
// Add products
products.insertMany([
{ name: 'Laptop', price: 999, category: 'Electronics', inStock: true, tags: ['computer', 'portable'] },
{ name: 'Mouse', price: 29, category: 'Electronics', inStock: true, tags: ['computer', 'accessory'] },
])
// Find affordable electronics
const affordableElectronics = products.find({
category: 'Electronics',
price: { $lt: 100 },
})