The embedded database for TypeScript object graphs.
GraphVault persists the TypeScript domain model you already use: a root object with nested objects, arrays, maps, sets, shared references, cycles, and classes. It is designed for NestJS services, local-first tools, CLIs, desktop apps, simulations, and other applications that own a rich, connected model.
It is not a SQL server and it is not an ORM. Keep normal objects in memory, change them in ordinary TypeScript, then explicitly commit a verifiable graph store with WAL recovery, locking, indexes, GVQL, and an admin UI.
Current release: 0.2.9, with annotation-driven field constraints, stronger public TSDoc, production write defaults, explicit Node.js LTS support, field annotations for save/load filtering, professional persistent indexes, and NestJS smoke tests.
When a TypeScript model is truly a graph, persisting it through records or JSON often means building an extra mapping layer and reassembling relationships after every read. GraphVault keeps the model as the source of truth.
| If you need to... | GraphVault gives you... |
|---|---|
preserve object identity, cycles, Map, Set, and classes |
native object-graph persistence instead of a hand-built mapper |
| decide precisely when data becomes durable | explicit commits, transactions, WAL recovery, and locking |
| query a committed graph without shipping it elsewhere | GVQL, persistent indexes, execution plans, aggregates, previews, and batch updates |
| run an application-owned store safely | verification, backups, health reports, schema migrations, and GraphVault Studio |
| use an embedded store from NestJS | a NestJS module plus transactional service decorators |
GraphVault is deliberately focused: it is an embedded, application-owned store, not a replacement for Postgres, SQLite, or a distributed database server.
npm install @sprengmeister/graphvaultimport { EmbeddedStorage } from "@sprengmeister/graphvault";
const document = { id: "doc-1", title: "Hello object graph" };
const storage = await EmbeddedStorage.start({
storageDirectory: "./data",
root: { documents: [], pinned: new Set() },
});
storage.root.documents.push(document);
storage.root.pinned.add(document); // the same object can be referenced in more than one place
await storage.storeRoot();
await storage.shutdown();Use GraphVault when:
- your app owns a rich object model and you do not want to flatten it into tables
- object identity, shared references, cycles, classes,
Map,Set, and rich JS values matter - you want embedded persistence for a NestJS service, CLI, desktop app, simulation, rules engine, local-first tool, test harness, or admin-heavy internal tool
- writes should be explicit and auditable instead of hidden behind ORM change tracking
- a bounded graph slice should be easy to expose through an API with
loadSubtree({ depth })
Reach for Postgres, SQLite, MongoDB, or Redis when:
- many unrelated systems need to write independently into the same database
- SQL compatibility, mature DBA tooling, replication, external roles, and ad-hoc reporting are central
- your data model is mostly records and indexes rather than a connected object graph
- you need a replicated consensus database rather than an embedded application-owned store
- Persist rich TypeScript object graphs with identity, shared references, cycles, classes,
Map,Set,Date,Buffer,bigint, typed arrays, and other JS values. - Exclude sensitive or runtime-only class fields with field decorators such as
@GraphVaultIgnore(),@GraphVaultIgnoreSave(), and@GraphVaultIgnoreLoad(). - Enforce simple DB-style field constraints with annotations such as
@GraphVaultRequired(),@GraphVaultUnique(),@GraphVaultEnum(...),@GraphVaultMin(...),@GraphVaultMax(...), and@GraphVaultReferenceExists(). - Query and batch-update committed graphs with GVQL, persistent indexes, execution plans, aggregates, previews, and GraphVault Studio.
- Run explicit transactions with rollback, optimistic or pessimistic locking, WAL recovery, fencing tokens, transaction metadata, and a tamper-evident hash chain.
- Operate stores with health/safety reports, verification, consistent backup, schema migrations, bounded subtree exports, and pluggable local, memory, HTTP, S3-compatible, SQLite-tested, and PostgreSQL-tested SQL storage targets.
GraphVault ships a NestJS module and a transaction decorator. The package smoke test installs a fresh NestJS 11 project, compiles this style of setup, writes data, verifies rollback, runs GVQL, closes the app, and reloads persisted data.
import { Injectable, Module } from "@nestjs/common";
import { GraphVaultModule, GraphVaultTransactional, StorageManager } from "@sprengmeister/graphvault";
class AppRoot {
notes: Array<{ id: string; title: string; status: "draft" | "approved" }> = [];
}
@Injectable()
class NotesService {
constructor(readonly storage: StorageManager<AppRoot>) {}
@GraphVaultTransactional({ mode: "pessimistic", managerProperty: "storage" })
async approve(id: string): Promise<void> {
const note = this.storage.root.notes.find((item) => item.id === id);
if (!note) throw new Error(`Unknown note ${id}`);
note.status = "approved";
}
}
@Module({
imports: [
GraphVaultModule.forRoot<AppRoot>({
global: true,
storageDirectory: "./data/graphvault",
rootFactory: () => new AppRoot(),
lockStrategy: "pessimistic",
transactionLog: "full",
recoverCommittedWal: true,
readCommittedWal: true,
staleLockTimeoutMs: 60_000,
}),
],
providers: [NotesService],
})
export class AppModule {}See NestJS integration.
GraphVault Studio is the separate graphical admin client for browsing, searching, verifying, backing up, and editing stores.
npm install graphvault-studio
npx graphvault-studio --dir ./data/graphvault --port 4177Then open http://127.0.0.1:4177.
The benchmark is reproducible with npm run benchmark; a comparative JSON/SQLite/GraphVault benchmark is available with npm run benchmark:compare. The full tables live in docs/BENCHMARKS.md. Latest local run on macOS/Apple Silicon:
| target | documents | storeRoot | indexed GVQL aggregate | reload | storage size |
|---|---|---|---|---|---|
| memory | 100 | 21.8 ms | 2.5 ms | 5.0 ms | - |
| filesystem/production | 100 | 60.1 ms | 2.2 ms | 31.9 ms | 0.44 MiB |
| filesystem/inspect | 100 | 2856.9 ms | 1.7 ms | 40.7 ms | 1.02 MiB |
| memory | 750 | 76.5 ms | 8.5 ms | 20.8 ms | - |
| filesystem/production | 750 | 323.1 ms | 8.9 ms | 197.0 ms | 2.88 MiB |
| filesystem/inspect | 750 | 19891.3 ms | 8.7 ms | 244.6 ms | 6.62 MiB |
The default filesystem profile is writeProfile: "production": binary object records, compact metadata, no debug-oriented duplicate object writes, and higher local write concurrency. Use writeProfile: "inspect" when human-readable JSON sidecars and conservative local flush behavior matter more than write throughput.
- Query and manipulate graphs with GVQL: graph patterns, joins, aggregates, execution plans, previews, and batch updates.
- Keep sensitive or runtime-only model fields out of persistence with field annotations.
- Keep invalid data out of committed stores with constraint annotations: required fields, type checks, enums, min/max, unique keys, and reference existence checks before WAL publish.
- Keep large stores fast with persistent indexes: property, composite, range, text/substring, full-text token, unique, partial/sparse, and expression indexes with verify/repair operations.
- Protect concurrent writers with transactions: rollback, optimistic and pessimistic locking, fencing tokens, stale-lock recovery, and NestJS decorators.
- Run production checks with operations guidance and ACID configuration: WAL recovery, strict durability, verification, backups, health reports, and safety profiles.
- Expose bounded graph slices with subtree loading and NestJS REST examples.
- Evolve persisted roots with schema migrations: storage-wide
upanddownsteps committed through the same transaction path as application writes.
GraphVault is intentionally application-owned embedded storage, not a drop-in replacement for a server database. It does not provide SQL wire-protocol compatibility, external user/role management, built-in replication, or distributed consensus. Multi-pod writers can share a store through the configured storage target and GraphVault's lock/transaction path, but high-availability replication and quorum semantics remain an infrastructure concern.
For critical production workloads, read Guarantees and boundaries, ACID configuration, and Production operations, run the storage-target conformance tests for any custom target, and use application-specific commitValidators for domain invariants.
npm run build
node examples/basic.mjsOpen the generated store with GraphVault Studio:
npm install graphvault-studio
npx graphvault-studio --dir ./graphvault-example-store --port 4177Then open http://127.0.0.1:4177.
For a fuller graph-shaped product demo, see the CaseGraph demo concept: an investigation/case-management app where people, companies, accounts, payments, documents, notes, hypotheses, and timeline events form one navigable object graph.
- Usage guide - modeling roots, registering classes, writing data, lazy data, cycles, verification, and lifecycle.
- Guarantees and boundaries - the precise contract for ACID-oriented behavior, storage-target requirements, tested paths, and when not to use GraphVault.
- ACID configuration - WAL, recovery, fencing tokens, validators, and durability tradeoffs.
- Production operations - production profiles, backup/restore, verification, monitoring, and known boundaries.
- GVQL guide - graph queries, indexed filtering, aggregates, execution plans, and mutation previews.
- Persistent indexes - storage-wide index configuration, consistency modes, and rebuild operations.
- Transactions and concurrency - optimistic and pessimistic locking for multi-pod writers.
- Storage configuration - local filesystem, memory, HTTP, S3-compatible, SQL, and operational options.
- NestJS integration - module setup, async config, multiple stores, and shutdown hooks.
- CaseGraph demo concept - the reference use case for graph-shaped, audit-heavy application data.
- API reference - public entry points and important options.
- Benchmarks - reproducible performance numbers and write profiles.
- 0.2.9 release notes - annotation-driven storage constraints for required, type, enum, min/max, unique, and reference checks.
- 0.2.8 release notes - public API TSDoc hardening and source-quality enforcement.
- 0.2.7 release notes - production write defaults and renamed write profiles.
- 0.2.6 release notes - explicit Node.js 22+ LTS support and CI validation.
- 0.2.5 release notes - field annotations for save/load filtering and current package baseline.
- 0.2.4 release notes - professional persistent indexes, index verification/repair, and current package baseline.
- 0.2.3 release notes - NestJS smoke tests and TypeScript developer polish.
- 0.2.0 release notes - production hardening, ACID-oriented recovery, subtree exports, and encrypted storage.
- 0.1.0 release notes - package overview for the first public release.
- Publishing checklist - local release checks, tagging, npm provenance, and GitHub topics.
If GraphVault has removed persistence glue from a TypeScript project, please star the repository. It is a small signal that helps developers with graph-shaped models discover the project.
npm ci
npm test
npm run benchmark:check
npm run pack:dry-run
npm run package:smokeThe smoke test stores and reloads a real object graph with class instances, shared references, maps, sets, and cycles. The package smoke test installs the generated tarball into a clean temporary project, verifies public and Studio-facing subpath imports, and compiles/runs a minimal NestJS app with injection, rollback, GVQL, health checks, backup, and restart persistence. CI runs on Node.js 22 LTS, 24 LTS, and 26, and includes a source-size quality gate.
This is an early TypeScript implementation. The storage format is GraphVault-native, not a database-server protocol and not a JVM binary format. The project is designed for production discipline: explicit commits, verification, recovery paths, locking, and readable storage artifacts.
