ProvenPin Install

lowdb JSON store: working versions (tested 2026-10-16)

Seed default data, read and write nested collections, and verify a persistence roundtrip with lowdb's JSONFilePreset and MemoryPreset fully offline.

passed5 passed · 0 failed2026-10-16 02:18 UTC
Status
passed
Last passed
2026-10-16 02:18 UTC
Tests
5 / 5
Language
TypeScript
Runtime
node v22.23.3
pinned versions3
lowdb@7.0.1@types/node@26.6.5tsx@4.23.15
run it
npm install --no-audit --no-fund --ignore-scripts=false
npm test
test environment
node v22.23.3
linux 6.17.0-1022-azure
x64
runner: github-actions 38839686104

The code

Exactly the files that passed. Your agent can pull them with get_recipe("lowdb-json-store") (install), or see them on GitHub.

package.json
{
  "name": "lowdb-json-store",
  "private": true,
  "type": "module",
  "scripts": {
    "test": "node --import tsx --test test/*.test.ts"
  },
  "dependencies": {
    "lowdb": "7.0.1"
  },
  "devDependencies": {
    "@types/node": "26.6.5",
    "tsx": "4.23.15"
  }
}
src/store.ts
import { Low, Memory } from "lowdb";
import { JSONFilePreset } from "lowdb/node";

export type User = { id: string; name: string };

export type Comment = { id: string; postId: string; body: string };

export type Post = {
  id: string;
  title: string;
  authorId: string;
  tags: string[];
  comments: Comment[];
};

export type Data = {
  users: User[];
  posts: Post[];
};

export type Store = Low<Data>;

export const defaultData: Data = {
  users: [],
  posts: [],
};

export async function openStore(filename: string): Promise<Store> {
  const store = await JSONFilePreset<Data>(filename, structuredClone(defaultData));
  // Ensure the seed file exists for callers that read it immediately.
  await store.write();
  return store;
}

export async function openMemoryStore(): Promise<Store> {
  const store = new Low<Data>(new Memory<Data>(), structuredClone(defaultData));
  await store.read();
  return store;
}

export async function addUser(store: Store, user: User): Promise<void> {
  await store.update((data) => {
    if (data.users.some((u) => u.id === user.id)) {
      throw new Error(`user already exists: ${user.id}`);
    }
    data.users.push(user);
  });
}

export async function addPost(store: Store, post: Post): Promise<void> {
  await store.update((data) => {
    const author = data.users.find((u) => u.id === post.authorId);
    if (!author) {
      throw new Error(`unknown author: ${post.authorId}`);
    }
    data.posts.push(post);
  });
}

export async function addComment(
  store: Store,
  postId: string,
  comment: Comment,
): Promise<void> {
  await store.update((data) => {
    const post = data.posts.find((p) => p.id === postId);
    if (!post) {
      throw new Error(`unknown post: ${postId}`);
    }
    post.comments.push(comment);
  });
}

export function getPost(store: Store, postId: string): Post | undefined {
  return store.data.posts.find((p) => p.id === postId);
}

export function getPostsByTag(store: Store, tag: string): Post[] {
  return store.data.posts.filter((p) => p.tags.includes(tag));
}

export function getUserPosts(store: Store, userId: string): Post[] {
  return store.data.posts.filter((p) => p.authorId === userId);
}

export function countComments(store: Store, postId: string): number {
  return getPost(store, postId)?.comments.length ?? 0;
}
test/store.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import { mkdtemp, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import {
  addComment,
  addPost,
  addUser,
  countComments,
  defaultData,
  getPost,
  getPostsByTag,
  getUserPosts,
  openMemoryStore,
  openStore,
} from "../src/store.ts";

async function withTempFile(fn: (file: string) => Promise<void>): Promise<void> {
  const dir = await mkdtemp(join(tmpdir(), "lowdb-"));
  try {
    await fn(join(dir, "db.json"));
  } finally {
    await rm(dir, { recursive: true, force: true });
  }
}

test("seeds default data into a missing JSON file", async () => {
  await withTempFile(async (file) => {
    const store = await openStore(file);
    assert.deepEqual(store.data, defaultData);
    const raw = await readFile(file, "utf8");
    assert.deepEqual(JSON.parse(raw), defaultData);
  });
});

test("persists nested collections across reopen", async () => {
  await withTempFile(async (file) => {
    const store = await openStore(file);
    await addUser(store, { id: "u1", name: "Ada" });
    await addPost(store, {
      id: "p1",
      title: "Hello lowdb",
      authorId: "u1",
      tags: ["intro", "json"],
      comments: [],
    });
    await addComment(store, "p1", { id: "c1", postId: "p1", body: "nice" });
    await addComment(store, "p1", { id: "c2", postId: "p1", body: "agree" });

    const reopened = await openStore(file);
    const post = getPost(reopened, "p1");
    assert.ok(post);
    assert.deepEqual(post!.tags, ["intro", "json"]);
    assert.equal(countComments(reopened, "p1"), 2);
    assert.deepEqual(getUserPosts(reopened, "u1").map((p) => p.id), ["p1"]);
    assert.deepEqual(getPostsByTag(reopened, "json").map((p) => p.id), ["p1"]);
  });
});

test("supports direct data mutation followed by write()", async () => {
  await withTempFile(async (file) => {
    const store = await openStore(file);
    await addUser(store, { id: "u9", name: "Linus" });
    store.data.posts.push({
      id: "p9",
      title: "Manual write",
      authorId: "u9",
      tags: ["manual"],
      comments: [],
    });
    await store.write();

    const reopened = await openStore(file);
    assert.equal(getPost(reopened, "p9")?.title, "Manual write");
  });
});

test("Memory adapter keeps data in memory only", async () => {
  const store = await openMemoryStore();
  assert.deepEqual(store.data, defaultData);
  await addUser(store, { id: "u2", name: "Grace" });
  await addPost(store, {
    id: "p2",
    title: "Ephemeral",
    authorId: "u2",
    tags: ["memory"],
    comments: [],
  });
  assert.deepEqual(getUserPosts(store, "u2").map((p) => p.id), ["p2"]);
});

test("rejects writes that violate referential integrity", async () => {
  const store = await openMemoryStore();
  await assert.rejects(
    () =>
      addPost(store, {
        id: "p3",
        title: "orphan",
        authorId: "missing",
        tags: [],
        comments: [],
      }),
    /unknown author/,
  );
  assert.equal(store.data.posts.length, 0);
  await assert.rejects(
    () => addComment(store, "nope", { id: "c9", postId: "nope", body: "x" }),
    /unknown post/,
  );
});

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.