/how-to-build-replit

How to Build a Directory service with Replit

Learn how to build a directory service using Replit. Follow easy steps to create, host, and manage your own powerful online directory.

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 Directory service with Replit

If you want to build a simple directory service (like a phone book, employee list, or contacts directory) inside Replit, the cleanest approach is to use a small Node.js + Express backend with a lightweight SQLite database or JSON file for storage. Replit runs Node apps very well and gives you a built-in web server environment that’s always live when you click “Run.” You can start with the Replit Node.js template and then add your backend routes, database connection, and a small frontend page to display or manage your directory.

 

Project Setup in Replit

 

Start a new Repl using the “Node.js” template. You’ll see a index.js file and a package.json. The index.js file is your main entry point (this is what Replit runs automatically).

Open the Shell tab (bottom pane) and install Express and SQLite:

npm install express sqlite3

 

Basic Server Setup

 

In your existing index.js file, replace any sample code with this. This sets up an Express server and connects to SQLite to hold your directory data:

// index.js
const express = require('express')
const sqlite3 = require('sqlite3').verbose()
const path = require('path')
const app = express()

// Middleware to parse JSON data from requests
app.use(express.json())

// Initialize database file inside repl folder
const db = new sqlite3.Database('./directory.db')

// Create table if not exists
db.run(`CREATE TABLE IF NOT EXISTS people (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT NOT NULL,
  phone TEXT NOT NULL,
  email TEXT
)`)

// Serve static files (frontend) from "public"
app.use(express.static(path.join(__dirname, 'public')))

// Route to get all directory entries
app.get('/api/people', (req, res) => {
  db.all('SELECT * FROM people', [], (err, rows) => {
    if (err) return res.status(500).send(err)
    res.json(rows)
  })
})

// Route to add a new entry
app.post('/api/people', (req, res) => {
  const { name, phone, email } = req.body
  if (!name || !phone) return res.status(400).send('Name and phone required')
  db.run(`INSERT INTO people (name, phone, email) VALUES (?, ?, ?)`, [name, phone, email], function (err) {
    if (err) return res.status(500).send(err)
    res.status(201).json({ id: this.lastID, name, phone, email })
  })
})

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

This file both handles backend routes and serves the frontend from a folder we’ll add next.

 

Creating the Frontend

 

In your Replit sidebar, click “New Folder” → name it public. Inside that folder, click “New File” → name it index.html. Replit will automatically serve it because of Express’ static middleware above.

<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Directory Service</title>
</head>
<body>
  <h1>Directory</h1>
  
  <div id="directory"></div>

  <form id="addForm">
    <input name="name" placeholder="Name" required />
    <input name="phone" placeholder="Phone" required />
    <input name="email" placeholder="Email" />
    <button type="submit">Add</button>
  </form>

  <script>
    async function loadDirectory() {
      const res = await fetch('/api/people')
      const data = await res.json()
      document.getElementById('directory').innerHTML = 
        data.map(p => `<div><strong>${p.name}</strong> - ${p.phone} (${p.email || ''})</div>`).join('')
    }

    document.getElementById('addForm').onsubmit = async (e) => {
      e.preventDefault()
      const form = e.target
      const newPerson = {
        name: form.name.value,
        phone: form.phone.value,
        email: form.email.value
      }
      await fetch('/api/people', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(newPerson)
      })
      form.reset()
      loadDirectory()
    }

    loadDirectory() // initial load
  </script>
</body>
</html>

Now when you click “Run” in Replit, Replit will start your Express server and open a webview tab with the directory UI.

 

Working with Secrets and Database in Replit

 

  • If you plan to store sensitive data (like admin passwords or API keys), use Replit’s Secrets tab (🔐 icon), and then access them with process.env.MY\_SECRET in your code. Don’t hardcode secrets inside code files.
  • Your SQLite database file directory.db will persist between runs, but not through full clone resets. So back it up or store in GitHub if needed.
  • Replit’s filesystem is persistent per repl, but temporary for “multiplayer” previews if running multiple copies, so careful when deleting files.

 

Deploying and Sharing

 

  • Press “Run” → Replit will show a URL like https://yourname.yourreplname.repl.co.
  • That URL is public and updates as soon as you “Run” again.
  • If you want a stable hosted version, open the “Deployments” panel on the left, pick “Always On” or “Static” (for Node it’ll be “Always On”), then deploy.

 

Common Pitfalls

 

  • Don’t forget to call express.json() before using req.body. Without it, req.body will be undefined.
  • Make sure your SQLite DB file path uses ./ so it saves inside Replit’s project folder (not root).
  • You can collaborate in “multiplayer,” but note that multiple people editing can cause server restarts — so save frequently.
  • Each run resets the server process; ongoing connections are lost after reload — so treat Replit as a preview environment, not a full production host.

 

