/how-to-build-replit

How to Build a Finance tracker with Replit

Learn how to build a simple finance tracker with Replit. Follow this step-by-step guide to manage expenses and improve your coding skills efficiently

Matt Graham, CEO of Rapid Developers

Book a call with an Expert

Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.

How to Build a Finance tracker with Replit

A simple but real-world Finance Tracker on Replit can be built as a small full-stack project using Node.js (for backend), Express (for API routes), and SQLite (a lightweight file‑based database that works well on Replit). You’ll build a small web app with an input form for transactions (like expenses or income) and a table to display saved data. Replit hosts the app live, stores secrets like API keys in its Secrets tab, and you don’t need to deploy elsewhere unless you want a custom domain.

 

Step 1: Create and Set Up Your Repl

 

On Replit, create a new Repl using the Node.js template. This gives you an empty environment with a index.js entry point and a working console. It automatically sets up package.json for dependency management.

  • Click Create → Node.js.
  • Rename your Repl to something like finance-tracker.

Then install dependencies in the shell:

npm install express better-sqlite3

Express handles web routes; better-sqlite3 is a stable, synchronous SQLite client for Node.js and works perfectly in Replit.

 

Step 2: Set Up Database

 

Inside the Repl filetree, create a new folder named db and inside it create a file database.js. This will initialize the SQLite database and define a “transactions” table.

// db/database.js
const Database = require('better-sqlite3');
const db = new Database('finance.db'); // creates or opens finance.db file

// Create table if not exists
db.prepare(`
CREATE TABLE IF NOT EXISTS transactions (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  description TEXT NOT NULL,
  amount REAL NOT NULL,
  date TEXT DEFAULT CURRENT_TIMESTAMP
)
`).run();

module.exports = db;

This script ensures that the first run creates finance.db file at the root directory of the Repl.

 

Step 3: Create Express Server

 

In your main index.js file (already present by default), set up a simple Express app. This will serve the static frontend and expose API routes to add or list transactions.

// index.js
const express = require('express');
const db = require('./db/database');
const app = express();

app.use(express.json());
app.use(express.static('public')); // serve files from public folder

// API route to add transaction
app.post('/api/transactions', (req, res) => {
  const { description, amount } = req.body;
  if (!description || !amount) {
    return res.status(400).json({ error: 'Missing description or amount' });
  }
  const stmt = db.prepare('INSERT INTO transactions (description, amount) VALUES (?, ?)');
  stmt.run(description, amount);
  res.json({ success: true });
});

// API route to get all transactions
app.get('/api/transactions', (req, res) => {
  const rows = db.prepare('SELECT * FROM transactions ORDER BY date DESC').all();
  res.json(rows);
});

