Now that you understand what Redis is, it's time to use it in real code. The official redis npm package gives you a clean async interface that maps almost one-to-one with the CLIWhat is cli?Short for Command Line Interface. A tool you use by typing commands in the terminal instead of clicking buttons. commands you learned in the previous lesson. Once you see the caching pattern in action, you'll want to add it everywhere.
Installing and connecting
Setup
Start by installing the package and creating a client. The client connects to Redis over TCP, so you give it a URL pointing to your Redis server.
npm install redisimport { createClient } from 'redis';
const client = createClient({
url: 'redis://localhost:6379' // default Redis port
});
client.on('error', (err) => {
console.error('Redis connection error:', err);
});
await client.connect();The error event listener is important. Without it, unhandled connection errors can crash your Node.js process entirely. Always register it before calling connect().
process.env.REDIS_URL. Never hardcode credentials or hostnames in source files.Verifying the connection
After connecting, do a quick round-trip to confirm everything works:
await client.set('ping', 'pong');
const result = await client.get('ping');
console.log(result); // 'pong'Basic read and write operations
Strings
The Node.js client method names match the Redis CLIWhat is cli?Short for Command Line Interface. A tool you use by typing commands in the terminal instead of clicking buttons. commands almost exactly: set, get, del, exists, expire.
// Store a string
await client.set('username', 'alice');
// Retrieve it
const name = await client.get('username');
console.log(name); // 'alice'
// Store with TTL (expires after 1 hour)
await client.set('session:xyz', 'data', { EX: 3600 });
// Delete
await client.del('username');Storing objects
Redis strings can hold any text, so you can store JSONWhat is json?A text format for exchanging data between systems. It uses key-value pairs and arrays, and every programming language can read and write it.-serialized objects. You just need to serialize on write and parse on read.
const user = { id: 1, name: 'Alice', role: 'admin' };
await client.set('user:1', JSON.stringify(user), { EX: 600 });
const raw = await client.get('user:1');
const parsed = JSON.parse(raw);
console.log(parsed.name); // 'Alice'JSON.parse(null) returns null in JavaScript, so if a key doesn't exist, client.get() returns null and JSON.parse(null) gives you null. That's actually safe, just check for it before using the value.The cache-aside pattern
How it works
Cache-aside (also called "lazy loadingWhat is lazy loading?Deferring the loading of a resource like an image or component until the moment it's actually needed, speeding up the initial page load.") is the most common caching pattern. The idea is simple: check Redis first, and only hit the database if the data isn't cached yet.
Request comes in
|
v
Check Redis --> HIT --> Return cached data (fast)
|
MISS
|
v
Query database --> Store result in Redis --> Return data| Scenario | Redis called? | Database called? | Speed |
|---|---|---|---|
| Cache hit | Yes | No | Fast |
| Cache miss | Yes (write) | Yes | Normal |
| Key expired | Yes (write) | Yes | Normal |
Implementation
Here's a complete cache-aside function you can drop into any Express route:
async function getCachedUser(id) {
const cacheKey = `user:${id}`;
// Step 1: check the cache
const cached = await client.get(cacheKey);
if (cached) {
console.log('Cache hit');
return JSON.parse(cached);
}
// Step 2: cache miss - query the database
console.log('Cache miss, querying DB');
const user = await db.users.findById(id);
if (!user) return null;
// Step 3: store in Redis for future requests
await client.set(cacheKey, JSON.stringify(user), { EX: 3600 });
return user;
}Cache invalidationWhat is cache invalidation?Removing or updating cached data when the original data changes, so users never see outdated information.
When the underlying data changes, you need to remove or update the cached copy. Forgetting this is the most common caching bug, users see stale data.
async function updateUser(id, updates) {
// Update the database
await db.users.update(id, updates);
// Invalidate the cache so the next read gets fresh data
await client.del(`user:${id}`);
}Closing the connection
In a long-running server process, you keep the Redis client alive for the lifetime of the process. In scripts or serverlessWhat is serverless?A hosting model where individual functions run on demand and the platform handles all server management, scaling, and uptime for you. functions, disconnect when you're done.
// In a script
await client.disconnect();
// In an Express server
process.on('SIGTERM', async () => {
await client.disconnect();
process.exit(0);
});Quick reference
| Node.js method | Redis equivalent | Description |
|---|---|---|
client.set(key, value) | SET key value | Store a string |
client.set(key, value, { EX: n }) | SETEX key n value | Store with TTL |
client.get(key) | GET key | Retrieve a string |
client.del(key) | DEL key | Delete a key |
client.exists(key) | EXISTS key | Check existence |
client.incr(key) | INCR key | Increment counter |
client.expire(key, n) | EXPIRE key n | Set TTL on existing key |
client.ttl(key) | TTL key | Get remaining TTL |
import { createClient } from 'redis';
async function redisDemo() {
const client = createClient();
await client.connect();
// Simple string
await client.set('framework', 'Express');
const value = await client.get('framework');
console.log(value); // 'Express'
// Using Hashes (Objects)
await client.hSet('user:42', {
name: 'John',
surname: 'Doe'
});
const user = await client.hGetAll('user:42');
console.log(user); // { name: 'John', surname: 'Doe' }
await client.disconnect();
}