@khankudo/kisdb
v0.1.3
Published
KisDB is a zero-dependency library that is highly modular and can serve as a schemaless, realtime Database and as the primary API router for your small-scale personal project. It runs on Bun and can be interface-, protocol- and database-agnostic.
Readme
[!WARNING] KisDB is a personal passion project reaslitically only meant for myself to use. It has plenty of cool features that others certainly could enjoy, so I'm putting it here, but do NOT expect regular updates or any maintenance of any kind. Use at your own risk!
Introduction
KisDB is a zero-dependency library that is highly modular and can serve as a schemaless, realtime Database and the primary API router for your small-scale personal project. It runs on Bun and can be interface-, protocol- and database-agnostic. The main goal is to have the simplest and most flexible Developer Experience possible, even if some performance/efficiency has to be sacrificed. Personal projects should be built to push your imagination and skills, not dance around dependencies' limitations.
Motivation
I like building small, personal-use projects. I don't like designing and planning them. All Database/API solutions I could find were either DBaaS, lacked realtime-support or required fixed schemas that were difficult to constantly migrate during early development. I found myself having to first figure out the whole API & DB structure, decide on the tools that fit my needs best, get familiar with provided SDKs and then design my whole project around the tool's limitations. So instead of developing, I was just planning everything over and over again for each project. As one might think, this get's boring quite fast, so the projects ended up being abandoned before they really even began. No single Solution that I could find fit all my needs, so it was either deal with boring planning & repetitive design-work or abandon these kinds of projects I enjoy so much. So as any sane person would do; I chose to create KisDB :)
Goals
- Realtime
- easily subscribe to get updates for any values
- DX over Everything
- prioritize ease of use & flexibility instead of raw performance & efficiency
- Built for small-scale personal projects, not enterprise-grade solutions
- API Handling
- interact with and define the project's logical API through the same interface
- no duplicate connections
- no separate dataflows
- Schemaless support
- DB & whole interface may never mandate a schema
- schemas should of course be supported, but never required, only ever optional
- Exponentially faster & easier early development
- Client & Server
- simplify development for both the client and the server
- no cumbersome duplicates of anything ever again
- Local Support
- no cloud dependency
- no lock-in to any service/company/product
- No Dependencies
- one shouldn't need a hundred different npm-packages just create a simple client & database/backend
- Any Data
- Must support Nested Objects, Key-Value-Pairs and Arrays/Lists
- Any Protocol
- Allow for protocol-modules to handle transport in any way they see fit
- REST (http/s), WebSocket, MQTT, raw tcp/udp, ...
- Any Interface
- Allow flexible client interfaces (interoperable too):
- Object-Oriented, Functional, VanillaJS, TypeScript, React, go, C/C++, ...
Overview

