localclientdb a lightweight JavaScript/TypeScript library, that provides a schema-based, MongoDB-like API as client-side database built on top of localForage.
Supports IndexedDB, WebSQL, and LocalStorage with MongoDB-like collection methods and validation.
New GUI feature: With flexible page setup via startGui
Perfect for local-first apps, prototypes, or applications with offline-first features.
- π Collection System β Mimics MongoDB-style collection handling.
- βοΈ CRUD Operations β Insert, find, update, and delete documents
- π§± Schema Validation β Define Schema validation or collections and validate inserts by type and
requiredchecks - π Query Support β Find documents with simple object queries.
- βοΈ Pluggable Storage β IndexedDB (default), WebSQL, LocalStorage
- π§ In-browser Only β No server required
- β‘ Simple β MongoDB-like chainable API, synchronous-looking API (Promise-based)
- πΌοΈ Built-in GUI β Easily inspect, modify, and visualize your data!
npm install localclientdb
# or beta version
npm install localclientdb@beta<script src="https://unpkg.com/localclientdb@beta/dist/localclientdb.umd.js"></script>
<!-- Minified -->
<script src="https://unpkg.com/localclientdb@beta/dist/localclientdb.umd.min.js"></script>Other import Script for compatibility purpose
<!-- cjs -->
<script src="https://unpkg.com/localclientdb@beta/dist/localclientdb.cjs.js"></script>
<!-- ems -->
<script src="https://unpkg.com/localclientdb@beta/dist/localclientdb.esm.js"></script>Inspect your collections visually by launching the built-in GUI.
import { startGui } from 'localclientdb';
startGui({
name: 'NoteApp',
storeNames: ['notes', 'users'], // Optional: Required to display docs in specific collections, else fallback/default is used : ["default"]
mountId: 'my-gui-root', // Optional: div ID to mount GUI (defaults to body)
toggleBtn: true // Optional: shows toggle button to toggle Gui interface
});<body>
<div id="my-gui-root"></div>
<script>
const db = LocalClientDB.localClientDB();
db.connect({ name: "MyDB" }); //connect/create to local database
db.createCollection(
"tasks", //collection name
{title: { type: "string", required: true }, //schema object
});
LocalClientDB.startGui({
name: "MyDB", //connect to local database you want to view
storeNames: ["tasks"], //list of collections available in database or to be created.
mountId: "my-gui-root" // Optional: div ID to mount GUI (defaults to body)
toggleBtn: true // Optional: shows toggle button to toggle Gui interface
});
</script>
</body>You can also create a standalone page (e.g. /public/db.html) to inspect your appβs local data.
src/
βββ schemas/ # Define collection schemas
β βββ note.schema.ts
βββ components/ # NoteApp pages
β βββ Gui.tsx # Gui display component/page
| βββ Note.tsx # Note component/page
βββ localClientDB.config.ts # DB config
βββ App.tsx # React entry
connect(config); // config uses interface of StorageConfig
/* interface StorageConfig {
name: string;
storeName?: string;
driver?: string | string[];
} */
createCollection(name, schema);
insertOne(collection, doc) // or collection.insertOne(doc)
insertMany(collection, doc[]) // or collection.insertMany(doc[])
find(collection, query) // or collection.find(query)
findById(collection, _id) // or collection.findById(_id)
findOne(collection, query) // or collection.findOne(query)
updateOne(collection, query, update) // or collection.updateOne(query, docUpdateData)
deleteById(collection, _id) // or collection.deleteById(_id)
deleteOne(collection, query) // or collection.deleteOne(query)
getAllDocs(collection) // or collection.getAllDocs()
dropCollection(collection) // or collection.dropCollection()Then Export for usage across application
// localClientDB.config.ts
import { localClientDB, driver } from "localclientdb";
// Optional: Set a specific driver
await localClientDB().setDriver(driver.INDEXEDDB);
// Connect (config)
const db = localClientDB();
db.connect({ name: 'NoteApp' })
// export database instance
export default db<script>
const db = localclientdb.localClientDB();
await db.setDriver(driver.INDEXEDDB); // Optional except you want change driver
/*
drivers are available throught
import {driver} from "localclientdb"
driver returns/expose the following
"INDEXEDDB": for INDEXEDDB
"WEBSQL": for WEBSQL
"LOCALSTORAGE": for LOCALSTORAGE
*/
db.connect({ name: "localDB", storeName: "users" });
const users = db.createCollection("users", {
name: { type: "string", required: true },
age: "number",
});
users.insertOne({ name: "Anna", age: 21 });
</script>Note: This an example using React
//note.schema.ts
// import db instance declare/create/init collection and collection schema.
import db from "./localClientlDB.config";
export const noteschema = {
title: "string",
content: "string",
date: "string",
}
// name of collection = note (just like mongoose model)
export const noteSchema = db.createCollection("notes", noteschema)// App.tsx
// a simple use case inside of App.jsx
import { useState } from "react";
import { noteSchema } from "./schemas/note.schema";
const [notes, setNotes] = useState([])
const [note, setNote] = useState({ title: "", content: "", id: null })
const createNote = async (data: typeof noteSchema) => {
if (!data.title || !data.content) return;
const newNote = await noteSchema.insertOne({
...data,
date: new Date().toISOString(),
});
setNotes([...notes, newNote]);
setNote({ title: "", content: "", id: null });
}
const deleteNote = async (id : string) => {
await noteSchema.deleteOne({ _id: id });
setNotes(notes.filter(n => n._id !== id));
}
const all = await noteSchema.getAllDocs();
const note = await noteSchema.findOne({ title: "Welcome" });
const young = await users.find({ age: 22 });await noteSchema.updateOne({ title: "Old" }, { title: "New" });
await noteSchema.deleteOne({ _id: "doc_id" });
await noteSchema.deleteById("some_doc_id");await noteSchema.drop();import { driver } from "localclientdb";
// IndexedDB (default)
await db.setDriver(driver.INDEXEDDB);
// Switch to WebSQL or LocalStorage
await db.setDriver(driver.WEBSQL);
await db.setDriver(driver.LOCALSTORAGE);db.createCollection("books", {
title: { type: "string", required: true },
pages: "number", // shorthand
});Validation errors will be thrown if the inserted data doesn't match the schema.
- Fork the repo
git checkout -b feature-name- Make changes
- Push and open a pull request!
Have questions or suggestions? Open an issue.
This project is licensed under the MIT License Β© Dev Abraham
X (fomerly twitter): @DevAbraham035
LinkedIn: @javascriptenthusiat