DEV Community

Cover image for Stop Writing Try-Catch Blocks for JSON.parse - handlejson v1.0.0 is Production Ready
Chintan Shah
Chintan Shah

Posted on

Stop Writing Try-Catch Blocks for JSON.parse - handlejson v1.0.0 is Production Ready

A few weeks ago, I introduced handlejson with v0.2.0 - a simple library to eliminate try-catch spam when parsing JSON. After development from December 2025 to January 2026 and 244 tests, v1.0.0 is production-ready.

The Journey to v1.0.0

When I first released handlejson, it solved a real problem - eliminating try-catch boilerplate. Fast forward to today, and it's production-ready with comprehensive tests, performance improvements, and enterprise-grade features.

What Changed Since v0.2.0

The core idea remains the same - a focused solution that returns null on error instead of throwing. Here's what's been added:

v0.3.0 (January 10)

  • Better validation errors: tryValidate() now returns detailed error info (path, expected, actual)
  • Optional fields: Prefix types with ? to mark fields as optional (age: '?number')
  • Array validation: Validate array item types, not just array presence
  • CI/CD: GitHub Actions workflows for automated testing

v1.0.0 (January 30) - Production Ready

  • 244 comprehensive tests covering all edge cases
    • Security features (maxSize, maxDepth, safeKeys)
    • Date handling (ISO strings, timestamps)
    • Circular reference handling
    • Stream parsing for large files
    • Schema validation with detailed errors
  • Performance improvements: 2-3x faster parsing (5.2M ops/s vs ~2M ops/s)
    • Date regex compiled once instead of per call
    • Smart object copying (only when dangerous keys found) - optimized key sanitization
  • Enhanced security: Improved sanitizeKeys performance for prototype pollution protection
  • Enhanced error messages: Better formatting with position and context
  • Memory efficiency: Improved garbage collection behavior
  • CI/CD: Automated testing across Node 18, 20, 22
  • Documentation: Performance benchmarks and enhanced docs

How many times have you written this?

let data
try {
  data = JSON.parse(str)
} catch {
  data = null
}
Enter fullscreen mode Exit fullscreen mode

If you're like me, probably dozens of times. Every API response, every localStorage read, every user input - same pattern, same boilerplate.

After writing this pattern one too many times, I built handlejson.

What is handlejson?

It's a focused wrapper around JSON.parse that returns null on error instead of throwing. This simple change eliminates the try-catch boilerplate you write everywhere.

import { parse } from 'handlejson'

const data = parse(str) // null if invalid
const data = parse(str, { default: {} }) // {} if invalid
Enter fullscreen mode Exit fullscreen mode

Simple, but genuinely useful.

Why I built it

The breaking point was a project parsing JSON from APIs, localStorage, user input, and config files. Every single parse needed a try-catch. My code was cluttered with error handling boilerplate.

I checked existing solutions, but they were either too heavy, missing security features, or had dependencies I didn't want. So I built my own.

What makes it useful

1. Security options built-in

When handling untrusted input, you need protection. Most JSON libraries don't include this:

const safe = parse(userInput, {
  maxSize: 10 * 1024 * 1024,  // Prevent memory exhaustion attacks
  maxDepth: 100,                // Prevent stack overflow from deeply nested JSON
  safeKeys: true                // Block prototype pollution (__proto__, constructor, prototype)
})
Enter fullscreen mode Exit fullscreen mode

These aren't theoretical - they're real attack vectors:

  • maxSize: Prevents attackers from sending huge JSON payloads that exhaust memory
  • maxDepth: Prevents deeply nested JSON that causes stack overflow
  • safeKeys: Blocks prototype pollution attacks that can lead to code execution

Having them built-in saves you from implementing them yourself. In v1.0.0, the sanitizeKeys function was optimized to only copy objects when dangerous keys are actually found, improving performance.

2. Better error messages

When debugging JSON errors, knowing where the error is helps:

import { parseWithDetails } from 'handlejson'

const result = parseWithDetails('{"name":"John", invalid}')
if (!result.success) {
  console.log(result.position)  // 18
  console.log(result.context)   // Shows surrounding context
  console.log(result.error)     // Detailed error message
}
Enter fullscreen mode Exit fullscreen mode

Much better than "Unexpected token" with no context.

3. Schema validation

Basic type checking without extra dependencies:

const schema = { name: 'string', age: 'number' }
const user = parse('{"name":"John","age":30}', { schema })
// Returns null if validation fails
Enter fullscreen mode Exit fullscreen mode

What's new in v0.3.0+: Optional fields and better error messages:

const schema = {
  name: 'string',
  age: '?number',      // Optional field
  email: '?string'     // Optional field
}

const [valid, error] = tryValidate(data, schema)
if (!valid) {
  console.log(error.path)      // 'age'
  console.log(error.expected)  // 'number'
  console.log(error.actual)    // 'string'
}
Enter fullscreen mode Exit fullscreen mode

Perfect for validating API responses, configuration files, and user input without adding heavy dependencies. Handles optional fields, nested objects, and arrays with detailed error messages.

4. Stream parsing

For large files, parse in chunks:

import { parseStream } from 'handlejson'

const result = await parseStream(largeFileStream, {
  onProgress: (parsed) => console.log('Progress:', parsed)
})
Enter fullscreen mode Exit fullscreen mode

Handles files without loading everything into memory.

Performance

Benchmarks against native JSON:

  • Small JSON (<1KB): 5.2M ops/s (vs ~6M for native)
  • With security options: 3.4M ops/s
  • Overhead exists, but minimal

For most applications, the convenience and security features are worth the minimal performance overhead. The 5.2M ops/s performance handles high-throughput scenarios while providing better error handling and built-in security.

Real-world use cases

API responses:

const response = await fetch('/api/user')
const user = parse(await response.text(), { default: {} })
// Always get an object, never crashes
Enter fullscreen mode Exit fullscreen mode

LocalStorage:

const saved = parse(localStorage.getItem('data'), { default: null })
// Safe, no try-catch needed
Enter fullscreen mode Exit fullscreen mode

User input with security:

const userData = parse(userInput, {
  maxSize: 10 * 1024 * 1024,
  maxDepth: 100,
  safeKeys: true
})
Enter fullscreen mode Exit fullscreen mode

When to use it

✅ Parsing API responses

✅ Handling user input

✅ Processing configuration files

✅ Debugging JSON errors

✅ Handling untrusted input (security options)

Why it matters

handlejson solves a problem every developer faces daily - repetitive try-catch boilerplate. Beyond convenience, it adds production-ready features like built-in security, detailed error messages, and comprehensive test coverage. The security options protect against real attack vectors, making it ideal for production applications handling untrusted input.

Production ready

After development from December 2025 to January 2026, v1.0.0 is ready:

  • 244 tests covering edge cases (security, dates, circular refs, streams, validation)
  • Zero dependencies
  • 1.5KB gzipped
  • TypeScript-first
  • CI/CD with GitHub Actions (tests on Node 18, 20, 22)
  • Performance: 5.2M ops/s for small JSON (vs ~6M for native)

The main difference from v0.2.0 is production-ready quality - comprehensive tests, performance optimizations, and better documentation. The API hasn't changed, so existing code works without modifications.

Try it out

npm install handlejson
Enter fullscreen mode Exit fullscreen mode
import { parse, stringify } from 'handlejson'

const data = parse('{"name":"John"}')  // { name: 'John' }
const json = stringify({ name: 'John' })  // '{"name":"John"}'
Enter fullscreen mode Exit fullscreen mode

Links:

If you're new to handlejson, check out my original introduction article where I covered the basics and v0.2.0 features.

Top comments (0)