Type-safe, schema-driven JSON serialization for TypeScript and Effect
npm install effect-json



Type-safe, schema-driven JSON serialization for TypeScript and Effect.
effect-json provides multiple serialization backends (JSON, JSONC, SuperJSON, JSON Lines) unified under a single, Effect-native API with comprehensive error handling and schema validation.
> Status: Production ready โข Published on npm โข v0.1.0
- ๐ Type-safe: Schema-driven validation using Effect.Schema
- ๐ฏ Multiple backends: JSON (strict), JSONC (with comments), SuperJSON (type-preserving), JSON Lines (streaming/batch), TOON (experimental)
- โก Effect-native: All operations return Effects for composability
- ๐ Precise errors: Line/column information for parse errors, detailed validation errors
- ๐งช Fully tested: 136 tests with comprehensive coverage
- ๐ Pluggable: Extensible backend system
``bash
bun add effect-json effect
Quick Start
$3
`typescript
import { Schema, Effect } from "effect";
import * as Json from "effect-json";const UserSchema = Schema.Struct({
id: Schema.Number,
name: Schema.String,
email: Schema.String,
});
const jsonString = '{"id": 1, "name": "Paul", "email": "paul@example.com"}';
const effect = Json.parse(UserSchema, jsonString);
const user = await Effect.runPromise(effect);
// { id: 1, name: "Paul", email: "paul@example.com" }
`$3
`typescript
const jsonc = ;const effect = Json.parseJsonc(UserSchema, jsonc);
const user = await Effect.runPromise(effect);
`$3
`typescript
const ComplexSchema = Schema.Struct({
id: Schema.Number,
createdAt: Schema.DateFromSelf,
tags: Schema.Array(Schema.String),
});const data = {
id: 1,
createdAt: new Date("2025-01-01"),
tags: ["typescript", "effect"],
};
// Stringify with type preservation
const stringified = await Effect.runPromise(
Json.stringifySuperjson(ComplexSchema, data)
);
// Parse back with Date as Date (not string)
const reparsed = await Effect.runPromise(
Json.parseSuperjson(ComplexSchema, stringified)
);
// reparsed.createdAt is a Date object
`$3
TOON is a compact, human-readable encoding of the JSON data model, optimized for LLM prompts and responses.
> Note: The TOON integration is experimental and subject to change as the TOON specification and ecosystem evolve.
`typescript
import { Effect, Schema } from "effect";
import { parseToon, stringifyToon } from "effect-json/Toon";const User = Schema.Struct({
id: Schema.Number,
name: Schema.String,
tags: Schema.Array(Schema.String)
});
type User = Schema.Schema.Type;
const user: User = {
id: 1,
name: "Alice",
tags: ["admin", "beta"]
};
// Domain type -> TOON string
const program = stringifyToon(User, user).pipe(
Effect.tap((toon) => Effect.log("TOON:", toon)),
Effect.flatMap((toon) => parseToon(User, toon)), // TOON string -> domain type
);
// Run with your Effect runtime...
await Effect.runPromise(program);
`$3
JSON Lines is a format for storing structured data where each line is a valid JSON value. It's commonly used for logs, event streams, and LLM training datasets.
#### Batch API (Arrays)
`typescript
import { Effect, Schema } from "effect";
import { parseJsonLines, stringifyJsonLines } from "effect-json/JsonLines";const Event = Schema.Struct({
id: Schema.String,
level: Schema.Literal("info", "warn", "error"),
message: Schema.String
});
const events = [
{ id: "1", level: "info" as const, message: "Started" },
{ id: "2", level: "error" as const, message: "Boom" }
];
const program = stringifyJsonLines(Event, events).pipe(
Effect.tap((jsonl) => Effect.log("JSONL:\n" + jsonl)),
Effect.flatMap((jsonl) => parseJsonLines(Event, jsonl)),
Effect.tap((parsed) => Effect.log("Parsed:", parsed))
);
await Effect.runPromise(program);
// Output:
// JSONL:
// {"id":"1","level":"info","message":"Started"}
// {"id":"2","level":"error","message":"Boom"}
`#### Streaming API (Large Files)
`typescript
import { Stream } from "effect";
import { streamParseJsonLines, streamStringifyJsonLines } from "effect-json/JsonLines";// Parse stream of JSONL chunks (handles arbitrary chunk splits)
const jsonlChunks = Stream.fromIterable([
'{"id":"1","level":"info","message":"Started"}\n{"id":"',
'2","level":"error","message":"Boom"}\n'
]);
const parsedEvents = streamParseJsonLines(Event, jsonlChunks);
const program = parsedEvents.pipe(
Stream.tap((e) => Stream.fromEffect(Effect.log("Event:", e))),
Stream.runCollect
);
await Effect.runPromise(program);
``$3
effect-json uses Effect's powerful error handling capabilities. All functions return
Effect which you can handle using:#### Using Effect.runPromise (Recommended)
`typescript
// Simple case - let errors throw
const user = await Effect.runPromise(Json.parse(UserSchema, jsonString));// Handle errors explicitly with Effect.either
const result = await Effect.runPromise(
Effect.either(Json.parse(UserSchema, badJson))
);
if (result._tag === "Left") {
const error = result.left;
console.log(error.message); // Parse error details
console.log(error.line); // Line number
console.log(error.column); // Column number
console.log(error.snippet); // Code snippet with pointer
}
`#### Using catchTag for Specific Errors
`typescript
const effect = Json.parse(UserSchema, badJson).pipe(
Effect.catchTag("ParseError", (error) => {
console.error(Parse failed at ${error.line}:${error.column});
return Effect.succeed(DEFAULT_USER);
}),
Effect.catchTag("ValidationError", (error) => {
console.error(Validation failed: ${error.message});
return Effect.succeed(DEFAULT_USER);
}),
);const user = await Effect.runPromise(effect);
`#### Fallback Strategies with orElse
`typescript
const effect = Effect.orElse(
Json.parse(UserSchema, jsonString), // Try JSON first
() => Json.parseJsonc(UserSchema, jsonString) // Fallback to JSONC
);const user = await Effect.runPromise(effect);
`> Why no
parseSync()? effect-json is designed to work seamlessly with Effect's ecosystem. Using Effect.runPromise ensures proper error handling, composability, and integration with Effect's runtime. For synchronous needs, simply await Effect.runPromise(...) - it's concise and idiomatic.API
$3
-
parse(schema, input) - Parse JSON with schema validation
- parseJsonc(schema, input) - Parse JSONC (strips comments)
- parseSuperjson(schema, input) - Parse SuperJSON with type preservation
- parseToon(schema, input) - Parse TOON string (experimental)
- parseJsonLines(schema, input) - Parse JSON Lines (JSONL/NDJSON) batch
- streamParseJsonLines(schema, inputStream) - Parse JSON Lines stream$3
-
stringify(schema, value, options?) - Stringify to JSON
- stringifyJsonc(schema, value, options?) - Stringify to JSON (same as stringify)
- stringifySuperjson(schema, value, options?) - Stringify with type metadata
- stringifyToon(schema, value, options?) - Stringify to TOON (experimental)
- stringifyJsonLines(schema, values, options?) - Stringify array to JSON Lines
- streamStringifyJsonLines(schema, valuesStream, options?) - Stringify stream to JSON Lines$3
-
ParseError - JSON parsing failed (includes line/column)
- JsonLinesParseError - JSON Lines parsing failed (includes line number)
- ValidationError - Schema validation failed
- StringifyError - Stringification failed (e.g., circular references)Development
$3
`
packages/effect-json/
โโโ src/
โ โโโ index.ts # Public API
โ โโโ api.ts # Core functions
โ โโโ backends/ # JSON, JSONC, SuperJSON
โ โโโ errors.ts # Error types
โ โโโ __tests__/ # Test suites
`$3
`bash
Install dependencies
bun installBuild
bun run buildTest
bun run test
bun run test:watch
bun run test:coverageLint & Format
bun run checkType check
bun run typecheck
`Tech Stack
- Runtime: Bun
- Monorepo: Turborepo
- Testing: Vitest (136 tests, 85%+ coverage)
- Linting/Formatting: Biome
- TypeScript: Strict mode
- Effect: Latest
- Bundling: Vite
- CI/CD: GitHub Actions (multi-platform testing, automated releases)
Documentation
$3
-
packages/effect-json/ARCHITECTURE.md - Implementation details, patterns, and best practices$3
See the
/docs directory for project planning:
- MRD.md - Market requirements
- PRD.md - Product requirements
- Architecture.md - Technical design
- ImplementationPlan.md - Development roadmapContributing
Contributions are welcome! Please read our Contributing Guide for details on our development process, coding standards, and how to submit pull requests.
$3
`bash
Clone the repository
git clone https://github.com/PaulJPhilp/effect-json.git
cd effect-jsonInstall dependencies
bun installRun tests
bun testRun linting
bun run checkBuild
bun run build
``See CLAUDE.md for detailed development guidelines and architecture information.
Security issues should be reported privately. See our Security Policy for details.
MIT ยฉ Paul J. Philp
- npm Package: https://www.npmjs.com/package/effect-json
- GitHub Repository: https://github.com/PaulJPhilp/effect-json
- Issue Tracker: https://github.com/PaulJPhilp/effect-json/issues
- Discussions: https://github.com/PaulJPhilp/effect-json/discussions
- Changelog: CHANGELOG.md
---