Getting started with Valkey using JavaScript
Tags: valkey, redis, javascript, database
Published: 2024-06-17 04:27:12

> Run existing Redis apps with Valkey and learn how to use it with LangChain

[Valkey](https://valkey.io/) is an open source alternative to [Redis](https://redis.io/). It's a community-driven, [Linux Foundation project](https://www.linuxfoundation.org/press/linux-foundation-launches-open-source-valkey-community) created to keep the project available for use and distribution under the open source Berkeley Software Distribution (BSD) 3-clause license after the [Redis license changes](https://redis.com/blog/redis-adopts-dual-source-available-licensing/).

I think the path to Valkey was well summarised in this [inaugural blog post](https://valkey.io/blog/hello-world/):


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qtugwhgl2dfwb5z8d5nj.png)

I will walk through how to use Valkey for JavaScript applications using existing clients in Redis ecosystem as well as [iovalkey](https://github.com/valkey-io/iovalkey) (a friendly fork of [ioredis](https://github.com/redis/ioredis)).

## Using Valkey with `node-redis`

[node-redis](https://github.com/redis/node-redis) is a popular and widely used client. Here is a [simple program](https://github.com/abhirockzz/valkey-javascript/blob/master/subscriber.js) that uses the [Subscriber](https://valkey.io/commands/subscribe/) component of the [PubSub](https://valkey.io/commands/pubsub/) API to subscribe to a channel.

```javascript
import redis from 'redis';

const client = redis.createClient();
const channelName = 'valkey-channel';

(async () => {
  try {
    await client.connect();
    console.log('Connected to Redis server');

    await client.subscribe(channelName, (message, channel) => {
      console.log(`message "${message}" received from channel "${channel}"`)
    });

    console.log('Waiting for messages...');
  } catch (err) {
    console.error('Error:', err);
  }
})();
```

To try this with Valkey, let's start an instance using the [Valkey Docker image](https://hub.docker.com/r/valkey/valkey/):

```bash
docker run --rm -p 6379:637 valkey/valkey
```

Also, head over to https://valkey.io/download to get OS specific distribution, or use Homebrew (on Mac) - `brew install valkey`. You should now be able to use the Valkey CLI (`valkey-cli`).

Get the code from GitHub repo:

```bash
git clone https://github.com/abhirockzz/valkey-javascript
cd valkey-javascript

npm install
```

Start the subscriber app:

```bash
node subscriber.js
```

Publish a message and ensure that the subscriber is able to receive it:

```bash
valkey-cli PUBLISH valkey-channel 'hello valkey'
```

Nice! We were able to write a simple application with an existing Redis client and run using Valkey (instead of Redis). Sure, this is an over-simplified example, but there were no code changes required.

## Use Valkey with `ioredis` client

[ioredis](https://github.com/redis/ioredis) is another popular client. To be doubly sure, lets try `ioredis` with Valkey as well. Lets write a [publisher application](https://github.com/abhirockzz/valkey-javascript/blob/master/publisher.js):

```javascript
import Redis from 'ioredis';

const redisClient = new Redis();
const channelName = 'valkey-channel';

const message = process.argv[2];

if (!message) {
  console.error('Please provide a message to publish.');
  process.exit(1);
}

async function publishMessage() {
  try {
    const receivedCount = await redisClient.publish(channelName, message);
    console.log(`Message "${message}" published to channel "${channelName}". Received by ${receivedCount} subscriber(s).`);
  } catch (err) {
    console.error('Error publishing message:', err);
  } finally {
    // Close the client connection
    await redisClient.quit();
  }
}

publishMessage();
```

Run the publisher, and confirm that the subscriber app is able to receive it:

```bash
node publisher.js 'hello1'
node publisher.js 'hello2'
```

You should see these logs in the subscriber application:

```text
message "hello1" received from channel "valkey-channel"
message "hello2" received from channel "valkey-channel"
```

## Switch to `iovalkey` client

As mentioned, `iovalkey` is a fork of `ioredis`. I made the following changes to port the producer code to use `iovalkey`:

1. Commented out `import Redis from 'ioredis';`
2. Added `import Redis from 'iovalkey';`
3. Installed `iovalkey` - `npm install iovalkey`

Here is the updated version - yes, this was all I needed to change (at least for this simple application):

```javascript
// import Redis from 'ioredis';
import Redis from 'iovalkey';
```

Run the new `iovalkey` based publisher, and confirm that the subscriber is able to receive it:

```bash
node publisher.js 'hello from iovalkey'
```

You should see these logs in the subscriber application:

```text
message "hello from iovalkey" received from channel "valkey-channel"
```

Awesome, this is going well. We are ready to sprinkle some generative AI now!

## Use Valkey with LangchainJS

Along with Python, JavaScript/TypeScript is also being used in the generative AI ecosystem. [LangChain](https://www.langchain.com/) is a popular framework for developing applications powered by large language models (LLMs). LangChain has JS/TS support in the form of [LangchainJS](https://js.langchain.com/v0.2/docs/introduction/).

Having worked a lot [with the Go port](https://community.aws/tags/langchaingo) ([langchaingo](https://github.com/tmc/langchaingo)), as well as [Python](https://community.aws/content/2aq9ju6xvYtywGVbuPoWFTk5oK4/build-a-streamlit-app-using-langchain-amazon-bedrock-and-redis), I wanted to try LangchainJS. 

One of the common use cases is to use Redis as a chat history component in generative AI apps. LangchainJS has this built-in, so let's try it out with Valkey.

### Using Valkey as chat history in Langchain

To install LangchainJS:

```bash
npm install langchain
```

For the LLM, I will be using [Amazon Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html) (its [supported natively with LangchainJS](https://js.langchain.com/v0.2/docs/integrations/platforms/aws#bedrock)), but feel free to use others. 

For Amazon Bedrock, you will need to [configure and set up Amazon Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/setting-up.html?sc_channel=el&sc_campaign=genaiwave&sc_content=amazon-bedrock-golang-getting-started&sc_geo=mult&sc_country=mult&sc_outcome=acq), including [requesting access](https://docs.aws.amazon.com/bedrock/latest/userguide/setting-up.html#manage-model-access?sc_channel=el&sc_campaign=genaiwave&sc_content=amazon-bedrock-golang-getting-started&sc_geo=mult&sc_country=mult&sc_outcome=acq) to the Foundation Model(s).


[Here is the chat application](https://github.com/abhirockzz/valkey-javascript/blob/master/chat.js). As you can see, it uses the `RedisChatMessageHistory` component.


```javascript
import { BedrockChat } from "@langchain/community/chat_models/bedrock";
import { RedisChatMessageHistory } from "@langchain/redis";
import { ConversationChain } from "langchain/chains";
import { BufferMemory } from "langchain/memory";
import prompt from "prompt";

import {
  ChatPromptTemplate,
  MessagesPlaceholder,
} from "@langchain/core/prompts";

const chatPrompt = ChatPromptTemplate.fromMessages([
  [
    "system",
    "The following is a friendly conversation between a human and an AI.",
  ],
  new MessagesPlaceholder("chat_history"),
  ["human", "{input}"],
]);

const memory = new BufferMemory({
  chatHistory: new RedisChatMessageHistory({
    sessionId: new Date().toISOString(),
    sessionTTL: 300,
    host: "localhost",
    port: 6379,
  }),
  returnMessages: true,
  memoryKey: "chat_history",
});

const model = "anthropic.claude-3-sonnet-20240229-v1:0"
const region = "us-east-1"

const langchainBedrockChatModel = new BedrockChat({
  model: model,
  region: region,
  modelKwargs: {
    anthropic_version: "bedrock-2023-05-31",
  },
});

const chain = new ConversationChain({
   llm: langchainBedrockChatModel,
   memory: memory,
   prompt: chatPrompt,
});


while (true) {
  prompt.start({noHandleSIGINT: true});
  const {message} = await prompt.get(['message']);
  const response = await chain.invoke({
    input: message,
  });
  console.log(response);
}
```

Run the application:

```bash
node chat.js
```

Start a conversation:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7plb0b56g02tc5imhb6s.png)

If you peek into Valkey, notice that the conversations are saved in a `List`:

```bash
valkey-cli keys *
valkey-cli LRANGE <enter list name> 0 -1
```

> Don't run` keys *` in production - its just for demo purposes


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/06y1dj0gf07lgdn4pyno.png)

### Using `iovalkey` implementation for chat history

The [current implementation](https://github.com/langchain-ai/langchainjs/blob/d09f63a/libs/langchain-redis/src/chat_histories.ts) uses the `node-redis` client, but I wanted to try out `iovalkey` client. I am not a JS/TS expert, but it was simple enough to port the existing implementation.You can [refer to the code on GitHub](https://github.com/abhirockzz/valkey-javascript)

As far as the client (chat) app is concerned, I only had to make a few changes to switch the implementation:

- Comment out `import { RedisChatMessageHistory } from "@langchain/redis";`
- Add `import { ValkeyChatMessageHistory } from "./valkey_chat_history.js";`
- Replace `RedisChatMessageHistory` with `ValkeyChatMessageHistory` (while creating the `memory` instance)

It worked the same way as above. Feel free to give it a try!

## Wrapping up

It's still early days for the Valkey (at the time of writing), and there is a long way to go. I'm interested in the how the project evolves and also the client ecosystem for Valkey.

Happy Building!