Implementing QR Codes at Scale: Design, Performance, and Edge Cases
ā¢By Sameer Nimjeā¢4 min read
qr-codesystem-designnextjsreactbackendperformance
Loading...
Modern apps like payments, login systems, and ticketing rely heavily on QR scanning.
But building a fast, scalable, and optimized QR system requires careful design.
This blog covers a real-world system design using TypeScript (Frontend + Backend) with performance optimizations.
We want to build a QR system that:
Camera ā Decode ā Cache Check ā Backend ā Decision Engine ā Action| Feature | Description |
|---|---|
| Config-based scan | Behavior controlled via config |
| Failed QR cache | Avoid repeated failures |
| Internal scan | Navigate inside app |
| External scan | Open browser |
| Redirect flag | Control navigation |
import { useState } from "react";
const failedCache = new Set<string>();
export function useQRScanner() {
const [lastScanned, setLastScanned] = useState<string | null>(null);
const handleScan = async (data: string) => {
if (!data) return;
// š« Prevent re-scan of failed QR
if (failedCache.has(data)) return;
setLastScanned(data);
try {
const res = await fetch(`/api/scan?qr=${encodeURIComponent(data)}`);
const result = await res.json();
if (!result.success) {
failedCache.add(data);
return;
}
handleRedirect(result);
} catch {
failedCache.add(data);
}
};
return { handleScan };
}function handleRedirect(result: any) {
const { type, url, redirect } = result;
if (type === "INTERNAL" && redirect !== false) {
window.location.href = url;
} else if (type === "EXTERNAL") {
window.open(url, "_blank");
}
}export const QR_CONFIG = {
internalDomains: ["myapp.com"],
retryBlock: true,
};import { Request, Response } from "express";
export const scanQR = async (req: Request, res: Response) => {
const qr = req.query.qr as string;
if (!qr) {
return res.json({ success: false });
}
try {
const isExternal = qr.startsWith("http");
if (isExternal) {
return res.json({
success: true,
type: "EXTERNAL",
url: qr,
});
}
return res.json({
success: true,
type: "INTERNAL",
url: `/app/${qr}`,
redirect: true,
});
} catch {
return res.json({ success: false });
}
};const failedCache = new Set<string>();let lastScanTime = 0;
if (Date.now() - lastScanTime < 2000) return;
lastScanTime = Date.now();if (!qr.startsWith("http") && qr.length < 3) {
return;
}Use Redis:
if (cache.has(qr)) return cache.get(qr);qr-system/
ā
āāā frontend/
ā āāā hooks/useQRScanner.ts
ā āāā utils/redirect.ts
ā
āāā backend/
ā āāā controllers/scan.controller.ts
ā āāā config/config.ts
ā āāā services/qr.service.ts
ā
āāā shared/
āāā types.tsIf you want, I can: