Skip to Content

Step 4 — JSON parse & stringify

We have a Bookmark type and a way to validate one. Now let’s give the service some state: persist a list of bookmarks to bookmarks.json on disk, and load it back on startup.

This is where two of typia’s most pragmatic functions earn their keep:

  • typia.json.assertParse — JSON.parse + validation in one call. Refuses to start the program if the file on disk has been hand-edited into nonsense.
  • typia.json.assertStringify — JSON.stringify, but much faster (typia emits a hand-rolled stringifier for your type), with a quick “is this actually a Bookmark?” check up front.

The store

Make a new file:

src/store.ts
import { promises as fs } from "node:fs"; import typia from "typia"; import { Bookmark } from "./bookmark"; const FILE = "bookmarks.json"; /** Load and validate the bookmarks file. */ export async function loadAll(): Promise<Bookmark[]> { try { const text = await fs.readFile(FILE, "utf8"); return typia.json.assertParse<Bookmark[]>(text); } catch (err) { if ((err as NodeJS.ErrnoException).code === "ENOENT") return []; throw err; // bad JSON or schema mismatch → surface it } } /** Atomically write all bookmarks back to disk. */ export async function saveAll(items: Bookmark[]): Promise<void> { const text = typia.json.assertStringify<Bookmark[]>(items); await fs.writeFile(FILE, text, "utf8"); }

Two things worth pointing out:

  1. assertParse<Bookmark[]> doesn’t only validate the array — it also checks every constraint on every field (URL format, max length, …). Edit bookmarks.json by hand and put "createdAt": "yesterday" in one entry, the next loadAll() throws on that field’s path. You get to choose whether to surface the error or fall back to an empty list, but you never get a half-corrupt in-memory model.
  2. assertStringify<Bookmark[]> is the validating sibling of JSON.stringify. It runs your data through a quick assert first (so you don’t accidentally serialize garbage), then runs typia’s bespoke serializer — which is several times faster than the native one because it knows exactly which fields exist on Bookmark.

Use typia.json.stringify<T> (no assert) when you’ve just constructed the value yourself and there’s no way for it to be wrong. Otherwise, assertStringify is the safe default.

Use it

src/04-store.ts
import typia from "typia"; import { Bookmark } from "./bookmark"; import { loadAll, saveAll } from "./store"; const main = async () => { const existing = await loadAll(); console.log(`Loaded ${existing.length} bookmark(s).`); // Add one const fresh: Bookmark = typia.random<Bookmark>(); await saveAll([...existing, fresh]); // Read again to verify the round-trip const after = await loadAll(); console.log(`Now ${after.length}.`); }; main().catch(console.error);

Run it twice. The first time it creates bookmarks.json with one bookmark. The second time it loads that one, appends another, and writes both back — so the file grows by one entry per run.

Terminal
npx ttsx src/04-store.ts npx ttsx src/04-store.ts

Corrupt the file on purpose

To see the safety net in action, open bookmarks.json and break one entry — for example, change the createdAt value to "yesterday" so the file looks like:

bookmarks.json
[ { "id": "...", "url": "https://...", "title": "...", "tags": ["..."], "createdAt": "yesterday" } ]

Then re-run:

Terminal
npx ttsx src/04-store.ts
TypeGuardError: invalid type on $input[0].createdAt, expect to be string & Format<"date-time">

assertParse refused to hand back a half-valid array. You can decide to fall back to [], log the corrupt record, prompt the user — but you never silently load broken data.

The variants you didn’t need today

FunctionWhen
typia.json.isParse<T>”Try to parse, give me null if anything’s wrong”
typia.json.validateParse<T>”Give me every error in bookmarks.json so I can report all of them at once”
typia.json.isStringify<T>Like assertStringify but returns null instead of throwing
typia.json.validateStringify<T>Validate, then serialize, returning IValidation<string>
typia.json.stringify<T>Fastest. Skips validation. Trust the caller.

See json.parse and json.stringify for full signatures.

A note on Primitive<T>

If you look at the return type of assertParse<Bookmark[]>, it’s Primitive<Bookmark[]>. That’s typia’s “what survives a JSON round-trip” projection. For Bookmark it’s identical to Bookmark (nothing in it is Date or bigint or Set), so you can ignore the distinction.

If your type does contain a Date, Primitive<T> rewrites that property to string & tags.Format<"date-time"> — because that’s what JSON.stringify(date) produces and what JSON.parse gives you back. Same for Map, Set, and friends collapsing to plain objects. The Primitive type tells you about a few classes of “this won’t survive JSON” mistakes at compile time. See json.parse > Primitive for the details.

What you’ve done

  • Saved typed data to disk with assertStringify
  • Loaded it back with assertParse and watched it refuse corrupt input
  • Picked the right variant for each situation

Next we’ll do something more ambitious: hand the bookmark service to an LLM and let it create bookmarks from natural language.

→ Step 5: LLM function calling

Last updated on