KisDB consists of four module layers, each focusing only on it's own responsibilities with no additional requirements to consider.
Viewer - Client
The viewer is what transform the primitive KCPHandle Object a client provides into an actual programmatic interface for the developer to use to access their data and API endpoints. Any viewer can be chosen and freely used with any client, the developer can even make their own without needing to consider any of the other KisDB layers; simplicity and modularity at it's core. This boundary is what enables KisDB to be interface-agnostic, allowing for a personalized Developer Experience.
If you are using a custom client-server for a specific project, say for MQTT on a microcontroller, your client doesn't have to support a viewer, it itself can be both a client and a simple viewer. The separation exists in KisDB, so that you can reuse the same client in the browser with different frameworks natively and much more reliably rather than having to chain multiple viewers on top of one another.
Client - Server
The Client and Server modules must always come in pairs. A single client module is only ever compatible and must explicitly be used with it's paired server module. They are responsible for transporting the data and requests. It does not matter how a server handles talking with it's client, the only requirement is that the server accepts a DB's KCPHandle and the client provides that KCPHandle to viewers. How they do it is completely irrelevant. This boundary is what enables KisDB to be protocol-agnostic, allowing wide hardware and programming language support.
Server - Database
The Server module accepts a KCPHandle Object from the Database Module and communicates using it. The server is responsible for assigning unique connection-ID to each client or omit it if stateless. Any server can work with any Database, there is also nothing stopping you from using multiple server in parallel providing different protocols for the same database. Changes are automatically reflected at all servers through the database-module, nothing needs to be tracked, managed or coordinated by the servers; they merely 'serve'. This boundary is what enables the separation of the transport-layer and the data-layer, allowing any client using any protocol to seamlessly integrate with the same underlying Application Logic & Data as any other client using any other protocol.
Database - Storage
The Database module is responsible for interacting with the actual DB-instance. It provides the standardized KCPHandle Object and translates it locally via e.g. SQLite or remotely using available specific SDKs such as mongoose, S3 or any SQL-interface. The Database module is also responsible for implementing all authentication and authorization methods. Middleware-style solutions could have been implemented for more flexibility, however since many databases already implement varying degrees of auth and even perhaps even realtime listening, etc. extracting those into layers would have made everything more complicated, less efficient and much more prone to bugs. This way, only the DB module is considered 'trusted-context', everything else needs to authenticate itself (yes even the application logic server needs a token, even if used locally)
Example
Here are very basic example snippets from the client & server. You can also just run the included dev-sample (p.s. zero-dependencies -> no bun install needed):
git clone https://github.com/KhanKudo/kisdb.git
cd kisdb
bun run devClient / Browser
import { createHttpClient } from '@khankudo/kisdb/client/http'
import { createVanillaViewer } from '@khankudo/kisdb/viewer/vanilla'
const client = createHttpClient(undefined, { token: '123' })
const DB = createVanillaViewer(client)
DB.$onnow = console.info
//info > {}
DB.x.$once = console.warn
console.log(await DB.x) //> undefined
DB.x = 5
//info > {x:5}
//warn > 5
console.log(await DB.x) //> 5
delete DB.x
//info > {}
console.log(await DB.rnd()) //> -1..1 (random number)
console.log(await DB.rnd()) //> -1..1 (random number)
console.log(await DB.rnd()) //> -1..1 (random number)
DB.x.$off = console.infoServer / Bun
import { createSQLiteHandle, destroySQLiteHandle } from "@khankudo/kisdb/db/sqlite"
import { createHttpRoutes } from "@khankudo/kisdb/server/http"
import { createDirectClient } from "@khankudo/kisdb/client/direct"
import { createVanillaViewer } from "@khankudo/kisdb/viewer/vanilla"
import { createAdminHelper } from "../core/admin"
import { EVERYONE, SUPERADMIN, USERS } from "../core/auth"
export type MyDB = {
'': {
x?: number
rnd: () => number
}
}
const handle = await createSQLiteHandle<MyDB>('my_app') // bun:sqlite ./my_app.db
// helper with various methods for database/server administration
const admin = await createAdminHelper(handle, 'DEFAULT_PA$$WORD')
// set default permissions for path '' to be owned by root, read: all, write: only authenticated, execute: all
await admin.ensureAccess('', SUPERADMIN, EVERYONE, USERS, EVERYONE)
// create a demo account with password 'demo' and token '123', (true keeps already existing tokens, e.g. after reboot)
await admin.ensureUser('demo', 'demo', true, '123')
// create a server account with login disabled and token '456', (true keeps already existing tokens, e.g. after reboot)
await admin.ensureUser('server', false, true, '456')
// invalidate the admin object - destroy internal token
await admin.destroy()
const direct = createDirectClient(handle, {
connection: 0,
token: Bun.env.SERVER_TOKEN ?? '456'
})
const DB = createVanillaViewer(direct,'')
DB.rnd = async () => {
return Math.random() * 2 - 1
}
DB.x.$onnow = (value) => {
console.log('x is', value)
}
const server = Bun.serve({ routes: createHttpRoutes(handle) })
console.log('Ready! ( http://localhost:3000 )')
process.on('exit', () => {
server.stop(true)
destroySQLiteHandle(handle)
})API Reference
KCPHandle
ToDo
Helpers
ToDo
Notes on AI
This project has zero AI-generated code or content of any kind. No agent, in-editor or project-context-aware AI was ever used for anything at all. Some microsnippets (~5 lines of code) might have come from AI. I still love to simply google for stuff (actually Brave/DDG) and look through Documentation/StackOverflow/Reddit results but sometimes nothing comes up. Some tools or problems are too new, since AI noone sadly really posts solutions or questions on forums anymore. So my AI-philosophy is to use it as a modern-day search engine. The same way people would traditionally have used StackOverflow or Google Search (the good old one). I treat AI answers the same way I treat any zero-vote, zero-comment StackOverflow response: Cautiously optimistic.
The way we search the internet may have changed, my way of using it and it's content however has not. After all, even if one hard-headedly never interacts with AI themselves, there is no more way of knowing if any content on the internet came from an AI, a human, or simply a human using AI. As such if one wishes to guarantee never using anything AI-generated, one must simply stop using the internet; I won't do that.