const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Server running on port ${port}`));

Every call to /api/transactions reads or writes to the database. Express serves the frontend from a folder called public.

 

Step 4: Create Frontend Files

 

Now make a new folder named public at the root of your Repl. Inside it, create three files: index.html, style.css, and script.js. These form your app’s basic frontend.

index.html (this is the user interface):

<!-- public/index.html -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Finance Tracker</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <h1>Finance Tracker</h1>
  <form id="transactionForm">
    <input type="text" id="description" placeholder="Description" required />
    <input type="number" id="amount" step="0.01" placeholder="Amount" required />
    <button type="submit">Add</button>
  </form>

  <table id="transactionsTable">
    <thead>
      <tr><th>Date</th><th>Description</th><th>Amount</th></tr>
    </thead>
    <tbody></tbody>
  </table>

  <script src="script.js"></script>
</body>
</html>

style.css (simple styling):

/* public/style.css */
body {
  font-family: Arial, sans-serif;
  padding: 20px;
}

h1 {
  color: #333;
}

form {
  margin-bottom: 20px;
}

table {
  width: 100%;
  border-collapse: collapse;
}

th, td {
  padding: 8px;
  border-bottom: 1px solid #ccc;
}

td {
  text-align: center;
}

script.js (frontend logic):

// public/script.js
async function loadTransactions() {
  const res = await fetch('/api/transactions');
  const data = await res.json();
  const tbody = document.querySelector('#transactionsTable tbody');
  tbody.innerHTML = '';
  data.forEach(tx => {
    const row = `<tr>
      <td>${new Date(tx.date).toLocaleString()}</td>
      <td>${tx.description}</td>
      <td>${tx.amount.toFixed(2)}</td>
    </tr>`;
    tbody.insertAdjacentHTML('beforeend', row);
  });
}

document.querySelector('#transactionForm').addEventListener('submit', async (e) => {
  e.preventDefault();
  const description = document.querySelector('#description').value;
  const amount = parseFloat(document.querySelector('#amount').value);
  await fetch('/api/transactions', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ description, amount })
  });
  e.target.reset();
  loadTransactions(); // refresh table
});

loadTransactions();

 

Step 5: Run and Test

 

Click the “Run” button in Replit. Replit builds and starts the server. You’ll see “Server running on port 3000” in the console. The webview (the right side of the editor) shows your finance tracker. Try adding a few transactions; the table updates instantly using the API you built.

 

Step 6: Save Data and Collaborate

 

  • The SQLite database file (finance.db) lives inside your Repl filesystem. It’s persistent across sessions unless you delete it.
  • To share or collaborate, click the Invite button at the top of Replit, so your teammate can edit and test in real-time.
  • If you store sensitive info (for example, using a payment API or external key), use the Secrets tab (the lock icon on the left) instead of plain text configuration.

 

Common Pitfalls on Replit

 

  • Never use Replit’s temp folder for SQLite. Only use persistent paths like project root.
  • Use better-sqlite3 instead of async pool-based drivers (they often hang in Replit’s environment).
  • When you restart the Repl, the server port may change, so always use process.env.PORT as shown above.
  • Don’t store large DBs; Replit’s storage per Repl is limited (usually under 500 MB).

 

That’s It

 

Now you have a complete, working finance tracker fully hosted on Replit, using real backend, database, and frontend code. You can enhance it with categories, user authentication (with Replit Auth or JWT), or export reports later, but this foundation already teaches you the key patterns for a genuine production-like project on Replit.

Want to explore opportunities to work with us?

Connect with our team to unlock the full potential of no-code solutions with a no-commitment consultation!

Contact Us

How to Build a Simple Finance Tracker API with Express and Replit Database


<script type="module">
import express from 'express'
import bodyParser from 'body-parser'
import Database from '@replit/database'

const app = express()
const db = new Database()
app.use(bodyParser.json())

// Normalize categories and prevent duplicate entries for same date
function normalizeTransaction(tx) {
  return {
    date: new Date(tx.date).toISOString().split('T')[0],
    amount: Number(tx.amount),
    category: tx.category.trim().toLowerCase(),
    note: tx.note?.trim() || '',
  }
}

app.post('/api/transactions', async (req, res) => {
  const tx = normalizeTransaction(req.body)
  const existing = (await db.get(tx.date)) || []
  const updated = [...existing, tx]
  await db.set(tx.date, updated)
  res.json({ success: true, transactions: updated })
})

app.get('/api/summary/:month', async (req, res) => {
  const data = await db.list()
  const entries = Object.entries(data)
  const month = req.params.month
  const summary = entries.reduce((acc, [date, txs]) => {
    if (date.startsWith(month)) {
      txs.forEach(tx => {
        acc[tx.category] = (acc[tx.category] || 0) + tx.amount
      })
    }
    return acc
  }, {})
  res.json(summary)
})

app.listen(3000, () => console.log('Finance tracker API running on port 3000'))
</script>

How to Add a Live Currency Conversion API to Your Finance Tracker


<script type="module">
import express from 'express'
import fetch from 'node-fetch'

const app = express()
app.use(express.json())

// Endpoint to fetch live exchange rate and convert USD to target currency
app.get('/api/convert', async (req, res) => {
  const { amount, to } = req.query
  if (!amount || !to) return res.status(400).json({ error: 'Missing parameters' })

  try {
    const response = await fetch(`https://api.exchangerate.host/latest?base=USD&symbols=${to.toUpperCase()}`)
    const data = await response.json()
    const rate = data.rates[to.toUpperCase()]
    if (!rate) return res.status(404).json({ error: 'Currency not found' })

    const converted = Number(amount) \* rate
    res.json({ from: 'USD', to, rate, original: Number(amount), converted })
  } catch (err) {
    console.error('Conversion error:', err)
    res.status(500).json({ error: 'Failed to fetch exchange rate' })
  }
})

