Learn how to build a simple, efficient insurance claims tool using Replit. Follow this step-by-step guide to streamline development and boost productivity.

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
To build an insurance claims tool on Replit, create a simple Node.js + Express + SQLite app with a React frontend. The backend will handle claim storage and APIs, and the frontend will let users submit claims and review their statuses. Replit handles both client and server in one project easily. You’ll use the “Node.js” template as the base, add a frontend folder for React, store your database safely in a database file, and keep secrets like API keys in Replit’s “Secrets” tab. This step-by-step structure actually works in Replit’s hosted environment and is durable enough for demonstration or small-scale use.
Create a New Repl: choose “Node.js”. Inside this Repl, you’ll have a default index.js file. That file will run your backend server.
Then, create a folder named client. This will hold your React app. Replit can run both, but the Node server will serve API requests, and React will handle the UI (you can preview through the main URL Replit provides).
// In the Shell tab on Replit:
npm init -y
npm install express sqlite3 cors
npx create-react-app client
In your main index.js file (that Replit created automatically):
// index.js
const express = require('express')
const sqlite3 = require('sqlite3').verbose()
const cors = require('cors')
const app = express()
app.use(express.json())
app.use(cors())
// Create or open database file
const db = new sqlite3.Database('./database.db')
// Create claims table once
db.run('CREATE TABLE IF NOT EXISTS claims (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, policyNumber TEXT, description TEXT, status TEXT)')
// Endpoint to create new claim
app.post('/api/claims', (req, res) => {
const { name, policyNumber, description } = req.body
const status = 'Pending'
db.run('INSERT INTO claims (name, policyNumber, description, status) VALUES (?, ?, ?, ?)', [name, policyNumber, description, status], function(err) {
if (err) return res.status(500).json({ error: err.message })
res.json({ id: this.lastID, name, policyNumber, description, status })
})
})
// Endpoint to list all claims
app.get('/api/claims', (req, res) => {
db.all('SELECT * FROM claims', [], (err, rows) => {
if (err) return res.status(500).json({ error: err.message })
res.json(rows)
})
})
// Endpoint to update claim status
app.put('/api/claims/:id', (req, res) => {
const { status } = req.body
db.run('UPDATE claims SET status=? WHERE id=?', [status, req.params.id], function(err) {
if (err) return res.status(500).json({ error: err.message })
res.json({ changed: this.changes })
})
})
// Start server
const PORT = process.env.PORT || 3000
app.listen(PORT, () => console.log(`Server running on port ${PORT}`))
Make sure your package.json file includes a start command (Replit uses this to run your Repl). Open package.json and add:
"scripts": {
"start": "node index.js"
}
Navigate manually to client/src in your Replit file tree. Replace the contents of App.js with something basic that connects to your backend API.
// client/src/App.js
import React, { useState, useEffect } from 'react'
function App() {
const [claims, setClaims] = useState([])
const [form, setForm] = useState({ name: '', policyNumber: '', description: '' })
useEffect(() => {
fetch('http://localhost:3000/api/claims')
.then(res => res.json())
.then(setClaims)
}, [])
const handleSubmit = async (e) => {
e.preventDefault()
const res = await fetch('http://localhost:3000/api/claims', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(form)
})
const newClaim = await res.json()
setClaims([...claims, newClaim])
}
return (
<div>
<h2>Insurance Claims Tool</h2>
<form onSubmit={handleSubmit}>
<input placeholder="Name" onChange={e => setForm({ ...form, name: e.target.value })} />
<input placeholder="Policy Number" onChange={e => setForm({ ...form, policyNumber: e.target.value })} />
<textarea placeholder="Description" onChange={e => setForm({ ...form, description: e.target.value })} />
<button type="submit">Submit Claim</button>
</form>
<h3>Submitted Claims</h3>
<ul>
{claims.map(c => (
<li key={c.id}>{c.name} – {c.policyNumber} – {c.status}</li>
))}
</ul>
</div>
)
}
export default App
Replit always runs one process by default. So to run both React and your API server, the practical way is to run the backend (so `index.js` keeps live for database route access) and use the React development server only during editing. In the Shell:
cd client
npm start
You might get a prompt saying the port is already in use because Replit uses port 3000 for the Node server. Change your React app to run on a different port by editing client/package.json and adding this line under scripts:
"start": "PORT=3001 react-scripts start"
That way, your API stays available at http://localhost:3000/api/claims and your frontend locally at http://localhost:3001. In production or when showing the tool, you can instead build the React app and serve it directly from Express by adding inside index.js (below your existing routes):
// Add this only after building your React app with: cd client && npm run build
const path = require('path')
app.use(express.static(path.join(__dirname, 'client/build')))
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'client/build', 'index.html'))
})
SQLite works well for small tools because it stores all data in database.db. Replit persists that file between runs. If you ever add external APIs (SMS, email, etc.), use Replit’s Secrets panel on the left sidebar — never store tokens directly in code, always reference them with process.env.MY_SECRET_KEY.
In Replit, click the Deploy button on top right → choose “Static” if you only need frontend, or “Always On” if you need backend running continuously. Always test by opening the web preview tab and submitting a new claim to verify that the form talks to your API correctly.
This approach is the most practical way to build and demo a small insurance claims tool using Replit — it keeps the structure simple, uses features Replit naturally supports, and avoids common pitfalls like conflicting ports, lost database files, or exposing secrets.
import express from "express";
import multer from "multer";
import { v4 as uuidv4 } from "uuid";
import Database from "@replit/database";
const db = new Database();
const app = express();
const upload = multer({ dest: "uploads/" });
app.use(express.json());
app.post("/api/claims", upload.single("evidence"), async (req, res) => {
try {
const { policyId, claimantName, claimType, description, amount } = req.body;
if (!policyId || !claimantName || !claimType || !amount) {
return res.status(400).json({ error: "Missing required fields" });
}
const claimId = uuidv4();
const claimData = {
claimId,
policyId,
claimantName,
claimType,
description: description || "",
amount: parseFloat(amount),
filePath: req.file ? req.file.path : null,
status: "pending",
createdAt: new Date().toISOString()
};
await db.set(`claim:${claimId}`, claimData);
res.status(201).json({ message: "Claim submitted successfully", claimId });
} catch (err) {
console.error("Error saving claim:", err);
res.status(500).json({ error: "Server error while submitting claim" });
}
});
app.get("/api/claims/:id", async (req, res) => {
const claim = await db.get(`claim:${req.params.id}`);
if (!claim) return res.status(404).json({ error: "Claim not found" });
res.json(claim);
});
app.listen(3000, () => {
console.log("Insurance claim service running on port 3000");
});
import express from "express";
import crypto from "crypto";
import Database from "@replit/database";
const app = express();
const db = new Database();
app.use(express.json());
// Verify webhook signatures from external insurance API
function verifySignature(req, secret) {
const signature = req.headers["x-insurance-signature"];
const payload = JSON.stringify(req.body);
const expected = crypto.createHmac("sha256", secret).update(payload).digest("hex");
return signature === expected;
}
// This route listens for external claim status updates (fraud detection, auto-adjustment, etc.)
app.post("/webhook/claim-status", async (req, res) => {
const secret = process.env.INSURANCE_WEBHOOK_SECRET;
if (!verifySignature(req, secret)) return res.status(403).json({ error: "Invalid signature" });
const { claimId, newStatus, reason } = req.body;
if (!claimId || !newStatus) return res.status(400).json({ error: "Invalid payload" });
const claimKey = `claim:${claimId}`;
const claim = await db.get(claimKey);
if (!claim) return res.status(404).json({ error: "Claim not found" });
claim.status = newStatus;
claim.statusReason = reason || null;
claim.updatedAt = new Date().toISOString();
await db.set(claimKey, claim);
res.json({ message: "Claim status updated", claimId, newStatus });
});
app.listen(3001, () => {
console.log("Webhook listener active on port 3001");
});
import fetch from "node-fetch";
import Database from "@replit/database";
const db = new Database();
async function runFraudCheck() {
const allClaims = await db.list("claim:");
for (const key of allClaims) {
const claim = await db.get(key);
if (claim.status === "pending") {
try {
const response = await fetch("https://external-fraud-api.example.com/check", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${process.env.FRAUD_API_KEY}`
},
body: JSON.stringify({ claimId: claim.claimId, amount: claim.amount, claimant: claim.claimantName })
});
const result = await response.json();
if (result.flagged) {
claim.status = "review";
claim.statusReason = "Potential fraud detected";
} else {
claim.status = "approved";
}
claim.updatedAt = new Date().toISOString();
await db.set(key, claim);
} catch (err) {
console.error("Fraud check failed for claim:", claim.claimId, err);
}
}
}
}
runFraudCheck();

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
The best practice for building an insurance claims tool on Replit is to structure your project clearly — separate backend and frontend code, handle secrets properly using Replit’s Secrets tab, and use persistent storage like Replit’s built-in Database or an external one via environment variables. Build your backend (for example, using Node.js + Express or Python + Flask/FastAPI) in a file such as server.js or main.py, build your frontend (React, or a simple HTML/CSS/JS interface) in a separate folder, and connect them through defined APIs. Replit supports deployments, so you can use its “Deployments” feature for persistent uptime and easier sharing.
Use a clear folder structure to stay organized:
Create a file named backend/server.js inside your project. This is where your API logic lives.
// backend/server.js
import express from "express"
import cors from "cors"
import bodyParser from "body-parser"
const app = express()
app.use(cors())
app.use(bodyParser.json())
// Example endpoint: claim submission
app.post("/api/claims", (req, res) => {
const { policyNumber, incidentDescription, claimAmount } = req.body
if (!policyNumber || !claimAmount) {
return res.status(400).json({ message: "Missing required fields" })
}
// In real projects, you'd save this to a persistent database
console.log("Received claim:", req.body)
res.status(201).json({ success: true, message: "Claim submitted successfully" })
})
// Example endpoint: list all claims
app.get("/api/claims", (req, res) => {
// Replace with database fetch
res.json([{ policyNumber: "ABC123", status: "processing" }])
})
app.listen(3000, () => {
console.log("Server running on port 3000")
})
Where to put it: Inside backend/server.js. Replit will run it as the main entry point if you set the Run command to node backend/server.js in the “Shell” or “Run” settings.
If you want a simple frontend for submitting claims, create frontend/index.html with a basic form that hits your backend API.
<!-- frontend/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>Insurance Claim Form</title>
</head>
<body>
<h2>Submit Your Claim</h2>
<form id="claimForm">
<input type="text" name="policyNumber" placeholder="Policy Number" required />
<input type="text" name="incidentDescription" placeholder="Describe incident" required />
<input type="number" name="claimAmount" placeholder="Claim Amount" required />
<button type="submit">Submit</button>
</form>
<p id="response"></p>
<script>
const form = document.getElementById("claimForm")
form.addEventListener("submit", async (e) => {
e.preventDefault()
const data = {
policyNumber: form.policyNumber.value,
incidentDescription: form.incidentDescription.value,
claimAmount: form.claimAmount.value,
}
// Replit's backend runs on localhost:3000 while previewing
const res = await fetch("/api/claims", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
})
const result = await res.json()
document.getElementById("response").innerText = result.message
})
</script>
</body>
</html>
Where to put it: Inside frontend/index.html. When you preview your app in Replit, open this file or configure Replit to serve frontend via static middleware if you want to combine the two layers.
If you prefer a single app (no separate frontend develop command), serve the HTML file right from Express. Add this inside your server.js before your app.listen line:
// Serve static files
import path from "path"
import { fileURLToPath } from "url"
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
app.use(express.static(path.join(__dirname, "../frontend")))
Now, visiting the Replit preview URL will show your form served by the same backend.
Never hardcode keys or database credentials. Go to Replit’s sidebar → click the 🔒 Secrets icon → add items like DB_URI, ADMIN_TOKEN. Access them in Node like this:
const dbUri = process.env.DB_URI
This ensures sensitive information isn’t visible in your code or public Repls.
For production-level claims data, you can connect to MongoDB Atlas (which has a free tier). You’ll store your database link as a secret and use Mongoose:
import mongoose from "mongoose"
mongoose.connect(process.env.DB_URI)
.then(() => console.log("Database connected"))
.catch(err => console.error(err))
// Example model
const Claim = mongoose.model("Claim", new mongoose.Schema({
policyNumber: String,
incidentDescription: String,
claimAmount: Number,
status: { type: String, default: "submitted" }
}))
// Inside POST endpoint, replace console.log with save:
const claim = new Claim(req.body)
await claim.save()
res.status(201).json({ success: true })
Once things run correctly, open the Deployments tab on the left sidebar in Replit, select Always On or Static deployment (depending on your plan). This keeps your insurance tool live, so users can submit claims even when you’re not online.
process.env.PORT || 3000 if hosting externally.
By structuring your Replit project this way — clear separation, secure secrets, and stable endpoints — you create a maintainable, deployable, and realistic insurance claims tool that behaves predictably even beyond demo level.
When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.