Node.js Memcached Client
memcache-client is a high-performance Node.js client designed for interacting with Memcached servers, focusing on efficient ASCII protocol parsing through direct Node.js Buffer APIs. Originally developed and heavily used at WalmartLabs for powering the walmart.com e-commerce platform, it offers robust features crucial for large-scale applications. These include optional data compression, automatic reconnection on network errors, support for arbitrary Memcached commands, and the ability to store various data types like Buffer, string, numeric, and JSON. The library provides a flexible API supporting both traditional Node.js callbacks and modern Promise-based operations, alongside features like fire-and-forget requests, multiple connections, and TLS support. It is currently at version 1.0.5 and appears to be in a maintenance or stable release state, with a focus on reliability and performance in demanding environments.
Common errors
-
Error: spawn zstd ENOENT
cause The `zstd` executable is not found in the system's PATH when compression is enabled for data storage.fixInstall the `zstd` command-line utility on your operating system (e.g., `sudo apt install zstd` or `brew install zstd`). Ensure it's accessible in the environment where your Node.js application runs. -
Property 'value' does not exist on type 'unknown'.
cause Attempting to access properties like `value` on results from `get` or `gets` operations when TypeScript cannot infer the type, typically during multi-key retrievals without using `MultiRetrievalResponse`.fixFor single-key `get`, use a generic: `client.get<string>('key')`. For multi-key `get` or `gets`, use the appropriate type helper: `client.get<MultiRetrievalResponse<string>>(['key1', 'key2'])`. -
TypeError: MemcacheClient is not a constructor
cause This error often occurs when attempting to use CommonJS `require()` to import an ES Module (ESM) package incorrectly, or when the `MemcacheClient` class is not directly exported as a default.fixIf using CommonJS, ensure you're destructuring the named export: `const { MemcacheClient } = require('memcache-client');`. For new projects, it is recommended to use ESM `import { MemcacheClient } from 'memcache-client';` in a `type: "module"` Node.js environment.
Warnings
- gotcha Enabling compression features (e.g., `compress: true` in `set` options) requires the `zstd` executable to be installed and available in the system's PATH. If `zstd` is not found, compression will fail, potentially leading to errors or unexpected behavior.
- gotcha When retrieving multiple keys using methods like `client.get(['key1', 'key2'])` or `client.gets(['key1', 'key2'])` in TypeScript, it is crucial to apply the `MultiRetrievalResponse` or `MultiCasRetrievalResponse` generic type. Failing to do so will result in `unknown` types for the retrieved values, diminishing type safety.
- gotcha By default, the client does not ignore `NOT_STORED` responses from the Memcached server. If integrating with specific Memcached proxy configurations, such as McRouter in AllAsync mode, you might need to instantiate `MemcacheClient` with `{ ignoreNotStored: true }` to prevent these responses from being treated as errors.
Install
-
npm install memcache-client -
yarn add memcache-client -
pnpm add memcache-client
Imports
- MemcacheClient
const MemcacheClient = require('memcache-client').MemcacheClient;import { MemcacheClient } from 'memcache-client'; - MultiRetrievalResponse
const { MultiRetrievalResponse } = require('memcache-client');import { MultiRetrievalResponse } from 'memcache-client'; - client.set (Promise)
client.set('key', 'data'); // Fire and forget without handling result or errorclient.set('key', 'data').then((r) => console.log(r));
Quickstart
import { MemcacheClient, MultiRetrievalResponse } from 'memcache-client';
import assert from 'node:assert';
const server = 'localhost:11211';
// Create a client with default settings (maxConnections = 1)
const client = new MemcacheClient({ server });
async function runMemcacheExample() {
try {
// Set a key-value pair using Promises
const setResult = await client.set('myKey', 'myValue');
assert.deepEqual(setResult, ['STORED']);
console.log('Set "myKey":', setResult);
// Get the value of 'myKey' using Promises, with type assertion
const getData = await client.get<string>('myKey');
assert.equal(getData?.value, 'myValue');
console.log('Got "myKey":', getData?.value);
// Set multiple keys concurrently
await Promise.all([
client.set('key1', 'data1'),
client.set('key2', 'data2')
]);
console.log('Set "key1" and "key2".');
// Get multiple keys with correct TypeScript typing
const multiResults = await client.get<MultiRetrievalResponse<string>>(['key1', 'key2']);
assert.equal(multiResults['key1'].value, 'data1');
assert.equal(multiResults['key2'].value, 'data2');
console.log('Got multiple keys:', multiResults);
// Example with callback for 'delete' (optional pattern)
client.delete('myKey', (err, result) => {
if (err) {
console.error('Delete error:', err);
} else {
assert.deepEqual(result, ['DELETED']);
console.log('Deleted "myKey":', result);
}
});
} catch (error) {
console.error('An error occurred:', error);
} finally {
// It's good practice to close connections if not needed anymore, though auto-reconnect handles many cases
// client.disconnect(); // (Assuming a disconnect method exists or client handles it internally)
}
}
runMemcacheExample();