app.listen(3000, () => console.log('Currency conversion service running on port 3000'))
</script>

How to Add Secure User Authentication to Your Replit Finance Tracker


<script type="module">
import express from 'express'
import Database from '@replit/database'
import bcrypt from 'bcrypt'

const app = express()
const db = new Database()
app.use(express.json())

// Secure user signup + login with hashed password in Replit DB
app.post('/api/signup', async (req, res) => {
  const { username, password } = req.body
  if (!username || !password) return res.status(400).json({ error: 'Missing fields' })

  const existing = await db.get(`user:${username}`)
  if (existing) return res.status(400).json({ error: 'User already exists' })

  const hash = await bcrypt.hash(password, 10)
  await db.set(`user:${username}`, { username, hash })
  res.json({ success: true })
})

app.post('/api/login', async (req, res) => {
  const { username, password } = req.body
  const user = await db.get(`user:${username}`)
  if (!user) return res.status(401).json({ error: 'Invalid credentials' })

  const valid = await bcrypt.compare(password, user.hash)
  if (!valid) return res.status(401).json({ error: 'Invalid credentials' })

  // Example: store session in DB (temporary / simplified)
  const sessionToken = crypto.randomUUID()
  await db.set(`session:${sessionToken}`, username)
  res.json({ success: true, token: sessionToken })
})

app.listen(3000, () => console.log('Auth service ready on port 3000'))
</script>

Want to explore opportunities to work with us?

Connect with our team to unlock the full potential of no-code solutions with a no-commitment consultation!

Contact Us
Matt Graham, CEO of Rapid Developers

Book a call with an Expert

Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.

Best Practices for Building a Finance tracker with Replit

When building a Finance Tracker on Replit, treat it like a real full‑stack web app — keep backend, frontend, and secrets organized. Use Replit’s built‑in Node.js or Python templates, store sensitive data in Secrets (never in code), and use a small hosted database like Replit DB or an external database. Always remember that Replit’s file system resets on some deployments, so persist all data outside local files. Use the Replit shell for installing dependencies, the built‑in Git integration for version control, and the “Always On” or “Deployments” features to keep your tracker running reliably.

 

Project Setup

 

Create a new Repl with the Node.js template (good for combining backend and frontend easily). After it opens, your main files will be:

  • index.js — your backend logic
  • package.json — where dependencies are listed
  • public/ — create this folder for static assets (HTML, CSS, JS)
  • public/index.html — your frontend interface

 

// Inside the Replit shell, install Express (backend server) and Replit DB
npm install express @replit/database

 

Backend (index.js)

 

Your backend handles routes (like saving or fetching expenses). Add the code below inside index.js. This creates a simple Express server connected to Replit DB.

 

// Import required packages
const express = require("express");
const Database = require("@replit/database");

// Initialize
const app = express();
const db = new Database();

app.use(express.json()); // Parse incoming JSON data

// Route: add a new expense
app.post("/add-expense", async (req, res) => {
  const { description, amount } = req.body;
  const id = Date.now(); // unique key
  await db.set(id, { description, amount });
  res.json({ success: true });
});

// Route: get all expenses
app.get("/expenses", async (req, res) => {
  const keys = await db.list();
  const expenses = [];
  for (const key of keys) {
    const item = await db.get(key);
    expenses.push({ id: key, ...item });
  }
  res.json(expenses);
});

// Starting the server
app.listen(3000, () => console.log("Server running on port 3000"));

 

This backend script stays in the root index.js file. Replit automatically runs it when you press the “Run” button. When it says “Server running,” the mini webserver is active.

 

Frontend (public/index.html)

 

