Skip to content

Files

Latest commit

052b6ab · Apr 16, 2025

History

History
494 lines (366 loc) · 11.8 KB

01.redis_to_node.md

File metadata and controls

494 lines (366 loc) · 11.8 KB

Redis to Node

  1. How to connect Node.js to Redis.
  2. Basic Redis commands (set, get, delete).
  3. A simple example to test everything.

Step 1: Make Sure Redis is Running

Before coding, ensure your Redis Docker container is running (from the previous steps). Open PowerShell and run:

docker ps

If you see my-redis, you’re ready!


Step 2: Create a Node.js Project

  1. Open a folder (e.g., redis-demo) and initialize a Node.js project:

    npm init -y
  2. Install the Redis client library (we’ll use ioredis):

    npm install ioredis

Step 3: Connect Node.js to Redis

Create a file app.js and add this code to connect to Redis:

// Import the Redis library
const Redis = require("ioredis");

// Create a connection to Redis (running in Docker)
const redis = new Redis({
  host: "localhost", // Docker's default IP
  port: 6379, // Redis port
});

// Test the connection
async function testConnection() {
  try {
    const reply = await redis.ping();
    console.log("Redis says:", reply); // Should print "PONG"
  } catch (err) {
    console.error("Failed to connect to Redis:", err);
  }
}

testConnection();

Run it with:

node app.js

If you see Redis says: PONG, it works! 🎉


Step 4: Basic Redis Commands in Node.js

Let’s learn 3 essential commands: SET, GET, and DEL.

1. SET (Save Data)

async function setKey() {
  await redis.set("message", "Hello from Node.js!");
  console.log("Data saved!");
}
setKey();

2. GET (Read Data)

async function getKey() {
  const value = await redis.get("message");
  console.log("Value:", value); // Prints "Hello from Node.js!"
}
getKey();

3. DEL (Delete Data)

async function delKey() {
  await redis.del("message");
  console.log("Key deleted!");
}
delKey();

Step 5: Run a Complete Example

Update app.js with this code to test all steps together:

const Redis = require("ioredis");
const redis = new Redis({ host: "localhost", port: 6379 });

async function main() {
  // 1. Test connection
  console.log(await redis.ping()); // Should print "PONG"

  // 2. Save data
  await redis.set("fruit", "apple");
  console.log("Saved: fruit → apple");

  // 3. Read data
  const fruit = await redis.get("fruit");
  console.log("Read:", fruit); // Prints "apple"

  // 4. Delete data
  await redis.del("fruit");
  console.log("Deleted 'fruit'");
}

main()
  .then(() => process.exit(0))
  .catch((err) => console.error(err));

Run it:

node app.js

You’ll see:

PONG
Saved: fruit → apple
Read: apple
Deleted 'fruit'

Key Things to Know:

  1. Async/Await: We use async/await because Redis operations are asynchronous (they take time).
  2. Simple Data Types: Redis stores data as strings by default. For numbers/objects, use JSON.stringify():
    await redis.set("user", JSON.stringify({ name: "Alice", age: 30 }));
    const user = JSON.parse(await redis.get("user"));
  3. Close the Connection (Optional):
    await redis.quit();

Common Errors & Fixes:

Error Fix
Connection refused Ensure Docker and Redis are running (docker ps).
ReplyError: ERR no such key The key doesn’t exist. Check spelling!
ECONNREFUSED Wrong port/host. Double-check localhost:6379.

Let’s explore SETEX, HSET, and LPUSH with simple examples in Node.js! These commands unlock Redis’ power for caching, storing objects, and managing lists.


1. SETEX (Set with Expiration)

What it does: Stores a key-value pair that auto-deletes after X seconds.
Use case: Temporary data (e.g., OTP codes, session tokens).

// Store a key "promo_code" with value "SALE20" for 30 seconds
await redis.setex("promo_code", 30, "SALE20");
console.log("Promo code set!");

// Retrieve it before it expires
const promo = await redis.get("promo_code");
console.log(promo); // "SALE20" (if within 30s)

// After 30s, this returns `null`
const expiredPromo = await redis.get("promo_code");
console.log(expiredPromo); // null

2. HSET & HGETALL (Hashes for Objects)

What it does: Stores a hash (object) with multiple fields.
Use case: User profiles, product details.

// Store a user object
await redis.hset("user:123", {
  name: "Alice",
  email: "alice@example.com",
  age: 30,
});
console.log("User saved!");

// Retrieve the entire user object
const user = await redis.hgetall("user:123");
console.log(user);
// { name: 'Alice', email: 'alice@example.com', age: '30' }

// Get a single field
const email = await redis.hget("user:123", "email");
console.log(email); // "alice@example.com"

3. LPUSH & LRANGE (Lists)

What it does: Pushes items to the beginning of a list.
Use case: Activity logs, task queues, recent items.

// Add tasks to a list
await redis.lpush("tasks", "Buy milk");
await redis.lpush("tasks", "Call mom");
console.log("Tasks added!");

// Get all tasks (0 = start, -1 = end)
const tasks = await redis.lrange("tasks", 0, -1);
console.log(tasks); // ["Call mom", "Buy milk"]

// Get the first task (leftmost item)
const firstTask = await redis.lindex("tasks", 0);
console.log(firstTask); // "Call mom"

Full Example: Combining All Three

