What We’re Building
A URL shortener that automatically generates a QR code for every shortened link. Users paste a long URL, get a short link, and can download a QR code that points to it.
We’ll use Node.js with Express for the backend and the ToolCenter QR Code API for generating QR codes.
Prerequisites
- Node.js 18+ installed
- A ToolCenter key
- Basic knowledge of Express.js
Project Setup
mkdir url-shortener-qr
cd url-shortener-qr
npm init -y
npm install express nanoid
Project Structure
Step 1: Build the Server
Create server.js:
const express = require('express');
const { nanoid } = require('nanoid');
const app = express();
app.use(express.json());
app.use(express.static('public'));
// In-memory store (use a database in production)
const urls = new Map();
const BASE_URL = process.env.BASE_URL || 'http://localhost:3000';
const DEVTOOLBOX_KEY = process.env.DEVTOOLBOX_API_KEY;
// Shorten a URL
app.post('/api/shorten', async (req, res) => {
const { url } = req.body;
if (!url || !isValidUrl(url)) {
return res.status(400).json({ error: 'Invalid URL' });
}
const code = nanoid(7);
const shortUrl = `${BASE_URL}/${code}`;
urls.set(code, {
originalUrl: url,
shortUrl,
createdAt: new Date().toISOString(),
clicks: 0,
});
// Generate QR code via ToolCenter
const qrCodeUrl = await generateQRCode(shortUrl);
res.json({
shortUrl,
originalUrl: url,
qrCode: qrCodeUrl,
code,
});
});
// Redirect short URLs
app.get('/:code', (req, res) => {
const entry = urls.get(req.params.code);
if (!entry) {
return res.status(404).send('URL not found');
}
entry.clicks++;
res.redirect(301, entry.originalUrl);
});
// Get URL stats
app.get('/api/stats/:code', (req, res) => {
const entry = urls.get(req.params.code);
if (!entry) {
return res.status(404).json({ error: 'Not found' });
}
res.json(entry);
});
function isValidUrl(string) {
try {
new URL(string);
return true;
} catch {
return false;
}
}
app.listen(3000, () => console.log('Server running on port 3000'));
Step 2: Integrate the QR Code API
Add the QR code generation function to server.js:
const https = require('https');
async function generateQRCode(url) {
const response = await fetch('https://api.toolcenter.dev/v1/qrcode', {
method: 'POST',
headers: {
'Authorization': `Bearer ${DEVTOOLBOX_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
data: url,
size: 300,
format: 'png',
errorCorrection: 'M',
foregroundColor: '#000000',
backgroundColor: '#ffffff',
}),
});
if (!response.ok) {
throw new Error(`QR code generation failed: ${response.status}`);
}
// Convert to base64 data URL for embedding
const buffer = await response.arrayBuffer();
const base64 = Buffer.from(buffer).toString('base64');
return `data:image/png;base64,${base64}`;
}
Step 3: Build the Frontend
Create public/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>URL Shortener + QR Code</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui; max-width: 600px; margin: 50px auto; padding: 20px; }
h1 { margin-bottom: 30px; }
.input-group { display: flex; gap: 10px; margin-bottom: 20px; }
input { flex: 1; padding: 12px; border: 2px solid #ddd; border-radius: 8px; font-size: 16px; }
button { padding: 12px 24px; background: #667eea; color: white; border: none; border-radius: 8px; cursor: pointer; font-size: 16px; }
button:hover { background: #5a6fd6; }
.result { display: none; background: #f8f9fa; padding: 20px; border-radius: 8px; text-align: center; }
.result.show { display: block; }
.short-url { font-size: 20px; font-weight: bold; color: #667eea; margin: 15px 0; word-break: break-all; }
.qr-code img { max-width: 200px; margin: 15px 0; }
.stats { font-size: 14px; color: #666; }
</style>
</head>
<body>
<h1>🔗 URL Shortener + QR</h1>
<div class="input-group">
<input type="url" id="urlInput" placeholder="Paste your long URL here..." />
<button onclick="shortenUrl()">Shorten</button>
</div>
<div class="result" id="result">
<p>Your shortened URL:</p>
<div class="short-url" id="shortUrl"></div>
<div class="qr-code"><img id="qrCode" alt="QR Code" /></div>
<p class="stats">QR code generated via ToolCenter</p>
</div>
<script>
async function shortenUrl() {
const url = document.getElementById('urlInput').value;
if (!url) return;
const response = await fetch('/api/shorten', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url }),
});
const data = await response.json();
document.getElementById('shortUrl').textContent = data.shortUrl;
document.getElementById('qrCode').src = data.qrCode;
document.getElementById('result').classList.add('show');
}
</script>
</body>
</html>
Step 4: Custom QR Code Styling
Make your QR codes branded with custom colors:
async function generateBrandedQR(url, options = {}) {
const response = await fetch('https://api.toolcenter.dev/v1/qrcode', {
method: 'POST',
headers: {
'Authorization': `Bearer ${DEVTOOLBOX_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
data: url,
size: options.size || 400,
format: 'png',
errorCorrection: 'H', // High correction for logo overlay
foregroundColor: options.color || '#667eea',
backgroundColor: '#ffffff',
margin: 2,
}),
});
return response;
}
Step 5: Adding Analytics
Track clicks and display stats:
app.get('/api/analytics', (req, res) => {
const allUrls = Array.from(urls.entries()).map(([code, data]) => ({
code,
...data,
}));
const totalClicks = allUrls.reduce((sum, u) => sum + u.clicks, 0);
res.json({
totalUrls: allUrls.length,
totalClicks,
urls: allUrls.sort((a, b) => b.clicks - a.clicks),
});
});
Step 6: Run It
DEVTOOLBOX_API_KEY=your_key_here node server.js
Visit http://localhost:3000, paste a URL, and get a shortened link with a QR code.
Production Considerations
Before deploying to production:
- Use a real database — Replace the in-memory Map with PostgreSQL or Redis
- Add rate limiting — Prevent abuse with
express-rate-limit - Custom domains — Use a short domain like
sho.rt - Bulk generation — Add an endpoint that accepts multiple URLs
- QR code caching — Cache generated QR codes to avoid redundant API calls
const cache = new Map();
async function getCachedQR(url) {
if (cache.has(url)) return cache.get(url);
const qr = await generateQRCode(url);
cache.set(url, qr);
return qr;
}
Conclusion
You’ve built a functional URL shortener with automatic QR code generation. The ToolCenter QR Code API handles the heavy lifting of generating high-quality, customizable QR codes. From here, you can extend the project with user accounts, custom slugs, analytics dashboards, and branded QR code designs.