{"id":28848928,"url":"https://github.com/vladpuz/vladikdb","last_synced_at":"2026-05-09T07:02:48.393Z","repository":{"id":299979830,"uuid":"1004810284","full_name":"vladpuz/vladikdb","owner":"vladpuz","description":"Simple and fast JSON database for node and browser","archived":false,"fork":false,"pushed_at":"2026-04-24T11:11:18.000Z","size":143,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-24T13:21:57.436Z","etag":null,"topics":["browser","database","db","electron","embed","embedded","flat","json","local","localstorage","lowdb","sessionstorage","stream","streaming"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/vladikdb","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vladpuz.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-06-19T08:05:26.000Z","updated_at":"2026-04-24T11:11:22.000Z","dependencies_parsed_at":"2025-06-19T08:49:36.276Z","dependency_job_id":"30deef83-d081-4a8e-b172-312af5eea748","html_url":"https://github.com/vladpuz/vladikdb","commit_stats":null,"previous_names":["vladpuz/vladikdb"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/vladpuz/vladikdb","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vladpuz%2Fvladikdb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vladpuz%2Fvladikdb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vladpuz%2Fvladikdb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vladpuz%2Fvladikdb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vladpuz","download_url":"https://codeload.github.com/vladpuz/vladikdb/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vladpuz%2Fvladikdb/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32810381,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-08T08:22:46.396Z","status":"online","status_checked_at":"2026-05-09T02:00:06.633Z","response_time":123,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["browser","database","db","electron","embed","embedded","flat","json","local","localstorage","lowdb","sessionstorage","stream","streaming"],"created_at":"2025-06-19T20:41:55.442Z","updated_at":"2026-05-09T07:02:48.387Z","avatar_url":"https://github.com/vladpuz.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# vladikdb\n\n\u003e Simple and fast JSON database for node and browser\n\nFeatures:\n\n- Simple installation via npm, no need to download anything extra\n- Minimalist API, only needed methods and nothing extra\n- Hash-indexing for primary keys and other fields when needed\n- Instant access and work with data through RAM\n- Ability to store data in any format and location (if create custom adapter),\n  built-in adapters: JSONFile, LocalStorage, SessionStorage, Memory\n- Ability to work with models for different data structures (if create custom\n  model), built-in models: Collection, Single\n- Streaming for reading/writing large data\n- Safe atomic file writing in Node.js\n- Inspired by [lowdb](https://github.com/typicode/lowdb), vladikdb is more\n  complete solution for high-performance data work\n\n## Quick Start\n\nInstallation:\n\n```shell\nnpm install vladikdb\n```\n\nCreating database instance:\n\n```typescript\nimport path from 'node:path'\nimport Vladikdb, { Collection } from 'vladikdb'\nimport { JSONFile } from 'vladikdb/node'\n// import { LocalStorage } from 'vladikdb/browser'\n\ninterface Post {\n  id: number\n  userId: number\n  title: string\n}\n\n// For node\nconst postsPath = path.join('database', 'posts.json')\nconst postsAdapter = new JSONFile\u003cPost[]\u003e(postsPath)\n\n// For browser\n// const postsKey = 'posts'\n// const postsAdapter = new LocalStorage\u003cPost[]\u003e(postsKey)\n\n// userId - is an indexed key for example with findByIndex\nconst database = new Vladikdb({\n  posts: new Collection(postsAdapter, 'id', ['userId']),\n})\n```\n\nInitialization (reading) database:\n\n```typescript\nawait database.read()\n```\n\nSaving (writing) database:\n\n```typescript\n// Write all database (it's fast, checks for real changes)\nawait database.write()\n\n// Write only posts\nawait database.models.posts.write()\n```\n\nCreating document:\n\n```typescript\ndatabase.models.posts.create({\n  id: 1,\n  userId: 5, // Foreign key for example\n  title: 'vladikdb is awesome',\n})\n```\n\nGetting document by id or index:\n\n```typescript\n// Read by id\nconst post = database.models.posts.findByPrimaryKey(1)\nconsole.log(post)\n\n// Read by userId, works only for indexed keys\nconst posts = database.models.posts.findByIndex('userId', 5)\nconsole.log(posts)\n```\n\nUpdating document:\n\n```typescript\ndatabase.models.posts.updateByPrimaryKey(1, {\n  id: 1,\n  userId: 6,\n  title: 'new title',\n})\n```\n\nDeleting document:\n\n```typescript\ndatabase.models.posts.deleteByPrimaryKey(1)\n```\n\nClearing collection:\n\n```typescript\ndatabase.models.posts.clear()\n```\n\nIterating through documents:\n\n```typescript\n// This is an iterable object, not an array! Use a for..of loop to iterate.\n\nfor (const post of database.models.posts) {\n  // Bad, do not mutate collection documents!!! Use methods.\n  post.title = 'changed title'\n\n  // Good, method is used.\n  database.models.posts.updateByPrimaryKey(post.id, {\n    ...post,\n    title: 'changed title',\n  })\n}\n```\n\nNote: if you perform queries to get specific documents through iteration, cache\nthe result of the query because iterating through all documents takes a lot of\ntime - `O(n)`.\n\n### JSONFile options\n\nOverview:\n\n```typescript\ninterface JSONFileOptions {\n  mode?: 'stream' | 'auto' // Default: 'auto'\n  space?: number // Default: 0\n}\n```\n\n### mode\n\nType: `'stream' | 'auto'`\n\nDefault: `'auto'`\n\nSets the read and write file mode:\n\n- `'auto'` (Default) - reads/writes the file completely, but in case of\n  RangeError error (if file is very large), reads/writes through streaming.\n  Recommended if you are sure that the file will not exceed the maximum string\n  size of your engine, or you don't know how large it will be. Maximum string\n  length `512MB` (V8, x64), depends on the engine.\n- `'stream'` - reads/writes the file through streaming. Recommended if you are\n  sure that the file will exceed the maximum string size of your engine. Setting\n  this value will improve performance in this case because trying to read/write\n  the whole file will not happen.\n\n```typescript\nimport { JSONFile } from 'vladikdb/node'\n\n// For all JSONFile instances (can override)\nJSONFile.defaultOptions.mode = 'stream'\n\nconst adapter = new JSONFile\u003cPost[]\u003e('PATH', {\n  mode: 'stream', // For example stream\n})\n```\n\n### space\n\nType: `number`\n\nDefault: `0`\n\nPassed as third parameter in `JSON.stringify(value, replacer?, space?)`. Adds\nindentation and line breaks in the file, this takes additional memory, so\ndefault is 0.\n\n```typescript\nimport { JSONFile } from 'vladikdb/node'\n\n// For all JSONFile instances (can override)\nJSONFile.defaultOptions.space = 2\n\nconst adapter = new JSONFile\u003cPost[]\u003e('PATH', {\n  space: 2, // For example 2\n})\n```\n\n## Models\n\nDatabase provides two built-in models:\n\n- Collection (`object[]`) - collection of documents designed for fast work with\n  documents by primary keys and fast search by indexes.\n- Single (`object`) - designed for single objects, for example application\n  configuration.\n\nExample usage of Single:\n\n```typescript\nimport path from 'node:path'\n\nimport Vladikdb, { Single } from 'vladikdb'\nimport { JSONFile } from 'vladikdb/node'\n// import { LocalStorage } from 'vladikdb/browser'\n\ninterface Config {\n  apiKey?: string\n  loglevel?: string\n}\n\n// Node\nconst configPath = path.join('database', 'config.json')\nconst configAdapter = new JSONFile\u003cConfig\u003e(configPath)\n\n// Browser\n// const configKey = 'config'\n// const configAdapter = new LocalStorage\u003cConfig\u003e(configKey)\n\nconst database = new Vladikdb({\n  config: new Single\u003cConfig\u003e(configAdapter, {}),\n})\n\nawait database.read()\n\ndatabase.models.config.setData({\n  apiKey: '\u003cNEW_API_KEY\u003e',\n})\n\nconst data = database.models.config.getData()\nconsole.log(data)\n\n// Reset to default data\n// database.models.config.reset()\n\nawait database.write()\n```\n\n### Creating custom model\n\nYou can create new model for optimal, fast work with any data structure.\n\nTo create model you need to implement Model interface:\n\n```typescript\ninterface Model\u003cT\u003e {\n  readonly adapter: Adapter\u003cT\u003e\n  readonly hasChanges: boolean\n  read: () =\u003e Promise\u003cvoid\u003e\n  write: (force?: boolean) =\u003e Promise\u003cvoid\u003e\n}\n```\n\nAs an example refer to source code of built-in models:\nhttps://github.com/vladpuz/vladikdb/tree/main/src/models.\n\n## Adapters\n\n### List of built-in adapters\n\nFor node:\n\n- TextFile\n- JSONFile\n\nFor browser:\n\n- WebStorage\n- SessionStorage\n- LocalStorage\n\nFor any environment:\n\n- Memory\n\n### Creating custom adapter\n\nYou can create adapter for storing data in any format and location, for example\nYAML, remote storage, data encryption and so on.\n\nTo create adapter you need to implement Adapter interface:\n\n```typescript\ninterface Adapter\u003cT\u003e {\n  readonly isReading: boolean\n  readonly isWriting: boolean\n  read: () =\u003e Promise\u003cReadableData\u003cT\u003e\u003e | ReadableData\u003cT\u003e\n  write: (data: WritableData\u003cT\u003e) =\u003e Promise\u003cvoid\u003e | void\n}\n```\n\nAs an example refer to source code of built-in adapters:\nhttps://github.com/vladpuz/vladikdb/tree/main/src/adapters.\n\n#### JSONObjectStream\n\nTo make your adapter work with large data exceeding maximum string length in\nJavaScript `512MB` (V8, x64), you need to use streaming.\n\nIt is recommended to use built-in high-performance transforming stream\nJSONObjectStream, which is also used in built-in adapter JSONFile. Refer to\nsource code of adapter JSONFile for example usage:\nhttps://github.com/vladpuz/vladikdb/blob/main/src/adapters/node/JSONFile.ts.\n\nIt is recommended to provide a way to choose adapter mode because streaming\nworks a bit slower than processing data completely. Streaming is needed only if\ntotal data size exceeds `51 MB` (V8, x64) because this is maximum string length\nin JavaScript and in such case it is impossible to read/write data completely\nthrough JSON. For example, built-in adapter JSONFile provides option\n`mode?: 'stream' | 'auto'`.\n\nFeatures of JSONObjectStream:\n\n- Supports transformation of strings containing `object` or `object[]`\n- Supports string chunks with maximum size `512MB` (V8, x64)\n- Can work in node and browser because it is a web stream\n- Parses through native `JSON.parse()`. It iterates the string, through stack\n  determines the beginning and end of each object, then gets substring and\n  passes it to `JSON.parse()`.\n- Passes chunks of type `object[]` through the chain because passing each object\n  separately works very slowly for large number of small objects. For example, 1\n  string chunk of large size `512MB` (V8, x64) may contain more than 16 million\n  small objects and for performance reasons they are accumulated and passed as\n  `object[]` further through the chain.\n\nSimple example of usage:\n\n```typescript\nimport { JSONObjectStream } from 'vladikdb'\n\nconst webStream = ReadableStream.from(['[{\"id', '\": 1}]']) // 2 chunks\nconst objectStream = webStream.pipeThrough(new JSONObjectStream())\n\nfor await (const chunk of objectStream) {\n  for (const object of chunk) {\n    console.log(object) // { id: 1 }\n  }\n}\n```\n\n## Primary key generation\n\nIn node or browser environment:\n\n```typescript\nconst uuid = crypto.randomUUID()\n\ndatabase.models.posts.create({\n  id: uuid,\n  title: 'vladikdb is awesome',\n})\n```\n\n## Optimization\n\nWhen working with large amount of data you will face performance issues. This\nhappens because each call to `write()` serializes data through `JSON.stringify`,\nthus even if only one document is changed, the JSON data format forces to\nconvert all documents to string before writing.\n\nThis can be mitigated if accumulating changes and performing `write()`\nperiodically and on application exit to avoid data loss:\n\n```typescript\nconst WRITE_INTERVAL = 60 * 1000\n\nconst intervalId = setInterval(() =\u003e {\n  database.write()\n}, WRITE_INTERVAL)\n\n// Node (Docker, pm2, ...)\nprocess.on('SIGINT', () =\u003e {\n  clearInterval(intervalId)\n  database.write()\n})\nprocess.on('SIGTERM', () =\u003e {\n  clearInterval(intervalId)\n  database.write()\n})\n\n// Browser\nwindow.addEventListener('beforeunload', () =\u003e {\n  clearInterval(intervalId)\n  database.write()\n})\n```\n\nBy default, database models check for data changes before writing, if there are\nno changes, writing does not happen. This means you can call `database.write()`\nperiodically without worrying about unnecessary data writing.\n\n## Limitations\n\n### Engine limitations\n\n- All JavaScript type and data structure limitations. For example, maximum\n  string length, maximum number, maximum array length and so on.\n\n### General limitations\n\n- Only JSON-serializable types and data structures are supported.\n- Data is fully loaded and stored in RAM. This allows very fast data work\n  without delays, but limits maximum data size to your RAM capacity.\n- Maximum size of one document `512MB` (V8, x64). This limitation is imposed by\n  maximum string length in JavaScript (may depend on engine).\n- Multithreading is not supported. Need to work with database only in one thread\n  of the application because each thread stores data independently and data is\n  not synchronized between threads. Working with database in multiple threads\n  will lead to data desynchronization and data loss on writing.\n\n### Collection model limitations\n\n- Maximum number of documents in collection `2^24 = 16,777,216` (V8, x64). This\n  limitation is imposed by JavaScript Map implementation (may depend on engine).\n\n### JSONFile adapter limitations\n\n- Maximum file size is limited only by the file system.\n\n### LocalStorage, SessionStorage adapter limitations\n\n- Maximum storage size is limited by the browser.\n\n## Comparison with lowdb\n\n- lowdb and vladikdb use [steno](https://github.com/typicode/steno) for safe\n  atomic file writing (only single-thread).\n- vladikdb supports streaming for writing data of unlimited size.\n- vladikdb introduces new entity Model that defines stored data structure and\n  provides methods for efficient work with this data structure (indexing and so\n  on). lowdb does not handle efficient data work, delegating this responsibility\n  to the user.\n- lowdb provides synchronous and asynchronous adapters and database instances,\n  vladikdb provides synchronous and asynchronous adapters, but Model and\n  database are always asynchronous.\n- Built-in TextFile adapter from vladikdb recursively creates directory if it\n  doesn't exist, whereas lowdb's adapter will throw an error.\n- Built-in JSONFile adapter from vladikdb allows setting any json space\n  (indent), while lowdb space is always 2.\n- lowdb adapters are compatible with vladikdb adapters, and vladikdb has the\n  same set of built-in adapters as lowdb (except DataFile).\n- DataFile adapter was removed.\n\n## API\n\n### Vladikdb\n\n#### new Vladikdb(models)\n\nmodels: `Record\u003cstring, Model\u003cany\u003e\u003e`\n\nCreates database instance for managing models.\n\n#### database.models\n\nType: `Record\u003cstring, Model\u003cany\u003e\u003e`\n\nObject of models passed when creating instance.\n\n#### database.modelsArray\n\nType: `Array\u003cModel\u003cunknown\u003e\u003e`\n\nArray of models passed when creating instance.\n\n#### database.read()\n\nCalls read() to all models.\n\nIt is unsafe to change model data during reading because they may be overwritten\nby read data and lost. It is recommended to call this method only once when\nstarting the application.\n\n#### database.write(force?)\n\n- force? (`boolean = false`) - Forces writing, even if there are no data\n  changes.\n\nCalls write() to all models.\n\n#### database.isReading\n\nType: `boolean`\n\nIf adapter of at least one model is reading, will be true, otherwise false.\n\n#### database.isWriting\n\nType: `boolean`\n\nIf adapter of at least one model is writing, will be true, otherwise false.\n\n#### database.hasChanges\n\nType: `boolean`\n\nIf at least one model has changes, will be true, otherwise false.\n\n### Collection\n\n#### new Collection(adapter, primaryKey, options?)\n\nCreates collection instance.\n\n- adapter (`Adapter`) - Any adapter.\n- primaryKey (`keyof Document`) - Primary key of document. The specified key\n  must contain only primitive data types. The specified key must contain unique\n  value among other documents.\n- indexedKeys? (`Array\u003ckeyof Document\u003e`) - Indexed keys of document. Should not\n  contain primaryKey.\n\n#### collection.adapter\n\nType: `Adapter`\n\nAdapter passed when creating instance.\n\n#### collection.hasChanges\n\nType: `boolean`\n\nIf there are changes for writing will be true.\n\n#### collection.read()\n\nComplexity: `O(n)`\n\nReads data through collection adapter.\n\n#### collection.write(force?)\n\nComplexity: `O(n)`\n\n- force? (`boolean = false`) - Forces writing, even if there are no data\n  changes.\n\nWrites data through collection adapter.\n\n#### collection.clear()\n\nComplexity: `O(1)`\n\nClears collection from all documents.\n\n#### collection.create(document)\n\nComplexity: `O(1)`\n\ndocument: `Document`\n\nCreating document.\n\nThrows error if document with such primary key already exists.\n\n#### collection.findByIndex(indexKey, indexValue):\n\nComplexity: `O(1)`\n\nReturn: `Document[]`\n\nindexKey: `keyof Document`\n\nindexValue: `Document[keyof Document]`\n\nSearching documents by index.\n\nThrows error if parameter indexKey was not specified in indexedKeys when\ncreating instance.\n\n#### collection.findByPrimaryKey(primaryKey)\n\nComplexity: `O(1)`\n\nReturn: `Document | undefined`\n\nprimaryKey: `keyof Document`\n\nSearching document by primary key.\n\n#### collection.updateByPrimaryKey(primaryKey, document)\n\nComplexity: `O(1)`\n\nprimaryKey: `keyof Document`\n\ndocument: `Document`\n\nUpdating document by primary key.\n\nThrows error if document with primary key primaryKey does not exist.\n\nThrows error when trying to update primary key of document. Instead, delete old\ndocument and create new one.\n\n#### collection.deleteByPrimaryKey(primaryKey)\n\nComplexity: `O(1)`\n\nprimaryKey: `keyof Document`\n\nDeleting document(s) by primary key.\n\nThrows error if document with primary key primaryKey does not exist.\n\n#### collection.size()\n\nComplexity: `O(1)`\n\nReturns current size of collection.\n\n### Single\n\n#### new Single(adapter, defaultData)\n\nCreates single instance.\n\n- adapter (`Adapter`) - Any adapter.\n- defaultData (`Data`) - Default data.\n\n#### single.adapter\n\nType: `Adapter`\n\nAdapter passed when creating instance.\n\n#### single.hasChanges\n\nType: `boolean`\n\nIf there are changes for writing will be true.\n\n#### single.read()\n\nReads data through single adapter.\n\n#### single.write(force?)\n\n- force? (`boolean = false`) - Forces writing, even if there are no data\n  changes.\n\nWrites data through single adapter.\n\n#### single.getData()\n\nReturn: `Data`\n\nGets single data.\n\n#### single.setData(data)\n\ndata: `Data`\n\nSets single data.\n\n#### single.reset()\n\nResets data to defaultData.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvladpuz%2Fvladikdb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvladpuz%2Fvladikdb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvladpuz%2Fvladikdb/lists"}