Created
May 31, 2026 08:57
-
-
Save Gaurav8757/d1f3420aa815876665b65ccc389f67df to your computer and use it in GitHub Desktop.
OTP Verify | OTP Timer With Redis TTL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Best approach: | |
| // OTP send API se initial TTL le lo. | |
| // Frontend me local countdown chalao. | |
| // Jab page refresh ho ya component remount ho tab TTL API hit karo. | |
| // Backend API | |
| // send otp | |
| app.post("/otp/send", async (req, res) => { | |
| const { phone } = req.body; | |
| if (!phone) { | |
| return res.status(400).json({ error: "Phone number is required" }); | |
| } | |
| const otpCode = generateOtp(); | |
| await redisClient.set(otpKey(phone), Number(otpCode), "EX", 100); // valid only for 60 seconds | |
| const ttl = await redisClient.ttl(otpKey(phone)); | |
| return res.status(200).json({ success: true, data: `OTP for ${phone} is ${otpCode} and TTL is ${Math.floor(ttl)} seconds` }); | |
| }); | |
| // verify otp | |
| app.post("/otp/verify", async (req, res) => { | |
| const { phone, otp } = req.body; | |
| // if phone and otp are not found, return error | |
| if(!phone || !otp){ | |
| return res.status(400).json({ error: "Phone and OTP are required!" }); | |
| } | |
| // if OTP is not found, return error | |
| if(!Boolean(await redisClient.exists(otpKey(phone)))){ | |
| return res.status(404).json({ success: false, data: "OTP not found!" }); | |
| } | |
| // get the stored OTP | |
| const storedOtp = await redisClient.get(otpKey(phone)); | |
| // if OTP is null, return error | |
| if(!storedOtp){ | |
| return res.status(404).json({ success: false, data: "OTP not found!" }); | |
| } | |
| // if OTP is not matching, return error | |
| if(storedOtp !== otp){ | |
| return res.status(404).json({ success: false, data: "Invalid OTP!" }); | |
| } | |
| await redisClient.del(otpKey(phone)); | |
| return res.status(200).json({ success: true, data: "OTP verified successfully!" }); | |
| }); | |
| // ttl | |
| app.get("/otp/:phone/ttl", async (req, res) => { | |
| const { phone } = req.params; | |
| if(!phone){ | |
| return res.status(400).json({ error: "Phone number is required!" }); | |
| } | |
| const ttl = await redisClient.ttl(otpKey(phone)); | |
| return res.status(200).json({ success: true, data: ttl }); | |
| }); | |
| // Frontend | |
| import { useEffect, useState } from "react"; | |
| export default function OtpTimer() { | |
| const [timeLeft, setTimeLeft] = useState(0); | |
| // OTP send API se initial TTL le lo. | |
| const sendOtp = async () => { | |
| const res = await fetch("/otp/send", { | |
| method: "POST", | |
| }); | |
| const data = await res.json(); | |
| setTimeLeft(data.ttl); | |
| }; | |
| // Jab page refresh ho ya component remount ho tab TTL API hit karo. | |
| useEffect(() => { | |
| const getTTL = async () => { | |
| const res = await fetch(`/otp/ttl/${phone}`); | |
| const data = await res.json(); | |
| setTimeLeft(data.ttl); | |
| }; | |
| getTTL(); | |
| }, []); | |
| // Frontend me local countdown chalao | |
| useEffect(() => { | |
| if (timeLeft <= 0) return; | |
| const interval = setInterval(() => { | |
| setTimeLeft((prev) => prev - 1); | |
| }, 1000); | |
| return () => clearInterval(interval); | |
| }, [timeLeft]); | |
| return ( | |
| <> | |
| <button onClick={sendOtp}>Send OTP</button> | |
| <p> | |
| Remaining: {Math.floor(timeLeft / 60)}: | |
| {(timeLeft % 60).toString().padStart(2, "0")} | |
| </p> | |
| </> | |
| ); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment