XyPriss JSON (XJson) API - Extended JSON Response Handler
Overview
The XJson API is an advanced JSON response handler designed to solve serialization issues and handle large data responses without limitations. It provides enhanced JSON serialization capabilities that overcome common problems with standard JSON responses, particularly for complex data structures and large payloads.
Problem Solved
When making requests to XyPriss applications, developers often encounter serialization errors, especially with:
- BigInt values that can't be directly serialized by
JSON.stringify() - Circular references in object structures
- Very large data sets that cause memory issues
- Complex nested objects with mixed data types
The traditional .json() method fails with errors like:
{
"error": "Serialization failed",
"message": "Unable to serialize response object",
"originalError": "JSON Parse error: Unexpected identifier \"Serialization\""
}
Solution: XJson API
The XJson API provides a robust alternative that:
✅ Handles BigInt serialization - Converts BigInt values to strings automatically
✅ Prevents circular reference errors - Safely handles cyclic object structures
✅ Supports streaming for large data - Streams responses in chunks to avoid memory issues
✅ Maintains backward compatibility - Works alongside existing .json() methods
✅ Provides configurable options - Customizable serialization behavior
Quick Start
Basic Usage
import { createServer } from "@xypriss/core";
const app = createServer({});
// Use the new XJson endpoint
app.get("/api/data", (req, res) => {
const data = {
success: true,
file: {
id: "cmj17uh7f00002ef4otv5rqlj",
size: 18n, // BigInt value
filename: "document.pdf",
metadata: {
// Complex nested structure
author: "John Doe",
tags: ["important", "urgent"],
},
},
};
// Use res.xJson() instead of res.json()
res.xJson(data);
});
// Traditional JSON endpoint (still works for simple data)
app.get("/api/simple", (req, res) => {
res.json({ message: "Hello World" });
});
app.start();
Response Examples
Traditional JSON endpoint:
$ curl http://localhost:8085/api/data
{"error":"Serialization failed","message":"Unable to serialize response object"}
XJson endpoint:
$ curl http://localhost:8085/api/data.xJson
{"success":true,"file":{"id":"cmj17uh7f00002ef4otv5rqlj","size":"18","filename":"document.pdf","metadata":{"author":"John Doe","tags":["important","urgent"]}}}
API Reference
res.xJson(data, options?)
The main method for sending XJson responses.
Parameters:
data(any): The data to serialize and sendoptions(XJsonOptions, optional): Configuration options
Returns: void
XJsonOptions Interface
interface XJsonOptions {
/**
* Maximum depth for object serialization
* @default 20
*/
maxDepth?: number;
/**
* Maximum string length before truncation
* @default 10000
*/
truncateStrings?: number;
/**
* Include non-enumerable properties
* @default false
*/
includeNonEnumerable?: boolean;
/**
* Enable streaming for very large responses
* @default true
*/
enableStreaming?: boolean;
/**
* Chunk size for streaming responses (in bytes)
* @default 1024 * 64 (64KB)
*/
chunkSize?: number;
}
Advanced Usage
Custom Configuration
import { XJsonResponseHandler } from "@xypriss/core";
// Create a custom XJson handler with specific options
const customHandler = new XJsonResponseHandler({
maxDepth: 50, // Allow deeper nesting
truncateStrings: 50000, // Allow longer strings
enableStreaming: true, // Enable streaming for large data
chunkSize: 1024 * 128, // 128KB chunks
});
// Use in middleware
app.use(
XJsonResponseHandler.createMiddleware({
maxDepth: 30,
truncateStrings: 20000,
})
);
// Use directly in routes
app.get("/large-data", (req, res) => {
const largeData = generateLargeDataset();
res.xJson(largeData, {
maxDepth: 40,
enableStreaming: true,
});
});
Handling Complex Data Structures
app.get("/complex-data", (req, res) => {
const complexData = {
// Circular reference handling
user: {
id: 1,
name: "Alice",
// This would normally cause serialization errors
// bestFriend: null // (circular reference)
},
// BigInt values
bigNumber: 12345678901234567890n,
// Mixed data types
metadata: {
timestamp: new Date(),
regex: /test/gi,
bigIntArray: [1n, 2n, 3n],
buffer: Buffer.from("test data"),
},
// Large nested objects
deep: {
level1: {
level2: {
level3: {
data: "This is deeply nested",
},
},
},
},
};
// XJson handles all of this automatically
res.xJson(complexData);
});
Streaming Large Responses
app.get("/stream-data", (req, res) => {
const largeData = {
type: "large_dataset",
items: Array.from({ length: 10000 }, (_, i) => ({
id: i,
data: `Item ${i}`,
bigValue: BigInt(i) * 1000000n,
})),
};
// This will automatically stream the response
res.xJson(largeData, {
enableStreaming: true,
chunkSize: 1024 * 64, // 64KB chunks
});
});
Content-Type Headers
XJson responses automatically set the appropriate Content-Type header:
// XJson responses
Content-Type: application/json
// Streamed XJson responses (for large data)
Content-Type: application/json
Content-Length: [calculated size]
Performance Considerations
Memory Efficiency
- Streaming enabled by default for responses larger than 64KB
- Chunk-based processing prevents memory overflow
- Lazy serialization only processes data when needed
Speed Optimization
- Fast path for simple data that can be serialized normally
- Safe path fallback only when standard serialization fails
- Minimal overhead for compatible data types
When to Use XJson vs Regular JSON
Use XJson (res.xJson()) when:
- Dealing with BigInt values
- Handling large datasets (>64KB)
- Complex nested objects
- Potential circular references
- Mixed data types (Dates, Buffers, RegExp, etc.)
Use Regular JSON (res.json()) when:
- Simple data structures
- Performance is critical
- Data is known to be serialization-safe
- Backward compatibility is required
Error Handling
XJson provides detailed error information when serialization fails:
app.get("/error-test", (req, res) => {
try {
const problematicData = /* ... */;
res.xJson(problematicData);
} catch (error) {
// XJson handles errors gracefully
res.status(500).json({
error: "Internal Server Error",
message: "Failed to serialize response",
details: error.message
});
}
});
Integration with Middleware
XJson integrates seamlessly with XyPriss middleware:
import { createServer } from "@xypriss/core";
import { securityMiddleware } from "@xypriss/security";
const app = createServer({});
// Security middleware works with XJson
app.use(
securityMiddleware({
enabled: true,
level: "enhanced",
})
);
// Cache middleware
app.get("/cached-data", cacheMiddleware(), (req, res) => {
const data = getExpensiveData();
res.xJson(data); // Cached properly
});
Migration Guide
From Regular JSON to XJson
Before:
app.get("/data", (req, res) => {
const data = { value: 123n }; // BigInt
res.json(data); // ❌ Fails
});
After:
app.get("/data", (req, res) => {
const data = { value: 123n }; // BigInt
res.xJson(data); // ✅ Works perfectly
});
Gradual Migration
You can migrate endpoints one by one:
// Keep existing endpoints
app.get("/simple", (req, res) => {
res.json({ message: "Still works" });
});
// Migrate problematic endpoints
app.get("/complex", (req, res) => {
res.xJson(getComplexData());
});
Best Practices
-
Use XJson for known problematic data
- BigInt values
- Complex object graphs
- Large datasets
-
Configure streaming for very large responses
res.xJson(largeData, { enableStreaming: true, chunkSize: 1024 * 256, // 256KB chunks }); -
Set appropriate depth limits
res.xJson(data, { maxDepth: 10, // Prevent excessive nesting }); -
Monitor performance
- XJson adds minimal overhead for simple data
- Significant benefits for complex serialization scenarios
Troubleshooting
Common Issues
Issue: "Cannot find module" error Solution: Ensure you're importing from the correct package:
import { XJsonResponseHandler } from "@xypriss/core";
Issue: Streaming not working for large data
Solution: Check if enableStreaming is set to true and data size exceeds chunk size
Issue: Performance degradation
Solution: Use regular res.json() for simple, compatible data types
Debug Mode
Enable debug logging to see serialization details:
const app = createServer({
env: "development",
logging: {
level: "debug",
},
});
Examples
See the /examples directory for complete working examples:
- Basic XJson usage
- Large dataset streaming
- Complex data structures
- Middleware integration
- Error handling
API Comparison
| Feature | Regular JSON | XJson |
|---|---|---|
| BigInt support | ❌ | ✅ |
| Circular references | ❌ | ✅ |
| Streaming | ❌ | ✅ |
| Large data handling | ❌ | ✅ |
| Mixed data types | ❌ | ✅ |
| Performance (simple data) | ✅ | ⚡️ (minimal overhead) |
| Performance (complex data) | ❌ | ✅ |
Conclusion
The XJson API provides a robust, flexible solution for JSON serialization challenges in XyPriss applications. By handling edge cases like BigInt values, circular references, and large datasets, it ensures reliable data transmission while maintaining excellent performance for typical use cases.
Use XJson when you need reliable serialization for complex data, and rely on regular JSON for simple, performance-critical scenarios.