File I/O
The Crous Node.js SDK provides convenient functions for reading and writing serialized data to files and streams, on top of the in-memorydumps / loads API.
Writing to Files
dump — Write by File Path
The simplest way to persist data. Pass the data and a file path — Crous handles the rest.
write_file.js
const crous = require('crous');
const config = {
server: { host: '0.0.0.0', port: 8080 },
database: { url: 'postgres://localhost/mydb' },
features: ['auth', 'logging', 'rate-limiting'],
maxRetries: 3,
debug: false
};
// Write to file — synchronous
crous.dump(config, 'config.crous');
console.log('✓ Config saved');dump — Write to Stream
Pass a writable stream instead of a file path for integration with Node.js stream pipelines.
write_stream.js
const crous = require('crous');
const fs = require('fs');
const data = { message: 'Hello from a stream!' };
const stream = fs.createWriteStream('output.crous');
crous.dump(data, stream);
stream.end();
// Works with any Writable stream
const { PassThrough } = require('stream');
const passthrough = new PassThrough();
const chunks = [];
passthrough.on('data', (chunk) => chunks.push(chunk));
passthrough.on('end', () => {
const buffer = Buffer.concat(chunks);
console.log(`Captured ${buffer.length} bytes`);
});
crous.dump(data, passthrough);
passthrough.end();Reading from Files
load — Read by File Path
read_file.js
const crous = require('crous');
// Read from file — synchronous
const config = crous.load('config.crous');
console.log(config.server.host); // '0.0.0.0'
console.log(config.features); // ['auth', 'logging', 'rate-limiting']load — Read from Buffer
When you already have the file contents in memory (e.g., from an HTTP response or database), use loads directly.
read_buffer.js
const crous = require('crous');
const fs = require('fs');
// Read file into buffer manually
const buffer = fs.readFileSync('config.crous');
const config = crous.loads(buffer);
// From an HTTP response
const http = require('http');
http.get('http://api.example.com/data.crous', (res) => {
const chunks = [];
res.on('data', (chunk) => chunks.push(chunk));
res.on('end', () => {
const data = crous.loads(Buffer.concat(chunks));
console.log(data);
});
});Multi-Record Files
You can write multiple records to the same file by concatenating serialized buffers. Each call to dumps produces a self-contained record.
multi_record.js
const crous = require('crous');
const fs = require('fs');
// Write multiple records
const stream = fs.createWriteStream('events.crous');
const events = [
{ type: 'click', x: 100, y: 200, time: Date.now() },
{ type: 'scroll', delta: -50, time: Date.now() },
{ type: 'keypress', key: 'Enter', time: Date.now() },
];
for (const event of events) {
const buf = crous.dumps(event);
// Write length prefix + data for framing
const lenBuf = Buffer.alloc(4);
lenBuf.writeUInt32BE(buf.length);
stream.write(lenBuf);
stream.write(buf);
}
stream.end();
// Read multiple records back
const fileData = fs.readFileSync('events.crous');
let offset = 0;
const decoded = [];
while (offset < fileData.length) {
const len = fileData.readUInt32BE(offset);
offset += 4;
const record = fileData.subarray(offset, offset + len);
decoded.push(crous.loads(record));
offset += len;
}
console.log(`Read ${decoded.length} events`);
decoded.forEach((e) => console.log(e.type));Using with CrousEncoder / CrousDecoder
The class-based API also supports file I/O with the same dump /load methods.
encoder_file_io.js
const { CrousEncoder, CrousDecoder } = require('crous');
const encoder = new CrousEncoder({
default: (obj) => {
if (obj instanceof Date) {
return { __date__: obj.toISOString() };
}
throw new TypeError('Unserializable');
}
});
const decoder = new CrousDecoder({
object_hook: (obj) => {
if (obj.__date__) return new Date(obj.__date__);
return obj;
}
});
// Write
encoder.dump({ created: new Date(), name: 'test' }, 'record.crous');
// Read
const record = decoder.load('record.crous');
console.log(record.created instanceof Date); // trueError Handling for File I/O
error_handling.js
const crous = require('crous');
// File not found
try {
crous.load('nonexistent.crous');
} catch (e) {
console.log(e.code); // 'ENOENT'
console.log(e.message); // No such file or directory
}
// Corrupted file
try {
const fs = require('fs');
fs.writeFileSync('bad.crous', 'not valid crous data');
crous.load('bad.crous');
} catch (e) {
console.log(e.name); // 'CrousDecodeError'
console.log(e.message); // decode error details
}
// Permission denied
try {
crous.dump({ data: 1 }, '/root/protected.crous');
} catch (e) {
console.log(e.code); // 'EACCES'
}Synchronous I/O
File operations in the Crous Node.js SDK are currently synchronous. For high-throughput applications, consider running file I/O in a worker thread or using the in-memory
dumps / loads with your own async file handling.Practical Patterns
Configuration Files
config_pattern.js
const crous = require('crous');
const fs = require('fs');
const path = require('path');
function loadConfig(configPath) {
if (!fs.existsSync(configPath)) {
// Return defaults
return {
port: 3000,
debug: false,
allowedOrigins: ['http://localhost:3000']
};
}
return crous.load(configPath);
}
function saveConfig(config, configPath) {
// Atomic write: write to temp, then rename
const tmpPath = configPath + '.tmp';
crous.dump(config, tmpPath);
fs.renameSync(tmpPath, configPath);
}
const config = loadConfig('app.crous');
config.debug = true;
saveConfig(config, 'app.crous');Data Caching
cache_pattern.js
const crous = require('crous');
const fs = require('fs');
class CrousCache {
constructor(cacheDir) {
this.cacheDir = cacheDir;
if (!fs.existsSync(cacheDir)) {
fs.mkdirSync(cacheDir, { recursive: true });
}
}
_keyPath(key) {
// Simple hash for filename
const hash = Buffer.from(key).toString('hex').slice(0, 16);
return `${this.cacheDir}/${hash}.crous`;
}
set(key, value, ttlMs = 60_000) {
const entry = {
value,
expires: Date.now() + ttlMs
};
crous.dump(entry, this._keyPath(key));
}
get(key) {
const filePath = this._keyPath(key);
if (!fs.existsSync(filePath)) return null;
const entry = crous.load(filePath);
if (Date.now() > entry.expires) {
fs.unlinkSync(filePath);
return null;
}
return entry.value;
}
}
const cache = new CrousCache('./cache');
cache.set('users', [{ id: 1, name: 'Alice' }], 30_000);
console.log(cache.get('users')); // [{ id: 1, name: 'Alice' }]