Create a public/index.html file that allows a user to add and view expenses. Include simple forms that send requests to the backend routes we defined before.

 

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Finance Tracker</title>
</head>
<body>
  <h1>My Finance Tracker</h1>

  <form id="expense-form">
    <input type="text" id="desc" placeholder="Description" required />
    <input type="number" id="amount" placeholder="Amount" required />
    <button type="submit">Add Expense</button>
  </form>

  <h3>Expenses</h3>
  <ul id="expense-list"></ul>

  <script>
    // Load expenses from server
    async function loadExpenses() {
      const res = await fetch("/expenses");
      const data = await res.json();
      const list = document.getElementById("expense-list");
      list.innerHTML = "";
      data.forEach(e => {
        const li = document.createElement("li");
        li.textContent = `${e.description}: $${e.amount}`;
        list.appendChild(li);
      });
    }

    // Handle form submit
    document.getElementById("expense-form").addEventListener("submit", async (ev) => {
      ev.preventDefault();
      const description = document.getElementById("desc").value;
      const amount = parseFloat(document.getElementById("amount").value);
      await fetch("/add-expense", {
        method: "POST",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify({ description, amount })
      });
      loadExpenses();
    });

    loadExpenses();
  </script>
</body>
</html>

 

Serving the Frontend

 

To serve the frontend, modify your index.js file to include a static route right before your routes:

 

app.use(express.static("public"));

 

This tells Express to serve your HTML and JavaScript files from the public folder. After saving, click “Run.” Your Replit URL will now show the finance tracker’s web page.

 

Using Secrets & Environment Variables

 

If later you connect to an external API or database (like MongoDB), do not hardcode credentials. In Replit, click the Secrets tab (lock icon on the left) and add them there. Access them in your code using:

 

const mongoURI = process.env.MONGO_URI;

 

Persistence and Deployment Tips

 

  • Never depend on local .json files for data persistence; use a database like Replit DB, SQLite, or external services (PlanetScale, Supabase, etc.).
  • Use Replit “Deployments” instead of running your Repl forever; it ensures uptime and less risk of resets.
  • Commit often through Replit’s Git integration to avoid losing work.
  • Collaborate by inviting teammates via the top‑right “Invite” button; they can edit together in real time.

 

Common Pitfalls and Fixes

 

  • Do not upload node\_modules — Replit installs them from package.json automatically.
  • If frontend fetches fail with CORS errors, keep your API and frontend on the same Repl, or use relative URLs like "/expenses" instead of full URLs.
  • If output doesn’t update, check the “Console” tab for logs; Replit logs live there in real time.

 

Following this approach gives you a real, working finance tracker app fully hosted and runnable on Replit, with correct file structure, persistent data, and maintainable code separation.

Client trust and success are our top priorities

When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.

Rapid Dev was an exceptional project management organization and the best development collaborators I've had the pleasure of working with. They do complex work on extremely fast timelines and effectively manage the testing and pre-launch process to deliver the best possible product. I'm extremely impressed with their execution ability.

CPO, Praction - Arkady Sokolov

May 2, 2023

Working with Matt was comparable to having another co-founder on the team, but without the commitment or cost. He has a strategic mindset and willing to change the scope of the project in real time based on the needs of the client. A true strategic thought partner!

Co-Founder, Arc - Donald Muir

Dec 27, 2022

Rapid Dev are 10/10, excellent communicators - the best I've ever encountered in the tech dev space. They always go the extra mile, they genuinely care, they respond quickly, they're flexible, adaptable and their enthusiasm is amazing.

Co-CEO, Grantify - Mat Westergreen-Thorne

Oct 15, 2022

Rapid Dev is an excellent developer for no-code and low-code solutions.
We’ve had great success since launching the platform in November 2023. In a few months, we’ve gained over 1,000 new active users. We’ve also secured several dozen bookings on the platform and seen about 70% new user month-over-month growth since the launch.

Co-Founder, Church Real Estate Marketplace - Emmanuel Brown

May 1, 2024 

Matt’s dedication to executing our vision and his commitment to the project deadline were impressive. 
This was such a specific project, and Matt really delivered. We worked with a really fast turnaround, and he always delivered. The site was a perfect prop for us!

Production Manager, Media Production Company - Samantha Fekete

Sep 23, 2022