Introducing handlejson - Safe JSON Parsing Without the try-catch Spam
Source: Dev.to
Why a wrapper for JSON.parse?
I got tired of writing the same try‑catch boilerplate everywhere:
let data;
try {
data = JSON.parse(str);
} catch {
data = null;
}
Whether it was reading from localStorage, handling API responses, or parsing user input, the pattern repeated thousands of times.
Beyond the verbosity, native JSON has other quirks:
JSON.stringifythrows on circular references.Dateobjects become empty objects when stringified.- No built‑in way to validate the resulting structure.
Introducing handlejson
handlejson is a tiny wrapper around the native JSON functions that smooths out these edge cases.
import { parse, stringify } from 'handlejson';
// Safe parsing – returns null on error, no try‑catch needed
const user = parse(localStorage.getItem('user'));
const config = parse(invalidJson, { default: {} });
// Handles circular references automatically
const obj = { a: 1 };
obj.self = obj;
stringify(obj); // '{"a":1,"self":"[Circular]"}'
// BigInt support
stringify({ value: BigInt(123) }); // '{"value":"123n"}'
The library is ~2.8 KB gzipped, has zero dependencies, and ships with full TypeScript typings. It works both in the browser and in Node.js.
New features in v0.2.0
Automatic date handling
Previously you needed a custom reviver to turn ISO strings back into Date objects:
// The old way
const user = parse(jsonString, {
reviver: (key, value) => {
if (key.endsWith('At') && typeof value === 'string') {
const date = new Date(value);
return isNaN(date.getTime()) ? value : date;
}
return value;
}
});
Now it’s a single option:
const user = parse(jsonString, { dates: true });
// Dates are automatically converted
Stringifying also respects this option:
stringify({ createdAt: new Date() }, { dates: true });
// → '{"createdAt":"2023-01-01T10:00:00.000Z"}'
Simple schema validation
When API responses have the wrong types, you can validate the parsed object against a lightweight schema:
const schema = {
name: 'string',
age: 'number',
active: 'boolean',
address: {
street: 'string',
zip: 'number'
}
};
const user = parse(apiResponse, { schema });
// Returns null if validation fails
The validation runs after parsing, producing clear error messages such as:
Field 'age': expected number, got string
Streaming large JSON files
For multi‑megabyte payloads you can parse incrementally:
import { parseStream } from 'handlejson';
const result = await parseStream(response.body, {
onProgress: parsed => console.log('Loading...', parsed),
onError: err => console.error('Error:', err)
});
if (result.complete) {
console.log('Data:', result.data);
}
This avoids loading the entire file into memory at once.
Before vs. after
Before
function loadUser() {
const stored = localStorage.getItem('user');
if (!stored) return null;
try {
const parsed = JSON.parse(stored);
if (parsed.createdAt) {
parsed.createdAt = new Date(parsed.createdAt);
}
return parsed;
} catch {
return null;
}
}
After
function loadUser() {
return parse(localStorage.getItem('user'), {
default: null,
dates: true
});
}
The new API is far cleaner, and you can add schema validation if desired.
How does it compare?
Other libraries exist, but many are heavier or include features you may never need. handlejson focuses on the most common pain points:
- Small bundle size (~2.8 KB gzipped)
- Zero runtime dependencies
- Consistent API across browser and Node.js
- Optional, opt‑in features that won’t break existing code
Installation
npm install handlejson
The v0.2.0 release is live on npm: .
All new functionality is optional, so upgrading won’t affect code that already uses the earlier version.
Closing thoughts
If you find yourself writing try‑catch blocks for JSON.parse all the time, handlejson can tidy up your code and catch common bugs early. If native JSON works fine for you, there’s no need to add a dependency.
What’s your experience with JSON parsing boilerplate? Feel free to share your thoughts!