A modern, high performance Redis client for Node.js with full TypeScript support and all Redis Stack modules
—
Time series data operations for storing and querying time-stamped data with aggregation rules and downsampling using RedisTimeSeries. Ideal for metrics, IoT data, financial data, and monitoring applications.
Operations for creating and managing time series with metadata and configuration.
/**
* Create a new time series
* @param key - Time series key
* @param options - Optional series configuration
* @returns 'OK'
*/
function create(key: RedisArgument, options?: TsCreateOptions): Promise<SimpleStringReply<'OK'>>;
/**
* Alter time series configuration
* @param key - Time series key
* @param options - Configuration changes
* @returns 'OK'
*/
function alter(key: RedisArgument, options: TsAlterOptions): Promise<SimpleStringReply<'OK'>>;
/**
* Get time series information and statistics
* @param key - Time series key
* @returns Array containing series metadata and stats
*/
function info(key: RedisArgument): Promise<ArrayReply>;
/**
* Get detailed debug information about time series
* @param key - Time series key
* @returns Array containing debug information
*/
function infoDebug(key: RedisArgument): Promise<ArrayReply>;
/**
* Query time series index by labels
* @param filters - Label filter expressions
* @returns Array of matching time series keys
*/
function queryIndex(...filters: string[]): Promise<ArrayReply<BlobStringReply>>;
interface TsCreateOptions {
/** Data retention period in milliseconds */
RETENTION?: number;
/** Encoding algorithm */
ENCODING?: TimeSeriesEncoding;
/** Chunk size in bytes */
CHUNK_SIZE?: number;
/** Duplicate sample policy */
DUPLICATE_POLICY?: TimeSeriesDuplicatePolicies;
/** Labels for the series */
LABELS?: Record<string, string>;
}
interface TsAlterOptions {
/** Data retention period in milliseconds */
RETENTION?: number;
/** Chunk size in bytes */
CHUNK_SIZE?: number;
/** Duplicate sample policy */
DUPLICATE_POLICY?: TimeSeriesDuplicatePolicies;
/** Labels for the series */
LABELS?: Record<string, string>;
}
type TimeSeriesEncoding = 'COMPRESSED' | 'UNCOMPRESSED';
type TimeSeriesDuplicatePolicies = 'BLOCK' | 'FIRST' | 'LAST' | 'MIN' | 'MAX' | 'SUM';Usage Examples:
import { createClient } from "redis";
const client = createClient();
await client.connect();
// Create time series with configuration
await client.ts.create("temperature:sensor1", {
RETENTION: 86400000, // 24 hours in milliseconds
LABELS: {
sensor: "temperature",
location: "room1",
building: "office"
},
DUPLICATE_POLICY: 'LAST',
ENCODING: 'COMPRESSED'
});
// Create multiple related series
await client.ts.create("humidity:sensor1", {
RETENTION: 86400000,
LABELS: {
sensor: "humidity",
location: "room1",
building: "office"
}
});
// Get series information
const info = await client.ts.info("temperature:sensor1");
console.log("Series info:", info);
// Query by labels
const temperatureSensors = await client.ts.queryIndex("sensor=temperature");
const room1Sensors = await client.ts.queryIndex("location=room1");
const allOfficeSensors = await client.ts.queryIndex("building=office");
// Alter existing series
await client.ts.alter("temperature:sensor1", {
RETENTION: 172800000, // Extend to 48 hours
LABELS: {
sensor: "temperature",
location: "room1",
building: "office",
calibrated: "2023-01-01"
}
});Core operations for adding, retrieving, and manipulating time series data points.
/**
* Add sample to time series
* @param key - Time series key
* @param timestamp - Unix timestamp in milliseconds, or '*' for current time
* @param value - Numeric value
* @param options - Optional add parameters
* @returns The timestamp of the added sample
*/
function add(key: RedisArgument, timestamp: number | '*', value: number, options?: TsAddOptions): Promise<NumberReply>;
/**
* Add multiple samples to multiple time series
* @param samples - Array of sample specifications
* @returns Array of timestamps for added samples
*/
function mAdd(...samples: TsSample[]): Promise<ArrayReply<NumberReply>>;
/**
* Increment time series value
* @param key - Time series key
* @param value - Value to increment by
* @param options - Optional increment parameters
* @returns The timestamp of the incremented sample
*/
function incrBy(key: RedisArgument, value: number, options?: TsIncrByOptions): Promise<NumberReply>;
/**
* Decrement time series value
* @param key - Time series key
* @param value - Value to decrement by
* @param options - Optional decrement parameters
* @returns The timestamp of the decremented sample
*/
function decrBy(key: RedisArgument, value: number, options?: TsDecrByOptions): Promise<NumberReply>;
/**
* Delete samples in time range
* @param key - Time series key
* @param fromTimestamp - Start timestamp (inclusive)
* @param toTimestamp - End timestamp (inclusive)
* @returns Number of samples deleted
*/
function del(key: RedisArgument, fromTimestamp: number, toTimestamp: number): Promise<NumberReply>;
interface TsAddOptions {
/** Data retention period in milliseconds */
RETENTION?: number;
/** Encoding algorithm */
ENCODING?: TimeSeriesEncoding;
/** Chunk size in bytes */
CHUNK_SIZE?: number;
/** Only add if key doesn't exist */
ON_DUPLICATE?: TimeSeriesDuplicatePolicies;
/** Labels for new series */
LABELS?: Record<string, string>;
}
interface TsIncrByOptions extends TsAddOptions {
/** Custom timestamp (default: current time) */
TIMESTAMP?: number;
}
interface TsDecrByOptions extends TsAddOptions {
/** Custom timestamp (default: current time) */
TIMESTAMP?: number;
}
interface TsSample {
key: RedisArgument;
timestamp: number | '*';
value: number;
}Usage Examples:
// Add single sample with current timestamp
const timestamp1 = await client.ts.add("temperature:sensor1", "*", 23.5);
// Add sample with specific timestamp
const timestamp2 = await client.ts.add("temperature:sensor1", Date.now(), 24.1);
// Add sample with options (auto-create series)
const timestamp3 = await client.ts.add("pressure:sensor2", "*", 1013.25, {
LABELS: {
sensor: "pressure",
location: "room1",
unit: "hPa"
},
RETENTION: 86400000,
ON_DUPLICATE: 'LAST'
});
// Add multiple samples at once
await client.ts.mAdd(
{ key: "temperature:sensor1", timestamp: "*", value: 22.8 },
{ key: "humidity:sensor1", timestamp: "*", value: 65.2 },
{ key: "pressure:sensor1", timestamp: "*", value: 1012.8 }
);
// Increment counter-style metrics
const counterTs = await client.ts.incrBy("requests:total", 1, {
LABELS: { metric: "requests", service: "api" }
});
// Decrement with custom timestamp
await client.ts.decrBy("available:slots", 5, {
TIMESTAMP: Date.now() - 1000 // 1 second ago
});
// Delete old data
const deletedCount = await client.ts.del(
"temperature:sensor1",
Date.now() - 7 * 24 * 60 * 60 * 1000, // 7 days ago
Date.now() - 24 * 60 * 60 * 1000 // 1 day ago
);Operations for querying time series data with various filtering and aggregation options.
/**
* Get latest sample from time series
* @param key - Time series key
* @returns Array containing timestamp and value, or null if empty
*/
function get(key: RedisArgument): Promise<ArrayReply | null>;
/**
* Get latest samples from multiple time series
* @param keys - Array of time series keys
* @param options - Optional filter parameters
* @returns Array of time series data with latest samples
*/
function mGet(keys: RedisArgument[], options?: TsMGetOptions): Promise<ArrayReply>;
/**
* Get latest samples from multiple time series with labels
* @param keys - Array of time series keys
* @param options - Optional filter parameters
* @returns Array of time series data with labels and latest samples
*/
function mGetWithLabels(keys: RedisArgument[], options?: TsMGetOptions): Promise<ArrayReply>;
/**
* Get samples from time range
* @param key - Time series key
* @param fromTimestamp - Start timestamp (inclusive)
* @param toTimestamp - End timestamp (inclusive)
* @param options - Optional range parameters
* @returns Array of timestamp-value pairs
*/
function range(key: RedisArgument, fromTimestamp: number, toTimestamp: number, options?: TsRangeOptions): Promise<ArrayReply>;
/**
* Get samples from time range in reverse order
* @param key - Time series key
* @param fromTimestamp - Start timestamp (inclusive)
* @param toTimestamp - End timestamp (inclusive)
* @param options - Optional range parameters
* @returns Array of timestamp-value pairs in reverse order
*/
function revRange(key: RedisArgument, fromTimestamp: number, toTimestamp: number, options?: TsRangeOptions): Promise<ArrayReply>;
interface TsMGetOptions {
/** Label filters */
FILTER?: string[];
/** Include series labels in response */
WITHLABELS?: boolean;
/** Include only selected labels */
SELECTED_LABELS?: string[];
}
interface TsRangeOptions {
/** Filter by minimum value */
FILTER_BY_TS?: number[];
/** Filter by value range */
FILTER_BY_VALUE?: [min: number, max: number];
/** Limit number of samples */
COUNT?: number;
/** Align timestamps to bucket */
ALIGN?: number;
/** Aggregation configuration */
AGGREGATION?: {
type: TimeSeriesAggregationType;
bucketDuration: number;
BUCKETTIMESTAMP?: TimeSeriesBucketTimestamp;
EMPTY?: boolean;
};
}
type TimeSeriesAggregationType = 'AVG' | 'SUM' | 'MIN' | 'MAX' | 'RANGE' | 'COUNT' | 'STD.P' | 'STD.S' | 'VAR.P' | 'VAR.S' | 'FIRST' | 'LAST';
type TimeSeriesBucketTimestamp = '-' | 'low' | 'high' | 'mid';Usage Examples:
// Get latest sample
const latest = await client.ts.get("temperature:sensor1");
if (latest) {
const [timestamp, value] = latest;
console.log(`Latest: ${value}°C at ${new Date(timestamp)}`);
}
// Get latest from multiple series
const latestMultiple = await client.ts.mGet([
"temperature:sensor1",
"humidity:sensor1",
"pressure:sensor1"
]);
// Get latest with labels
const latestWithLabels = await client.ts.mGetWithLabels([
"temperature:sensor1"
], {
WITHLABELS: true
});
// Get range of data
const hourlyData = await client.ts.range(
"temperature:sensor1",
Date.now() - 60 * 60 * 1000, // 1 hour ago
Date.now() // now
);
// Get aggregated data (5-minute averages)
const averageData = await client.ts.range(
"temperature:sensor1",
Date.now() - 24 * 60 * 60 * 1000, // 24 hours ago
Date.now(),
{
AGGREGATION: {
type: 'AVG',
bucketDuration: 5 * 60 * 1000, // 5 minutes
BUCKETTIMESTAMP: 'mid'
}
}
);
// Get filtered range
const filteredData = await client.ts.range(
"temperature:sensor1",
Date.now() - 60 * 60 * 1000,
Date.now(),
{
FILTER_BY_VALUE: [20, 30], // Only values between 20-30
COUNT: 100 // Limit to 100 samples
}
);
// Get reverse chronological data
const recentData = await client.ts.revRange(
"temperature:sensor1",
Date.now() - 60 * 60 * 1000,
Date.now(),
{
COUNT: 10 // Last 10 samples
}
);Advanced querying operations across multiple time series with filtering and aggregation.
/**
* Query multiple time series by labels in time range
* @param fromTimestamp - Start timestamp
* @param toTimestamp - End timestamp
* @param filters - Label filter expressions
* @param options - Optional query parameters
* @returns Array of time series data
*/
function mRange(fromTimestamp: number, toTimestamp: number, filters: string[], options?: TsMRangeOptions): Promise<ArrayReply>;
/**
* Query multiple time series with labels in time range
* @param fromTimestamp - Start timestamp
* @param toTimestamp - End timestamp
* @param filters - Label filter expressions
* @param options - Optional query parameters
* @returns Array of time series data with labels
*/
function mRangeWithLabels(fromTimestamp: number, toTimestamp: number, filters: string[], options?: TsMRangeOptions): Promise<ArrayReply>;
/**
* Query multiple time series in reverse order
* @param fromTimestamp - Start timestamp
* @param toTimestamp - End timestamp
* @param filters - Label filter expressions
* @param options - Optional query parameters
* @returns Array of time series data in reverse order
*/
function mRevRange(fromTimestamp: number, toTimestamp: number, filters: string[], options?: TsMRangeOptions): Promise<ArrayReply>;
/**
* Query with grouping and reduction
* @param fromTimestamp - Start timestamp
* @param toTimestamp - End timestamp
* @param filters - Label filter expressions
* @param groupBy - Labels to group by
* @param reducer - Reduction function
* @param options - Optional query parameters
* @returns Array of grouped and reduced time series data
*/
function mRangeGroupBy(
fromTimestamp: number,
toTimestamp: number,
filters: string[],
groupBy: string,
reducer: TimeSeriesReducer,
options?: TsMRangeOptions
): Promise<ArrayReply>;
interface TsMRangeOptions {
/** Include series labels in response */
WITHLABELS?: boolean;
/** Include only selected labels */
SELECTED_LABELS?: string[];
/** Limit number of samples per series */
COUNT?: number;
/** Aggregation configuration */
AGGREGATION?: {
type: TimeSeriesAggregationType;
bucketDuration: number;
BUCKETTIMESTAMP?: TimeSeriesBucketTimestamp;
EMPTY?: boolean;
};
/** Filter by value range */
FILTER_BY_VALUE?: [min: number, max: number];
/** Filter by timestamp array */
FILTER_BY_TS?: number[];
/** Align timestamps */
ALIGN?: number;
}
type TimeSeriesReducer = 'SUM' | 'MIN' | 'MAX' | 'AVG' | 'STD.P' | 'STD.S' | 'VAR.P' | 'VAR.S' | 'COUNT';Usage Examples:
// Query all temperature sensors for last hour
const allTemperatures = await client.ts.mRange(
Date.now() - 60 * 60 * 1000, // 1 hour ago
Date.now(),
["sensor=temperature"]
);
// Query with labels and aggregation
const avgTemperatures = await client.ts.mRangeWithLabels(
Date.now() - 24 * 60 * 60 * 1000, // 24 hours ago
Date.now(),
["sensor=temperature", "building=office"],
{
WITHLABELS: true,
AGGREGATION: {
type: 'AVG',
bucketDuration: 60 * 60 * 1000, // 1 hour buckets
BUCKETTIMESTAMP: 'mid'
}
}
);
// Group by location and average
const locationAverages = await client.ts.mRangeGroupBy(
Date.now() - 60 * 60 * 1000,
Date.now(),
["sensor=temperature"],
"location",
"AVG",
{
AGGREGATION: {
type: 'AVG',
bucketDuration: 5 * 60 * 1000 // 5 minute buckets
}
}
);
// Complex multi-series query
const complexQuery = await client.ts.mRange(
Date.now() - 2 * 60 * 60 * 1000, // 2 hours ago
Date.now(),
["building=office", "location!=server_room"],
{
COUNT: 100,
FILTER_BY_VALUE: [18, 35], // Reasonable temperature range
AGGREGATION: {
type: 'MAX',
bucketDuration: 10 * 60 * 1000, // 10 minute max values
EMPTY: true // Include empty buckets
}
}
);Operations for creating and managing downsampling and aggregation rules.
/**
* Create aggregation rule between time series
* @param sourceKey - Source time series key
* @param destKey - Destination time series key
* @param aggregation - Aggregation configuration
* @param options - Optional rule parameters
* @returns 'OK'
*/
function createRule(
sourceKey: RedisArgument,
destKey: RedisArgument,
aggregation: TsAggregation,
options?: TsCreateRuleOptions
): Promise<SimpleStringReply<'OK'>>;
/**
* Delete aggregation rule
* @param sourceKey - Source time series key
* @param destKey - Destination time series key
* @returns 'OK'
*/
function deleteRule(sourceKey: RedisArgument, destKey: RedisArgument): Promise<SimpleStringReply<'OK'>>;
interface TsAggregation {
/** Aggregation type */
type: TimeSeriesAggregationType;
/** Time bucket duration in milliseconds */
bucketDuration: number;
/** Bucket timestamp alignment */
BUCKETTIMESTAMP?: TimeSeriesBucketTimestamp;
}
interface TsCreateRuleOptions {
/** Alignment timestamp */
alignTimestamp?: number;
}Usage Examples:
// Create destination series for aggregated data
await client.ts.create("temperature:sensor1:hourly", {
RETENTION: 30 * 24 * 60 * 60 * 1000, // 30 days
LABELS: {
sensor: "temperature",
location: "room1",
resolution: "hourly"
}
});
await client.ts.create("temperature:sensor1:daily", {
RETENTION: 365 * 24 * 60 * 60 * 1000, // 1 year
LABELS: {
sensor: "temperature",
location: "room1",
resolution: "daily"
}
});
// Create aggregation rules
await client.ts.createRule(
"temperature:sensor1", // Source: raw data
"temperature:sensor1:hourly", // Destination: hourly averages
{
type: 'AVG',
bucketDuration: 60 * 60 * 1000, // 1 hour
BUCKETTIMESTAMP: 'mid'
}
);
await client.ts.createRule(
"temperature:sensor1:hourly", // Source: hourly data
"temperature:sensor1:daily", // Destination: daily averages
{
type: 'AVG',
bucketDuration: 24 * 60 * 60 * 1000, // 1 day
BUCKETTIMESTAMP: 'low'
}
);
// Create multiple aggregation rules for different statistics
const sensors = ["temperature:sensor1", "humidity:sensor1"];
const aggregations = [
{ suffix: "min", type: 'MIN' as const },
{ suffix: "max", type: 'MAX' as const },
{ suffix: "avg", type: 'AVG' as const }
];
for (const sensor of sensors) {
for (const agg of aggregations) {
const destKey = `${sensor}:hourly:${agg.suffix}`;
// Create destination series
await client.ts.create(destKey, {
RETENTION: 7 * 24 * 60 * 60 * 1000, // 7 days
LABELS: {
source: sensor,
aggregation: agg.type,
resolution: "hourly"
}
});
// Create rule
await client.ts.createRule(sensor, destKey, {
type: agg.type,
bucketDuration: 60 * 60 * 1000 // 1 hour
});
}
}
// Delete rule when no longer needed
await client.ts.deleteRule("temperature:sensor1", "temperature:sensor1:hourly");Time series queries support powerful label filtering:
// Label filter examples:
"sensor=temperature" // Exact match
"location!=server_room" // Not equal
"building=(office|warehouse)" // Multiple values
"priority=(high|critical)" // OR condition
"sensor=temperature location=room1" // AND condition (space-separated)
"sensor!=pressure" // Exclude sensor typeUsage Examples:
// Query with various label filters
const officeTemperatures = await client.ts.queryIndex("sensor=temperature", "building=office");
const nonServerMetrics = await client.ts.queryIndex("location!=server_room");
const criticalMetrics = await client.ts.queryIndex("priority=(high|critical)");
const roomSensors = await client.ts.queryIndex("location=(room1|room2|room3)", "sensor=temperature");const TIME_SERIES_ENCODING = {
COMPRESSED: 'COMPRESSED',
UNCOMPRESSED: 'UNCOMPRESSED'
} as const;
const TIME_SERIES_DUPLICATE_POLICIES = {
BLOCK: 'BLOCK',
FIRST: 'FIRST',
LAST: 'LAST',
MIN: 'MIN',
MAX: 'MAX',
SUM: 'SUM'
} as const;
const TIME_SERIES_AGGREGATION_TYPE = {
AVG: 'AVG',
SUM: 'SUM',
MIN: 'MIN',
MAX: 'MAX',
RANGE: 'RANGE',
COUNT: 'COUNT',
STD_P: 'STD.P',
STD_S: 'STD.S',
VAR_P: 'VAR.P',
VAR_S: 'VAR.S',
FIRST: 'FIRST',
LAST: 'LAST'
} as const;
const TIME_SERIES_BUCKET_TIMESTAMP = {
LOW: 'low',
HIGH: 'high',
MID: 'mid'
} as const;
const TIME_SERIES_REDUCERS = {
SUM: 'SUM',
MIN: 'MIN',
MAX: 'MAX',
AVG: 'AVG',
STD_P: 'STD.P',
STD_S: 'STD.S',
VAR_P: 'VAR.P',
VAR_S: 'VAR.S',
COUNT: 'COUNT'
} as const;Install with Tessl CLI
npx tessl i tessl/npm-redis