const Redis = require("ioredis");
const redis = new Redis({ host: "localhost", port: 6379 });

async function main() {
  // 1. SETEX: Temporary promo code
  await redis.setex("promo:2024", 60, "CG50");

  // 2. HSET: User profile
  await redis.hset("user:456", {
    name: "Dev",
    role: "developer",
  });

  // 3. LPUSH: Recent activities
  await redis.lpush("activity:456", "Logged in");
  await redis.lpush("activity:456", "Updated profile");

  // Retrieve data
  const promoCode = await redis.get("promo:2024");
  const user = await redis.hgetall("user:456");
  const activities = await redis.lrange("activity:456", 0, -1);

  console.log({ promoCode, user, activities });
}

main().catch(console.error);

Output (if run within 60 seconds):

{
  promoCode: 'CG50',
  user: { name: 'Dev', role: 'developer' },
  activities: [ 'Updated profile', 'Logged in' ]
}

Key Takeaways

Command Description Example
SETEX Set key-value with expiration await redis.setex("key", 30, "value")
HSET Store an object (hash) await redis.hset("user:1", { name: "Alice" })
HGETALL Get entire hash await redis.hgetall("user:1")
LPUSH Add to a list (left side) await redis.lpush("tasks", "task1")
LRANGE Get list items await redis.lrange("tasks", 0, -1)

Try It Yourself!

  1. Run the examples above in your app.js file.
  2. Modify the keys/values (e.g., change user:123 to product:789).
  3. Use redis-cli to check data manually:
    docker exec -it my-redis redis-cli
    Then run commands like:
    GET promo:2024
    HGETALL user:456
    LRANGE activity:456 0 -1
    

Common Use Cases

  • SETEX: Temporary sessions, OTP codes, flash sales.
  • HSET: User profiles, configurations, product details.
  • LPUSH: Activity feeds, task queues, recent searches.

Let’s build a simple API caching system using Redis in Node.js. This will speed up your app by storing responses from slow/downstream APIs and serving them instantly on subsequent requests.


How It Works

  1. User requests data (e.g., /posts/1).
  2. Check if data is cached in Redis.
    • Yes: Return cached data immediately.
    • No: Fetch from the API, store it in Redis, then return it.

Step 1: Setup

  1. Install required packages:
    npm install express axios ioredis
  2. Create app.js and add this code:
const express = require("express");
const axios = require("axios");
const Redis = require("ioredis");

const app = express();
const redis = new Redis({ host: "localhost", port: 6379 }); // Redis Docker container
const PORT = 3000;

// Example API to cache (we'll use JSONPlaceholder)
const API_URL = "https://jsonplaceholder.typicode.com/posts";

// Cache middleware
async function cacheMiddleware(req, res, next) {
  const { id } = req.params;
  const cacheKey = `post:${id}`;

  try {
    // Check Redis for cached data
    const cachedData = await redis.get(cacheKey);
    if (cachedData) {
      console.log("Serving from cache 🚀");
      return res.send(JSON.parse(cachedData));
    }
    next(); // No cache → proceed to fetch
  } catch (err) {
    console.error("Cache error:", err);
    next();
  }
}

// Route: Get a post by ID (with caching)
app.get("/posts/:id", cacheMiddleware, async (req, res) => {
  const { id } = req.params;
  const cacheKey = `post:${id}`;

  try {
    // Fetch from API
    const response = await axios.get(`${API_URL}/${id}`);
    const data = response.data;

    // Save to Redis (expire after 1 hour = 3600 seconds)
    await redis.setex(cacheKey, 3600, JSON.stringify(data));
    console.log("Serving from API ⚡");

    res.send(data);
  } catch (err) {
    res.status(500).send("Error fetching post");
  }
});

app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});

Step 2: Test the Caching

  1. Start the server:
    node app.js
  2. Open your browser or use curl:
    curl http://localhost:3000/posts/1
    • First request: Fetches from API, saves to Redis.
    • Second request: Returns cached data instantly!

Key Explanations

Code Part Purpose
cacheMiddleware Checks Redis for cached data before hitting the API.
redis.setex Stores data with expiration (1 hour here).
JSON.parse/JSON.stringify Converts data to/from strings (Redis only stores strings).
axios Fetches data from the external API.

Example Output

First Request (API):

Serving from API ⚡
{
  "userId": 1,
  "id": 1,
  "title": "sunt aut...",
  "body": "quia et suscipit..."
}

Second Request (Cache):

Serving from cache 🚀
{
  "userId": 1,
  "id": 1,
  "title": "sunt aut...",
  "body": "quia et suscipit..."
}

Why This Works

  • Faster Responses: Cached data avoids slow API calls.
  • Reduced Load: Fewer requests to the external API.
  • TTL (Time-To-Live): Auto-deletes stale data with setex.

Enhancements (Optional)

  1. Cache Invalidation: Delete keys when data changes:
    app.delete("/posts/:id", async (req, res) => {
      const { id } = req.params;
      await redis.del(`post:${id}`);
      res.send("Cache invalidated");
    });
  2. Cache All Posts: Modify the middleware to cache list endpoints.
  3. Error Handling: Add retries if Redis/API fails.

Real-World Use Cases

  1. Cache weather data, stock prices, or news feeds.
  2. Store results of expensive database queries.
  3. Speed up e-commerce product listings.