This setup gives you a working small-scale directory service in Replit: an Express backend, SQLite data store, and simple HTML frontend all running together in one Repl. It’s fast to iterate, easy to understand, and 100% real.

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 Create a Simple Directory API with Express and Replit Database


const express = require("express");
const Database = require("@replit/database");
const db = new Database();
const app = express();

app.use(express.json());

// Normalize directory entry data
function normalizeEntry(entry) {
  return {
    id: entry.id || Date.now().toString(),
    name: entry.name.trim(),
    category: entry.category?.toLowerCase() || "general",
    contact: {
      email: entry.contact?.email || "",
      phone: entry.contact?.phone || ""
    },
    meta: {
      createdAt: entry.meta?.createdAt || new Date().toISOString(),
      updatedAt: new Date().toISOString()
    }
  };
}

// Create or update directory entry
app.post("/api/directory", async (req, res) => {
  const entry = normalizeEntry(req.body);
  await db.set(entry.id, entry);
  res.json({ success: true, data: entry });
});

// Filtered directory listing
app.get("/api/directory", async (req, res) => {
  const category = req.query.category?.toLowerCase();
  const keys = await db.list();
  const entries = await Promise.all(keys.map(k => db.get(k)));

  const filtered = category
    ? entries.filter(e => e.category === category)
    : entries;

  res.json({ count: filtered.length, data: filtered });
});

app.listen(3000, () => console.log("Directory API running on port 3000"));

How to Add Geocoding and Nearby Search to Your Replit Directory Service


const express = require("express");
const axios = require("axios");
const Database = require("@replit/database");
const db = new Database();

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

// Example: Enrich a directory entry's location using external geocoding API before saving
app.post("/api/directory/location", async (req, res) => {
  try {
    const { name, address } = req.body;
    if (!address) return res.status(400).json({ error: "Address is required" });

    // Avoid exposing secrets in client code, use Replit Secrets instead
    const GEOCODE_API = process.env.GEOCODE_API\_KEY;

    const geoResp = await axios.get("https://maps.googleapis.com/maps/api/geocode/json", {
      params: { address, key: GEOCODE\_API }
    });

    if (!geoResp.data.results.length)
      return res.status(404).json({ error: "Address not found" });

    const locData = geoResp.data.results[0].geometry.location;
    const entry = {
      id: Date.now().toString(),
      name,
      address,
      coordinates: locData,
      createdAt: new Date().toISOString()
    };

    await db.set(entry.id, entry);
    res.json({ success: true, data: entry });
  } catch (err) {
    console.error("Error saving entry:", err.message);
    res.status(500).json({ error: "Server error" });
  }
});

// Example route to retrieve entries within distance (basic filter)
app.get("/api/directory/nearby", async (req, res) => {
  const { lat, lng, range = 10 } = req.query;
  if (!lat || !lng) return res.status(400).json({ error: "lat and lng required" });
  const entries = await Promise.all((await db.list()).map(k => db.get(k)));
  const toRad = deg => deg \* (Math.PI / 180);
  const nearby = entries.filter(e => {
    if (!e.coordinates) return false;
    const R = 6371; // km
    const dLat = toRad(e.coordinates.lat - lat);
    const dLng = toRad(e.coordinates.lng - lng);
    const a =
      Math.sin(dLat / 2) \*\* 2 +
      Math.cos(toRad(lat)) _ Math.cos(toRad(e.coordinates.lat)) _ Math.sin(dLng / 2) \*\* 2;
    const c = 2 \* Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    const distance = R \* c;
    return distance <= range;
  });
  res.json({ count: nearby.length, data: nearby });
});

app.listen(3000, () => console.log("Directory API with geocoding running"));

How to Add an Automated Cleanup API to Your Replit Directory Service


const express = require("express");
const Database = require("@replit/database");
const db = new Database();
const app = express();

app.use(express.json());

// Automatically remove stale directory entries older than N days (housekeeping route)
async function purgeStaleEntries(days) {
  const cutoff = Date.now() - days _ 24 _ 60 _ 60 _ 1000;
  const keys = await db.list();
  let removed = 0;

  for (const key of keys) {
    const entry = await db.get(key);
    if (entry?.meta?.updatedAt && new Date(entry.meta.updatedAt).getTime() < cutoff) {
      await db.delete(key);
      removed++;
    }
  }

  return removed;
}

// Trigger purge via cron or manual HTTP call (e.g., Replit scheduler)
app.post("/api/admin/purge", async (req, res) => {
  const { days = 30, secret } = req.body;
  if (secret !== process.env.ADMIN\_SECRET)
    return res.status(403).json({ error: "Invalid admin secret" });

  const removed = await purgeStaleEntries(days);
  res.json({ success: true, removed });
});

app.listen(3000, () => console.log("Directory cleanup API active"));

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 Directory service with Replit

