A minimal LinkSwift‑inspired downloader built with Node.js and SQLite
Welcome to another official UVF IT build guide! In the next 45 minutes you’ll create a tiny browser‑based direct‑link downloader that mimics the core of LinkSwift, using only free, open‑source tools.
We’ll scaffold a Node/Express app, store generated links in a local SQLite file, expose a tiny API, and serve a one‑page UI that lets you paste a URL and get a direct download link.
Create a fresh folder for the clone, initialize a new npm project, and pull in the two dependencies we need – Express for the web server and better‑sqlite3 for a simple, synchronous SQLite wrapper. Doing it all in one line keeps the setup tidy and repeatable.
After this step you’ll have a package.json and a node_modules directory ready for the next phases.
mkdir linkswift-clone && cd linkswift-clone && npm init -y && npm install express better-sqlite3We create a tiny module (db.js) that opens (or creates) a links.db file in the project root. It ensures a links table exists and exports a saveLink(original, direct) function that inserts a row and returns its numeric id – the primary key we’ll later use to retrieve the link.
Using better‑sqlite3 means the API is synchronous, which keeps the code easy to read for beginners while still being performant for a small clone.
db.js
const Database = require('better-sqlite3');
const db = new Database('links.db');
// Ensure table exists
db.exec(`CREATE TABLE IF NOT EXISTS links (
id INTEGER PRIMARY KEY AUTOINCREMENT,
original TEXT NOT NULL,
direct TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);`);
/**
* Store a pair of URLs and get the row id.
* @param {string} original - The URL the user pasted.
* @param {string} direct - The rewritten direct‑download URL.
* @returns {number} The newly inserted row id.
*/
function saveLink(original, direct) {
const stmt = db.prepare('INSERT INTO links (original, direct) VALUES (?, ?)');
const info = stmt.run(original, direct);
return info.lastInsertRowid;
}
module.exports = { saveLink };In this step we spin up an Express server that will act as the backend for our downloader clone. It will expose a /api/convert endpoint that accepts a JSON body with a url field, pretends to convert the link by appending ?download=1, stores the mapping in SQLite, and returns a short /d/:id link. We also add a redirect route /d/:id that looks up the stored direct link and sends the client there. Finally the server will serve static assets from the public folder so our UI can be loaded directly from the same origin.
server.js
const express = require('express');
const sqlite3 = require('sqlite3').verbose();
const path = require('path');
const app = express();
const db = new sqlite3.Database('links.db');
// Initialise table if not present
db.run('CREATE TABLE IF NOT EXISTS links(id INTEGER PRIMARY KEY AUTOINCREMENT, original TEXT, direct TEXT)');
app.use(express.json());
app.use(express.static(path.join(__dirname, 'public')));
// API: receive a URL, fake‑convert it, store and return short link
app.post('/api/convert', (req, res) => {
const {url} = req.body;
if (!url) return res.status(400).json({error: 'url required'});
const direct = url.includes('?') ? `${url}&download=1` : `${url}?download=1`;
db.run('INSERT INTO links(original, direct) VALUES(?,?)', [url, direct], function(err){
if (err) return res.status(500).json({error: err.message});
const short = `/d/${this.lastID}`;
res.json({short, direct});
});
});
// Redirect short link to stored direct URL
app.get('/d/:id', (req, res) => {
const id = req.params.id;
db.get('SELECT direct FROM links WHERE id = ?', [id], (err, row) => {
if (err || !row) return res.status(404).send('Not found');
res.redirect(row.direct);
});
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`🚀 http://localhost:${PORT}`));Now we create a tiny HTML page that lets users paste any URL, hit a button, and see a short link generated by our API. The page lives in public/index.html and uses the Fetch API to POST the URL to /api/convert. When the response arrives we display the short link as a clickable anchor so the user can test the redirect immediately.
public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>LinkSwift Clone</title>
<style>
body{font-family:sans-serif;margin:2rem}
input,button{padding:.5rem;font-size:1rem}
#result{margin-top:1rem}
</style>
</head>
<body>
<h1>Direct‑Link Downloader</h1>
<input id="urlInput" type="text" placeholder="Enter a URL" size="50" />
<button id="convertBtn">Get Short Link</button>
<div id="result"></div>
<script>
document.getElementById('convertBtn').addEventListener('click', async () => {
const url = document.getElementById('urlInput').value.trim();
if (!url) return alert('Please enter a URL');
const resp = await fetch('/api/convert', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({url})
});
const data = await resp.json();
const link = document.createElement('a');
link.href = data.short;
link.textContent = `${window.location.origin}${data.short}`;
const resultDiv = document.getElementById('result');
resultDiv.innerHTML = '';
resultDiv.appendChild(link);
});
</script>
</body>
</html>With the server and UI in place, start the application and open the browser to see everything work together. Run node server.js to launch the Express app; you should see a console line with the local URL. Then navigate to that address, paste any HTTP URL into the input, click the button, and verify that the generated short link appears and redirects to the modified URL with ?download=1. This confirms the end‑to‑end flow of our minimal LinkSwift clone.
node server.js| Check | Command / Why it matters |
|---|---|
| Server starts without errors | node server.js → should log ‘🚀 http://localhost:3000’" |
| UI loads in browser | Open http://localhost:3000 and see the input form" |
| Short link redirects correctly | Enter https://example.com, click button, then click generated link; you should be taken to https://example.com?download=1" |
You now have a working clone. Extend it by adding real host‑specific parsers, shorten URLs with a hashids library, or deploy to a free cloud host like Railway.