@codeklb/local-db
v1.0.2
Published
Offline-first typed local DB with Zod validation, Query Builder, Prisma sync, and React hooks.
Downloads
307
Readme
🧩 @codeklb/local-db
Offline-first Local Database for TypeScript & React
Typed • Zod-Validated • Query Builder • Prisma Sync • Soft Delete • Transactions • React Hooks
@codeklb/local-db is a modern, enterprise-grade, offline-first TypeScript database built for serious production apps.
It provides a powerful, Prisma-like API with strong typing, Zod validation, lifecycle hooks, transactions, query builders, and local persistence.
Perfect for:
- Offline or partially-connected apps
- Dashboards & admin panels
- Hybrid mobile apps
- Local caching for APIs
- Form builders
- Complex UI with local state
- Apps that sync from a backend (Prisma/Postgres)
🚀 Features
- Offline-first local DB (LocalStorage & Memory adapters)
- Typed Collections (Prisma-like API)
- Zod schema validation (optional but powerful)
- Query Builder (
where(),orderBy(),limit(), etc.) - Enterprise CRUD (insert, update, delete, bulk operations)
- Soft delete with restoration
- Transactions with rollback
- Lifecycle hooks (before/after insert/update/delete)
- Prisma sync adapter (server-side)
- React hooks (auto-updating)
- Extensible architecture with adapters & plugins
📦 Installation
npm install @codeklb/local-db zodzod is optional but required for schema validation.
🧠 Quick Start
1. Define your type
import type { BaseRecord, EntityId } from "@codeklb/local-db";
export interface User extends BaseRecord {
id: EntityId;
name: string;
email: string;
role: "ADMIN" | "USER";
}2. Initialize DB + Collection
import { Database, LocalStorageAdapter } from "@codeklb/local-db";
import type { User } from "./types";
const db = new Database();
export const users = db.registerCollection<User>({
name: "users",
storage: new LocalStorageAdapter("users"),
softDelete: true,
idGenerator: () => crypto.randomUUID()
});🔥 CRUD Examples
Insert
await users.insert({
name: "Hasan",
email: "[email protected]",
role: "ADMIN"
});Update
await users.update("123", { role: "USER" });Delete (soft delete enabled)
await users.delete("123");Get All
const list = await users.getAll();Get Deleted Items
await users.getAll({ includeDeleted: true });📦 Bulk Operations
await users.bulkInsert([
{ name: "A", email: "[email protected]", role: "USER" },
{ name: "B", email: "[email protected]", role: "USER" }
]);
await users.bulkUpdate(
(u) => u.role === "USER",
{ role: "ADMIN" }
);
await users.bulkDelete((u) => u.email.endsWith("@test.com"));🔒 Transactions
If anything throws, all changes roll back.
await users.transaction(async (tx) => {
await tx.insert({ name: "A", email: "[email protected]", role: "USER" });
await tx.insert({ name: "B", email: "[email protected]", role: "USER" });
// Throw to rollback:
// throw new Error("Something failed");
});🔍 Query Builder
import { QueryBuilder } from "@codeklb/local-db";
const admins = await users.query(
new QueryBuilder<User>()
.where(u => u.role === "ADMIN")
.orderBy("name", "asc")
.limit(10)
.build()
);🛡️ Zod Schema Validation
import { z } from "zod";
import { createCollectionWithSchema, LocalStorageAdapter, Database } from "@codeklb/local-db";
const userSchema = z.object({
id: z.string().uuid().optional(),
name: z.string(),
email: z.string().email(),
role: z.enum(["ADMIN", "USER"])
});
const db = new Database();
export const users = createCollectionWithSchema(userSchema, {
name: "users",
storage: new LocalStorageAdapter("users"),
softDelete: true
});Invalid operations will throw Zod errors.
⚛️ React Integration
Auto-updating List
import { useCollection } from "@codeklb/local-db/react";
import { users } from "../db";
function UserList() {
const { data, ready } = useCollection(users);
if (!ready) return "Loading...";
return (
<ul>
{data.map(u => (
<li key={u.id}>{u.name}</li>
))}
</ul>
);
}🔗 Prisma Sync (Server → Local)
Server-side sync
import { PrismaClient } from "@prisma/client";
import {
Database,
MemoryAdapter,
PrismaSyncAdapter
} from "@codeklb/local-db";
const prisma = new PrismaClient();
const db = new Database();
export const usersServer = db.registerCollection({
name: "users",
storage: new MemoryAdapter(),
syncAdapter: new PrismaSyncAdapter(prisma.user, u => ({
id: u.id,
name: u.name,
email: u.email,
role: u.role
}))
});API route
await usersServer.init();
await usersServer.syncFromRemote();
return Response.json(await usersServer.getAll());Client pulling from server
export async function syncUsersClient() {
const res = await fetch("/api/users-sync");
const usersRemote = await res.json();
await users.init();
await users.clear();
await users.bulkInsert(usersRemote);
}🔧 Hooks (Middleware)
export const users = db.registerCollection<User>({
name: "users",
storage: new LocalStorageAdapter("users"),
hooks: {
beforeInsert: (u) => ({
...u,
email: u.email.toLowerCase()
}),
afterUpdate: (u) => {
console.log("Updated user", u.id);
}
}
});📁 Initialize All Collections
await db.initAll();🧩 Full Example
await db.initAll();
await users.insert({ name: "Ali", email: "[email protected]", role: "USER" });
const admins = await users.query(
new QueryBuilder<User>()
.where(u => u.role === "ADMIN")
.build()
);
console.log("Admins:", admins);📝 License
MIT © Codek Lebanon