A good, reliable way to build a Directory Service on Replit is to use Node.js with Express for your backend API, Replit’s built-in database (Replit DB) or an external database for storing entries, and, if needed, a small front-end (HTML/React) to display and interact with the directory. Keep your data access logic and server code separate for clarity, and store any sensitive data (API keys, DB URLs) in the Replit “Secrets” tab. Always be careful not to treat Replit like a full local server—its filesystem resets on rebuilds and public Repls expose ports. Use deployment tools and environment variables properly.

 

1. Setting up your Project Structure

 

Create the following structure directly inside your Replit project (you can do this from the Files sidebar):

  • index.js — main server entry point
  • data/ — a folder to hold helper modules or mock data
  • routes/ — for organizing routes like directory APIs
  • views/ — if you plan simple HTML views

 

.
├── index.js
├── routes/
│   └── directory.js
├── data/
│   └── db.js
├── views/
│   └── index.html
└── package.json

 

2. Installing Dependencies

 

In the Replit Shell (bottom pane), run:

npm init -y
npm install express

 

(Optionally add node-fetch or replit-database if you want storage)

npm install replit-database

 

3. Create the Server (index.js)

 

This is your main service file. You can write this in index.js located in your root directory:

// index.js
const express = require("express")
const app = express()
const directoryRoutes = require("./routes/directory")

app.use(express.json()) // to parse JSON requests

// attach route to /api/directory
app.use("/api/directory", directoryRoutes)

// basic homepage route
app.get("/", (req, res) => {
  res.send("Directory service is running.")
})

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

 

4. Directory Route (routes/directory.js)

 

This file holds endpoints for your directory entries. Create routes/directory.js:

// routes/directory.js
const express = require("express")
const router = express.Router() 
const db = require("../data/db")

// Get all contacts
router.get("/", async (req, res) => {
  const entries = await db.getAll()
  res.json(entries)
})

// Add a new contact
router.post("/", async (req, res) => {
  const { name, email } = req.body
  if (!name || !email) {
    return res.status(400).json({ message: "Name and email required" })
  }

  const newEntry = await db.addEntry(name, email)
  res.status(201).json(newEntry)
})

module.exports = router

 

5. Simple Database Logic (data/db.js)

 

If you don’t want to use external storage, the Replit Database is fine for small projects. Create data/db.js:

// data/db.js
const Database = require("@replit/database")
const db = new Database()

// Return all entries
async function getAll() {
  const keys = await db.list()
  const entries = []
  for (let key of keys) {
    const value = await db.get(key)
    entries.push(value)
  }
  return entries
}

// Add one entry
async function addEntry(name, email) {
  const id = Date.now().toString()
  const item = { id, name, email }
  await db.set(id, item)
  return item
}

module.exports = { getAll, addEntry }

 

6. Adding Secrets (Replit Secrets tab)

 

If you were connecting to a real database service (like MongoDB Atlas), you’d never hardcode the credentials. Open the “🔑 Secrets” panel on the left of your Replit and add variables (like DB_URI). Then you can use it as process.env.DB_URI in your server code.

 

7. Testing the API

 

Click “Run” on top of Replit. Then open the URL Replit gives you — something like https://yourproject.username.repl.co.

You can test the API with the built-in Replit Webview or using curl commands:

curl -X POST -H "Content-Type: application/json" \
-d '{"name": "Alice", "email": "[email protected]"}' \
https://yourproject.username.repl.co/api/directory

 

8. Best Practices & Tips for Replit

 

  • Persist data safely: Use replit-database for persistence; Replit file changes outside of it can vanish on rebuilds.
  • Use .env Secrets: Never store tokens or credentials in project files.
  • Test in the Shell: Use the Replit Shell to run lint or curl commands; it’s faster than relying on the webview.
  • Use multiplayer responsibly: when collaborating, lock important files or copy before major edits.
  • Deploy with Replit Deployments: once stable, click “Deploy” to get persistent hosting.
  • Commit to GitHub: use the “Version Control” tab; Replit syncs perfectly with GitHub for backup.

 

9. Optional Frontend (views/index.html)

 

If you want a minimal webpage to show your directory, create views/index.html and serve it by adjusting your route. You can use vanilla HTML or React depending on complexity.

<!DOCTYPE html>
<html>
  <head>
    <title>Directory Service</title>
  </head>
  <body>
    <h1>Directory</h1>
    <div id="directory"></div>
    <script>
      fetch('/api/directory')
        .then(response => response.json())
        .then(data => {
          const list = document.getElementById('directory')
          data.forEach(item => {
            const p = document.createElement('p')
            p.textContent = item.name + " - " + item.email
            list.appendChild(p)
          })
        })
    </script>
  </body>
</html>

 

This gives you a functioning, database-backed directory service hosted on Replit. Keep your code modular, make backups, and use Secrets for anything private.

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