Redis Protocol Parser for Node.js
redis-parser is a high-performance JavaScript implementation of the Redis Serialization Protocol (RESP) parser, specifically designed for Node.js environments. It serves as the underlying parsing engine for popular Redis clients like node-redis and ioredis. The current stable version is 3.0.0. This library focuses on efficiently converting raw Redis protocol buffers into usable JavaScript data structures, supporting various RESP data types including strings, numbers, arrays, and errors. Its development prioritizes performance, especially for large data structures, and graceful handling of malformed input, making it a critical component for robust Redis client libraries. It also exports dedicated error classes for specific parsing and reply errors.
Common errors
-
Error: Command reply out of sync. Please check your connection and client logic.
cause This error often indicates a desynchronization in the Redis protocol stream. It can be caused by network issues, a misbehaving Redis server, or incorrect client logic that sends malformed data or misinterprets responses, leading the parser to encounter unexpected data.fixImplement the `returnFatalError` callback to specifically handle protocol desynchronization. Within this callback, you should reset the parser state (`parser.reset()`), destroy the current network connection, and initiate a new one. All outstanding commands should be either rejected or re-queued for execution on the new connection. -
RangeError: Invalid array length
cause In older versions of `redis-parser` (prior to v2.3.0), parsing extremely large or deeply nested Redis arrays could sometimes lead to inefficiencies in memory handling or exceeding JavaScript's recursion limits, resulting in this error.fixUpgrade `redis-parser` to version 2.3.0 or higher. This version introduced significant performance improvements for handling large arrays, making the parsing time linear and much more resilient to arbitrary array sizes. -
TypeError: 'buffer' argument must be of type Buffer. Received type string
cause The `parser.execute()` method strictly expects a Node.js `Buffer` object as its input, containing the raw Redis protocol data. Passing a JavaScript string directly will result in this `TypeError`.fixEnsure that any data fed to `parser.execute()` is first converted into a `Buffer`. For example, if you receive string data, use `parser.execute(Buffer.from(yourStringData))`.
Warnings
- breaking Version 3.0.0 of `redis-parser` drops support for Node.js versions older than 4. Users on Node.js 0.x or 3.x environments must upgrade to Node.js 4 or newer, or pin their `redis-parser` dependency to '~2.x' to avoid compatibility issues.
- breaking Support for the optional native `hiredis` C++ parser has been completely removed in `redis-parser` v3.0.0. The package now exclusively uses its highly optimized JavaScript parser. While the JS parser has seen significant performance improvements, environments specifically configured to leverage `hiredis` might observe changes in parsing behavior or performance characteristics.
- gotcha JavaScript's `Number.MAX_SAFE_INTEGER` (`2^53 - 1`) limits the precision of integers. Redis can return numbers larger than this. If not handled, these large numbers will be truncated or inaccurate. To prevent data loss, use the `stringNumbers: true` option to receive all numbers as strings.
- gotcha By default, `redis-parser` returns bulk strings and array elements as JavaScript strings (UTF-8 decoded). If your application requires raw binary data (e.g., for storing images, serialized objects, or specific encodings), you must enable the `returnBuffers` option to receive `Buffer` objects.
- gotcha Protocol errors (indicating severe issues with the Redis response format) are typically passed to the `returnFatalError` callback. If this callback is not provided, fatal errors will be routed to `returnError`. It is crucial to implement `returnFatalError` to gracefully handle these scenarios, which usually involves tearing down the connection, rejecting any outstanding commands, and attempting a reconnect.
- deprecated Internal use of `new Buffer()` (which is deprecated in modern Node.js) was present in `redis-parser` versions prior to 2.6.0. Version 2.6.0 updated internal implementations to utilize `Buffer.allocUnsafe` for improved performance and alignment with current Node.js best practices.
Install
-
npm install redis-parser -
yarn add redis-parser -
pnpm add redis-parser
Imports
- Parser
import { Parser } from 'redis-parser';const Parser = require('redis-parser'); - RedisError
import { RedisError } from 'redis-parser';const { RedisError } = require('redis-parser'); - ParserError
import { ParserError } from 'redis-parser';const { ParserError } = require('redis-parser');
Quickstart
const Parser = require('redis-parser');
class MyRedisClient {
constructor() {
this.replies = [];
this.errors = [];
this.fatalErrors = [];
this.parser = new Parser({
returnReply: this.handleReply.bind(this),
returnError: this.handleError.bind(this),
returnFatalError: this.handleFatalError.bind(this),
returnBuffers: false, // Default: return strings instead of Buffers
stringNumbers: false // Default: return JavaScript numbers, not strings
});
}
handleReply(reply) {
this.replies.push(reply);
console.log('Received Reply:', reply);
}
handleError(err) {
this.errors.push(err);
console.error('Received Error:', err.message);
}
handleFatalError(err) {
this.fatalErrors.push(err);
console.error('Received FATAL Error (protocol error):', err.message);
// In a real Redis client, this is where you would typically:
// 1. Log the error for debugging.
// 2. Reject all outstanding commands.
// 3. Destroy the current socket connection.
// 4. Attempt to reconnect to the Redis server.
// This prevents further data corruption due to protocol desynchronization.
}
processStreamData(dataBuffer) {
// Simulate receiving raw Redis protocol data (as a Buffer) from a stream
this.parser.execute(dataBuffer);
}
}
const client = new MyRedisClient();
// Simulate receiving various Redis protocol responses
client.processStreamData(Buffer.from('$5\r\nHello\r\n')); // Bulk String "Hello"
client.processStreamData(Buffer.from(':12345\r\n')); // Integer 12345
client.processStreamData(Buffer.from('*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n')); // Array ["foo", "bar"]
client.processStreamData(Buffer.from('-ERR unknown command `FOO`\r\n')); // Error Reply
console.log('\n--- Parser State & Collected Data ---');
console.log('Total Replies:', client.replies.length, client.replies);
console.log('Total Errors:', client.errors.length, client.errors.map(e => e.message));
// Demonstrate dynamic option change: return numbers as strings for large integers
client.parser.setStringNumbers(true);
client.processStreamData(Buffer.from(':90071992547409920\r\n')); // A number larger than Number.MAX_SAFE_INTEGER
console.log('Last Reply (string number):', client.replies[client.replies.length - 1]);