This is a single page application powered by upstash and next.js.
We have developed an advanced version of Roadmap Voting App where users should
log in to request or vote for a new feature. See it live version in Upstash
Roadmap. See the blog
post to learn about it. The
below example allows users to request features anonymously.
In this tutorial we will write a single page application which uses Redis as
state store in a Next.js application.The example is a basic roadmap voting application where users enter and vote for
feature requests. You can check the complete application in
Upstash Roadmap page
You can check the source code of the complete application
here.
Thanks to Upstash&Vercel integration,
you can deploy the application yourself with zero cost/code by clicking below:
We will use Next.js as web framework. So let’s create a next.js app and install
the redis client first.npx create-next-app nextjs-with-redisnpm install ioredis
The list API connects to the Redis and fetches feature requests ordered by their
scores (votes) from the Sorted Set roadmap.
This example uses ioredis, you can copy the connection string from the
Node tab in the console.
Copy
Ask AI
import { fixUrl } from "./utils";import Redis from "ioredis";module.exports = async (req, res) => { let redis = new Redis(fixUrl(process.env.REDIS_URL)); let n = await redis.zrevrange("roadmap", 0, 100, "WITHSCORES"); let result = []; for (let i = 0; i < n.length - 1; i += 2) { let item = {}; item["title"] = n[i]; item["score"] = n[i + 1]; result.push(item); } redis.quit(); res.json({ body: result, });};
This API connects to the Redis server and add a new element to the sorted set
(roadmap) . We use “NX” flag together with ZADD, so a user will not be able to
overwrite an existing feature request with the same title.
Copy
Ask AI
import Redis from "ioredis";import { fixUrl } from "./utils";module.exports = async (req, res) => { let redis = new Redis(fixUrl(process.env.REDIS_URL)); const body = req.body; const title = body["title"]; if (!title) { redis.quit(); res.json({ error: "Feature can not be empty", }); } else if (title.length < 70) { await redis.zadd("roadmap", "NX", 1, title); redis.quit(); res.json({ body: "success", }); } else { redis.quit(); res.json({ error: "Max 70 characters please.", }); }};
This API updates (increments) the score of the selected feature request. It also
keeps the IP addresses of the user to prevent multiple votes on the same feature
request.
Copy
Ask AI
import Redis from "ioredis";import { fixUrl } from "./utils";module.exports = async (req, res) => { let redis = new Redis(fixUrl(process.env.REDIS_URL)); const body = req.body; const title = body["title"]; let ip = req.headers["x-forwarded-for"] || req.headers["Remote_Addr"] || "NA"; let c = ip === "NA" ? 1 : await redis.sadd("s:" + title, ip); if (c === 0) { redis.quit(); res.json({ error: "You can not vote an item multiple times", }); } else { let v = await redis.zincrby("roadmap", 1, title); redis.quit(); res.json({ body: v, }); }};
This API simply adds the user’s email to the Redis Set. As the Set already
ensures the uniqueness, we only need to check if the input is a valid email.
Copy
Ask AI
import Redis from "ioredis";import { fixUrl } from "./utils";module.exports = async (req, res) => { let redis = new Redis(fixUrl(process.env.REDIS_URL)); const body = req.body; const email = body["email"]; redis.on("error", function (err) { throw err; }); if (email && validateEmail(email)) { await redis.sadd("emails", email); redis.quit(); res.json({ body: "success", }); } else { redis.quit(); res.json({ error: "Invalid email", }); }};function validateEmail(email) { const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; return re.test(String(email).toLowerCase());}
If you deploy this application with Vercel; Vercel runs AWS Lambda functions
to back the API implementations. For best performance choose the the same
region for both Vercel functions and Upstash cluster.