New Project
Learn to use AWS ElastiCache with Next.js and Valkey for reliable message queue processing using streams.
This is an example of a Next.js application using AWS ElastiCache for implementing a reliable message queue with streams. The template demonstrates a contact form processor where messages are queued, consumed, and acknowledged using Valkey's streaming capabilities.
This template demonstrates the code pattern for implementing message queues with Valkey streams. It's designed to work with AWS ElastiCache in production environments.
Execute create-next-app with pnpm to bootstrap the example:
pnpm create next-app --example https://github.com/vercel/examples/tree/main/solutions/aws-message-queue-elasticache
Run Valkey locally:
Using Docker:
docker run -d -p 6379:6379 valkey/valkey:latest
Or install Valkey directly following the official installation guide.
Configure environment:
Create an .env.local file:
VALKEY_ENDPOINT=localhost:6379
Start the development server:
pnpm dev
Visit http://localhost:3000 to see the application.
AWS ElastiCache clusters run within a VPC (private network), which requires network connectivity setup for production deployments on Vercel.
For Vercel Enterprise customers, connectivity to AWS ElastiCache is available through Vercel Secure Compute, which enables private network access between Vercel Functions and AWS VPC resources.
High-level setup steps:
AWS Side:
Vercel Side:
VALKEY_ENDPOINT=your-cluster.cache.amazonaws.com:6379
For detailed networking configuration, refer to the Vercel Secure Compute documentation.
⚠️ Note: This demo does not include authentication. In production, add authentication middleware to protect the API endpoints from unauthorized access.
This template demonstrates a reliable serverless message queue workflow:
Key Features:
The application provides a single API route (/api/messages) with three HTTP methods demonstrating the message queue pattern:
POST /api/messages - Add a new message to the queue (contact form submission)GET /api/messages - Read the next unprocessed message from the consumer groupDELETE /api/messages?messageId=<id> - Acknowledge and remove a message from the pending listImportant Notes:
streamMessageId returned by GET is Valkey's unique stream entry ID and must be used for the DELETE operation.1. Produce Message (Add to Queue)
curl -X POST http://localhost:3000/api/messages \-H "Content-Type: application/json" \-d '{"name": "Test User","email": "test@example.com","message": "Hello from local dev!"}'
Response:
{"streamMessageId": "1764009314892-0","timestamp": "2024-11-24T18:35:14.890Z"}
2. Consume Message (Read from Queue)
curl http://localhost:3000/api/messages
Response with message:
{"message": {"streamMessageId": "1764009314892-0","name": "Test User","email": "test@example.com","message": "Hello from local dev!","timestamp": "2024-11-24T18:35:14.890Z","claimed": true}}
Response when queue is empty:
{ "message": null }
Note: The claimed field indicates whether this message was recovered from the Pending Entries List (a previously delivered but unacknowledged message). Messages idle for more than 60 seconds are automatically reclaimed.
3. Acknowledge Message (Mark as Processed)
Critical: Use the streamMessageId from step 2 for the DELETE operation:
curl -X DELETE "http://localhost:3000/api/messages?messageId=1764009314892-0"
Response:
{ "success": true }
This template consists of two UI views that demonstrate the complete message queue workflow:
/)The home page features a contact form where visitors can submit their name, email, and message. Upon submission, the message is immediately added to the Valkey stream and a confirmation is displayed.
/process)The processing view allows reviewers to consume and acknowledge messages from the queue. The page loads the messages on page load and displays:
Clicking Acknowledge confirms the message and removes it from the queue. A success message appears with a Next Message button to load the next message in the queue.
When the queue is empty, a "No message to process" indicator appears.
Message Recovery: If you GET a message but don't DELETE (acknowledge) it, the message stays in the Pending Entries List. After 60 seconds of idle time, subsequent GET requests will automatically reclaim that message (indicated by "claimed": true in the response). This is a reliability feature that handles consumer failures.
For clean testing, if you want to reset and start fresh:
# Access your Valkey instancedocker exec -it <container_id> valkey-cli# Delete the entire stream (removes consumer group too)DEL contact-messages# Or just delete the consumer groupXGROUP DESTROY contact-messages contact-processors# The consumer group will be recreated automatically on next GET
Checking Pending Messages: To see what's currently in the Pending Entries List:
# Access Valkeydocker exec -it <container_id> valkey-cli# View pending messagesXPENDING contact-messages contact-processors# View all messages in the streamXRANGE contact-messages - +