Learn how to build a customer loyalty program using Replit. Follow simple steps, code examples, and tips to boost engagement and retention.

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
A working loyalty program on Replit can be built as a small full-stack app, using Node.js + Express as backend API and a JSON file or SQLite database to track users’ points. You can also create a simple frontend (HTML + JS) to display points and let users earn more by completing actions. Replit can host this directly — once running, you can access it via the Replit web preview.
Create a new Replit: Choose the “Node.js” template.
Inside this Repl, you’ll already have a index.js file. That’s your backend entry point.
In index.js add this code to start your server and serve static frontend files:
// index.js
import express from "express";
import fs from "fs";
const app = express();
const PORT = process.env.PORT || 3000;
app.use(express.json()); // to read JSON request bodies
app.use(express.static("public")); // serve static frontend files
// Utility to read current users data from db.json
function readData() {
try {
const data = fs.readFileSync("db.json");
return JSON.parse(data);
} catch {
return { users: [] };
}
}
// Utility to save updated users
function writeData(data) {
fs.writeFileSync("db.json", JSON.stringify(data, null, 2));
}
// API endpoint to get all users
app.get("/api/users", (req, res) => {
const data = readData();
res.json(data.users);
});
// API endpoint to earn points
app.post("/api/earn", (req, res) => {
const { username, points } = req.body;
if (!username || typeof points !== "number") {
return res.status(400).json({ error: "Invalid input" });
}
const data = readData();
let user = data.users.find(u => u.username === username);
if (!user) {
user = { username, points };
data.users.push(user);
} else {
user.points += points;
}
writeData(data);
res.json({ success: true, user });
});
// API endpoint to get a single user's points
app.get("/api/user/:username", (req, res) => {
const data = readData();
const user = data.users.find(u => u.username === req.params.username);
if (!user) return res.status(404).json({ error: "User not found" });
res.json(user);
});
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
In public/index.html, you’ll create a simple interface to display and earn points.
<!DOCTYPE html>
<html>
<head>
<title>Loyalty Program</title>
</head>
<body>
<h1>User Loyalty Program</h1>
<input id="username" placeholder="Enter your username" />
<button id="check">Check Points</button>
<button id="earn">Earn 10 Points</button>
<p id="result"></p>
<script src="script.js"></script>
</body>
</html>
Then in public/script.js:
// public/script.js
const usernameInput = document.getElementById("username");
const resultEl = document.getElementById("result");
const checkBtn = document.getElementById("check");
const earnBtn = document.getElementById("earn");
checkBtn.addEventListener("click", async () => {
const username = usernameInput.value.trim();
if (!username) return alert("Enter username first!");
const res = await fetch(`/api/user/${username}`);
if (res.ok) {
const data = await res.json();
resultEl.textContent = `${data.username} has ${data.points} points.`;
} else {
resultEl.textContent = "User not found.";
}
});
earnBtn.addEventListener("click", async () => {
const username = usernameInput.value.trim();
if (!username) return alert("Enter username first!");
const res = await fetch(`/api/earn`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username, points: 10 })
});
const data = await res.json();
resultEl.textContent = `${data.user.username} now has ${data.user.points} points.`;
});
Create db.json in root folder with initial content:
{
"users": []
}
npm install express
If you later integrate a third-party or real database (like MongoDB Atlas), you can store your connection string securely in the Replit Secrets tab — access it in your code as process.env.MONGO\_URI. Never commit sensitive keys directly in files.
That’s a solid baseline loyalty program. It’s simple, fully working on Replit, and can be extended easily — you can later plug in a proper database, authentication, or even UI frameworks like React directly inside the same Repl if you upgrade to the Nix-enabled templates.
<script type="module">
import express from "express";
import Database from "@replit/database";
const app = express();
const db = new Database();
app.use(express.json());
// Issue or redeem loyalty points safely (transaction-like update)
app.post("/api/loyalty/update", async (req, res) => {
const { userId, change, reason } = req.body;
if (typeof userId !== "string" || typeof change !== "number") {
return res.status(400).json({ error: "Invalid input" });
}
const userKey = `loyalty:user:${userId}`;
const currentData = (await db.get(userKey)) || { points: 0, history: [] };
const updated = {
points: Math.max(0, currentData.points + change),
history: [
{ change, reason, date: new Date().toISOString() },
...currentData.history.slice(0, 49)
]
};
await db.set(userKey, updated);
res.json({ success: true, newPoints: updated.points });
});
app.get("/api/loyalty/:userId", async (req, res) => {
const data = await db.get(`loyalty:user:${req.params.userId}`);
res.json(data || { points: 0, history: [] });
});
app.listen(3000, () => console.log("Loyalty service running"));
</script>
<script type="module">
import express from "express";
import fetch from "node-fetch";
import Database from "@replit/database";
const app = express();
app.use(express.json());
const db = new Database();
// example: sync Replit-stored user points with an external CRM (like HubSpot or custom API)
app.post("/api/loyalty/syncCRM", async (req, res) => {
const { userId } = req.body;
if (!userId) return res.status(400).json({ error: "userId required" });
const userData = (await db.get(`loyalty:user:${userId}`)) || { points: 0 };
// external CRM endpoint saved in Secrets as CRM_WEBHOOK_URL
try {
const response = await fetch(process.env.CRM_WEBHOOK_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${process.env.CRM_API_KEY}`
},
body: JSON.stringify({
user\_id: userId,
loyalty\_points: userData.points,
source: "replit-loyalty"
})
});
if (!response.ok) {
const text = await response.text();
console.error("CRM sync failed:", text);
return res.status(502).json({ error: "CRM sync failed", detail: text });
}
const crmResult = await response.json();
res.json({ success: true, synced: true, crmResult });
} catch (err) {
console.error("Error syncing CRM:", err);
res.status(500).json({ error: "Server error during sync" });
}
});
app.listen(3000, () => console.log("Loyalty + CRM sync API running on Replit"));
</script>
<script type="module">
import express from "express";
import jwt from "jsonwebtoken";
import Database from "@replit/database";
const app = express();
const db = new Database();
const SECRET = process.env.JWT\_SECRET;
app.use(express.json());
// Middleware to verify JWT token before accessing secure loyalty rewards endpoint
function authenticate(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith("Bearer ")) {
return res.status(401).json({ error: "Unauthorized" });
}
try {
const token = authHeader.split(" ")[1];
req.user = jwt.verify(token, SECRET);
next();
} catch {
res.status(403).json({ error: "Invalid token" });
}
}
// Secure endpoint to redeem points for a reward
app.post("/api/loyalty/redeem", authenticate, async (req, res) => {
const { rewardId, cost } = req.body;
if (!rewardId || typeof cost !== "number") {
return res.status(400).json({ error: "Invalid input" });
}
const userKey = `loyalty:user:${req.user.id}`;
const current = (await db.get(userKey)) || { points: 0, rewards: [] };
if (current.points < cost) {
return res.status(400).json({ error: "Not enough points" });
}
const updated = {
...current,
points: current.points - cost,
rewards: [
{ id: rewardId, redeemedAt: new Date().toISOString() },
...current.rewards
]
};
await db.set(userKey, updated);
res.json({ success: true, newBalance: updated.points });
});
app.listen(3000, () => console.log("Secure loyalty redemption API running"));
</script>

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
A good loyalty program built on Replit should combine a simple backend (like Node.js + Express with a database) and a small frontend to display user points or rewards. Store sensitive data such as API keys or DB credentials in Replit Secrets, not directly in the code. You should make sure your app has persistent storage — Replit resets the filesystem sometimes, so put user data into a hosted database (like SQLite, Supabase, or MongoDB Atlas). Start small: allow user registration, tracking points, and redeeming rewards. Use routes for updating or reading points, and connect it to a frontend or internal dashboard. Replit’s always-on deployments make testing easy, but don’t expect the free Repl server to handle heavy production load.
In your Replit project, create these files and folders:
public/index.html)
// In Replit shell, type this to install Express and SQLite3
npm install express sqlite3
Use SQLite — it’s easy and works well on Replit. Add this to a new db.js file:
// db.js
const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database('./loyalty.db'); // creates or opens the DB file
// Create users table if it doesn’t exist
db.serialize(() => {
db.run(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
points INTEGER DEFAULT 0
)
`);
});
module.exports = db;
Inside index.js, set up your Express server. This will expose routes (endpoints) that handle loyalty logic — adding points, resetting them, etc.
// index.js
const express = require('express');
const db = require('./db');
const app = express();
app.use(express.json());
app.use(express.static('public')); // serves HTML in public folder
// Add new user
app.post('/users', (req, res) => {
const name = req.body.name;
db.run(`INSERT INTO users (name, points) VALUES (?, ?)`, [name, 0], function (err) {
if (err) return res.status(500).json({ error: err.message });
res.json({ id: this.lastID, name, points: 0 });
});
});
// Add points
app.post('/users/:id/add', (req, res) => {
const { id } = req.params;
const { points } = req.body;
db.run(`UPDATE users SET points = points + ? WHERE id = ?`, [points, id], function (err) {
if (err) return res.status(500).json({ error: err.message });
res.json({ message: "Points added!" });
});
});
// Get user info
app.get('/users/:id', (req, res) => {
const { id } = req.params;
db.get(`SELECT * FROM users WHERE id = ?`, [id], (err, row) => {
if (err) return res.status(500).json({ error: err.message });
if (!row) return res.status(404).json({ error: "User not found" });
res.json(row);
});
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
Inside public/index.html, create a form to add users or view points:
<!DOCTYPE html>
<html>
<body>
<h1>Loyalty Program</h1>
<input id="name" placeholder="User name" />
<button onclick="addUser()">Add User</button>
<hr>
<input id="userId" placeholder="User ID">
<input id="addPoints" type="number" placeholder="Points to add">
<button onclick="addPoints()">Add Points</button>
<pre id="result"></pre>
<script>
async function addUser() {
const name = document.getElementById('name').value;
const res = await fetch('/users', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ name })
});
const data = await res.json();
document.getElementById('result').textContent = JSON.stringify(data, null, 2);
}
async function addPoints() {
const id = document.getElementById('userId').value;
const points = parseInt(document.getElementById('addPoints').value);
const res = await fetch(`/users/${id}/add`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({ points })
});
const data = await res.json();
document.getElementById('result').textContent = JSON.stringify(data, null, 2);
}
</script>
</body>
</html>
If you later use an external service (like Supabase or Stripe) for user authentication or reward payments, don’t put the keys in your code. Use Replit’s Secrets tab (🔒 icon on left sidebar) and access them in code like this:
// index.js
const SUPABASE_URL = process.env.SUPABASE_URL;
const SUPABASE_KEY = process.env.SUPABASE_KEY;
Use Replit’s built-in Git integration to version control your loyalty program. You can invite collaborators via the "Invite" button or use multiplayer editing to pair-program in real-time. Before deploying, test connections thoroughly since database paths or ports may differ in deployment environments.
When everything works, click “Deploy” → “Production” to keep your loyalty program always running. Replit will manage hosting, but remember to monitor logs via “Shell” or “Console” for runtime errors. For serious production environments with many users, migrate the database to an external persistent host like Supabase or MongoDB Atlas.
console.log() in DB calls or routes while testing.
This approach stays within Replit’s strengths: fast setup, live collaboration, and working web apps without external servers. As your loyalty program grows, you can refactor or migrate to a bigger environment with the same Express and database logic.
When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.