/how-to-build-replit

How to Build a Inventory system with Replit

Learn how to build an inventory system using Replit step by step. Perfect guide for beginners to manage products efficiently online.

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 Inventory system with Replit

To build an inventory system on Replit, you can use a simple Node.js + Express backend with a small JSON-based storage or Replit’s built-in Database if you want persistent data across restarts. You’ll have one file for the backend (like index.js), an optional frontend (like public/index.html) if you want a simple interface, and a few helper modules (like db.js to manage your data). In Replit, this setup will run completely in the browser environment and auto-restart on changes. You’ll be able to add, view, edit, and delete inventory items by sending requests from the frontend or using a tool like Postman.

 

Create the Base Project

 

In your Replit, start a new Repl using the Node.js template. That gives you an index.js file automatically.

Open the Shell tab (bottom or right side in Replit) and install Express:

 

npm install express

 

If you want to use Replit’s built-in database, install it too:

 

npm install @replit/database

 

Setup the Backend Server

 

Inside index.js, create a simple Express app. This file will act as your backend API that manages inventory items.

 

// index.js
const express = require("express");
const app = express();
app.use(express.json()); // Needed to read JSON from requests

const PORT = process.env.PORT || 3000;

// For persistent storage, we use Replit DB
const Database = require("@replit/database");
const db = new Database();

// Define a basic route to test the server
app.get("/", (req, res) => {
  res.send("Inventory API is running");
});

 

Define the Inventory Endpoints

 

Add the following routes below your test route inside index.js. These control how inventory data is created, fetched, updated, and deleted.

 

// POST /items - Add a new item
app.post("/items", async (req, res) => {
  const { name, quantity } = req.body;
  if (!name || quantity === undefined) {
    return res.status(400).send("Missing name or quantity");
  }
  await db.set(name, { name, quantity });
  res.send({ message: "Item added", item: { name, quantity } });
});

// GET /items - View all items
app.get("/items", async (req, res) => {
  const keys = await db.list();
  const allItems = [];
  for (const key of keys) {
    const item = await db.get(key);
    allItems.push(item);
  }
  res.send(allItems);
});

// PUT /items/:name - Update existing item
app.put("/items/:name", async (req, res) => {
  const name = req.params.name;
  const existing = await db.get(name);
  if (!existing) return res.status(404).send("Item not found");
  const updated = { ...existing, ...req.body };
  await db.set(name, updated);
  res.send({ message: "Item updated", item: updated });
});

// DELETE /items/:name - Remove an item
app.delete("/items/:name", async (req, res) => {
  const name = req.params.name;
  await db.delete(name);
  res.send({ message: "Item deleted" });
});

 

Finally, at the very bottom of index.js, start the server:

 

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

 

Add a Simple Frontend (Optional)

 

Create a new folder named public in your Repl, and add a file inside it named index.html. This will become your minimal interface to interact with the inventory.

 

<!-- public/index.html -->
<!DOCTYPE html>
<html>
  <head>
    <title>Inventory Manager</title>
  </head>
  <body>
    <h1>Inventory</h1>
    <form id="addForm">
      <input type="text" id="name" placeholder="Item name" required />
      <input type="number" id="quantity" placeholder="Quantity" required />
      <button type="submit">Add Item</button>
    </form>
    <ul id="inventory"></ul>

    <script>
      const form = document.getElementById("addForm");
      const inventoryList = document.getElementById("inventory");

      async function loadItems() {
        const res = await fetch("/items");
        const items = await res.json();
        inventoryList.innerHTML = "";
        items.forEach((item) => {
          const li = document.createElement("li");
          li.textContent = `${item.name} - ${item.quantity}`;
          inventoryList.appendChild(li);
        });
      }

      form.addEventListener("submit", async (e) => {
        e.preventDefault();
        const name = document.getElementById("name").value;
        const quantity = document.getElementById("quantity").value;
        await fetch("/items", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ name, quantity }),
        });
        form.reset();
        loadItems();
      });

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

 

In your index.js, tell Express to serve this folder. Add this line above your routes:

 

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

 

Run, Test, and Debug

 

  • Click the Run button at the top of Replit. This starts your server and gives you a public URL.
  • Open the URL shown in the right preview or click “Open in a new tab”. You’ll see your simple inventory page.
  • Try adding, viewing, and reloading — data should persist, because it’s stored in the Replit Database.

 

Common Pitfalls in Replit

 

  • Don’t store secrets inside your code. If you had an external database or API key, store it in Replit’s Secrets tool under the 🔒 icon.
  • Use the built-in Replit Database for lightweight storage only. If you expect many concurrent users, move to an external service like Supabase or MongoDB Atlas later.
  • Replit auto-restarts when files change, so avoid saving inside loops or infinite tasks that constantly touch files.
  • All code runs in one container, so keep things simple — Replit isn’t a multi-microservice environment.

 

This is a fully working small inventory system inside Replit — no external setup required. You can grow this setup by adding authentication, connecting to a proper database, or styling your frontend later, but this version demonstrates all the important mechanics clearly and correctly for a real Replit environment.

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 Inventory API with Express and Replit Database


const express = require('express');
const bodyParser = require('body-parser');
const Database = require('@replit/database');

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

app.use(bodyParser.json());

// Update or create an inventory item with quantity adjustment
app.post('/api/inventory/update', async (req, res) => {
  const { sku, deltaQty, name } = req.body;
  if (!sku || !deltaQty) return res.status(400).json({ error: 'Missing sku or deltaQty' });

  try {
    const existing = await db.get(sku) || { name: name || 'Unnamed Item', qty: 0 };
    const newQty = Math.max(0, existing.qty + deltaQty);
    await db.set(sku, { ...existing, qty: newQty });
    res.json({ sku, ...existing, qty: newQty });
  } catch (e) {
    res.status(500).json({ error: e.message });
  }
});

// Fetch all inventory items
app.get('/api/inventory', async (req, res) => {
  try {
    const allItems = await db.list();
    const data = {};
    for (const key of allItems) {
      data[key] = await db.get(key);
    }
    res.json(data);
  } catch (e) {
    res.status(500).json({ error: e.message });
  }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log('Inventory API running on port', PORT));

How to Sync Your Inventory with an External Supplier API in Replit


import express from "express";
import fetch from "node-fetch";
import Database from "@replit/database";

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

app.use(express.json());

// Sync local inventory item with an external supplier API
app.post("/api/inventory/syncSupplier", async (req, res) => {
  const { sku } = req.body;
  if (!sku) return res.status(400).json({ error: "Missing SKU" });

  try {
    const localItem = await db.get(sku);
    if (!localItem) return res.status(404).json({ error: "Item not found" });

    const supplierResponse = await fetch(
      `https://api.supplier.example.com/products/${sku}`,
      {
        headers: { Authorization: `Bearer ${process.env.SUPPLIER_API_KEY}` },
      }
    );

    if (!supplierResponse.ok)
      throw new Error(`Supplier API error: ${supplierResponse.status}`);

    const supplierData = await supplierResponse.json();

    const updatedItem = {
      ...localItem,
      supplierPrice: supplierData.price,
      supplierStock: supplierData.stock,
      lastSynced: new Date().toISOString(),
    };

    await db.set(sku, updatedItem);
    res.json({ message: "Synced successfully", item: updatedItem });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log("Sync API running on port", PORT));

How to Handle Concurrent Inventory Reservations in Replit


import express from "express";
import Database from "@replit/database";

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

app.use(express.json());

// Atomic inventory reservation for concurrent checkout requests
app.post("/api/inventory/reserve", async (req, res) => {
  const { sku, quantity } = req.body;
  if (!sku || !quantity) return res.status(400).json({ error: "Missing sku or quantity" });

  try {
    const lockKey = `lock_${sku}`;
    const lock = await db.get(lockKey);
    if (lock) return res.status(423).json({ error: "Item is being updated, try again." });
    await db.set(lockKey, true);

    const item = (await db.get(sku)) || { qty: 0 };
    if (item.qty < quantity) {
      await db.delete(lockKey);
      return res.status(409).json({ error: "Not enough stock" });
    }

    item.qty -= quantity;
    await db.set(sku, item);

    await db.delete(lockKey);
    return res.json({ reserved: quantity, remaining: item.qty });
  } catch (err) {
    await db.delete(`lock_${req.body.sku}`); 
    res.status(500).json({ error: err.message });
  }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log("Inventory Reservation API running on port", PORT));

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 Inventory system with Replit

Building an inventory system on Replit works best if you treat it like a small cloud-hosted development environment — lightweight, persistent while it’s active, but not meant to store tons of data long-term or run 24/7 without external help. The simplest robust structure is a Node.js + Express backend with a SQLite or external database, a simple HTML/JS frontend, and environment variables handled as Replit Secrets. You’ll use the “Files” pane to create folders and files for routes, UI, and configuration. The key is: separate your logic in folders, use Replit secrets for credentials, don’t rely on local JSON files for production data, and test endpoints directly inside Replit’s built-in webview or a browser.

 

Project Structure

 

Here’s how your Replit file tree might look:

  • index.js — main server file
  • db.js — handles database setup and queries
  • routes/ — folder for Express routes (for example, routes/inventory.js)
  • public/ — static frontend files (HTML, CSS, JS)
  • secrets — handled through Replit’s built-in Secrets tab, not physically created

 

Step 1: Setup Express Server

 

In Replit, create a new Node.js repl. Replace the auto-created index.js with this:

// index.js

import express from "express"
import bodyParser from "body-parser"
import cors from "cors"
import inventoryRoutes from "./routes/inventory.js"

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

// Mount your routes
app.use("/api/inventory", inventoryRoutes)

// Static frontend (if any)
app.use(express.static("public"))

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

Run this once using the “Run” button. You’ll see “Server running on port …” in the console. Replit automatically creates a public URL at the top of your screen where your app is live while active.

 

Step 2: Setup Database

 

You can use SQLite (simple, built-in file-based database that works well inside Replit). In your db.js file:

// db.js

import Database from "better-sqlite3"

// Create a local database file named 'inventory.db' inside project root
const db = new Database("inventory.db")

// Create table if it doesn't exist
db.prepare(`
  CREATE TABLE IF NOT EXISTS items (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    quantity INTEGER NOT NULL,
    price REAL NOT NULL
  );
`).run()

export default db

Replit will persist this inventory.db file as long as your repl exists. Don’t rely on it for mission-critical long-term data, but it’s perfect for small projects or prototypes.

 

Step 3: Create Routes

 

Under a new folder routes (create it using Replit’s file tree UI), create inventory.js:

// routes/inventory.js

import express from "express"
import db from "../db.js"

const router = express.Router()

// GET all items
router.get("/", (req, res) => {
  const items = db.prepare("SELECT * FROM items").all()
  res.json(items)
})

// POST to add new item
router.post("/", (req, res) => {
  const { name, quantity, price } = req.body
  const stmt = db.prepare("INSERT INTO items (name, quantity, price) VALUES (?, ?, ?)")
  const result = stmt.run(name, quantity, price)
  res.json({ id: result.lastInsertRowid, name, quantity, price })
})

// PUT to update existing item
router.put("/:id", (req, res) => {
  const { id } = req.params
  const { name, quantity, price } = req.body
  db.prepare("UPDATE items SET name=?, quantity=?, price=? WHERE id=?").run(name, quantity, price, id)
  res.json({ message: "Item updated" })
})

// DELETE an item
router.delete("/:id", (req, res) => {
  const { id } = req.params
  db.prepare("DELETE FROM items WHERE id=?").run(id)
  res.json({ message: "Item deleted" })
})

export default router

 

Step 4: Simple Frontend (Optional)

 

Inside your public folder, create index.html with a small UI to test the API. It’ll be served automatically because of express.static("public") in your index.js.

<!-- public/index.html -->

<!DOCTYPE html>
<html>
  <head>
    <title>Inventory System</title>
  </head>
  <body>
    <h1>My Inventory</h1>
    <button id="load">Load Items</button>
    <ul id="list"></ul>

    <script>
      document.getElementById("load").onclick = async () => {
        const res = await fetch("/api/inventory")
        const items = await res.json()
        const list = document.getElementById("list")
        list.innerHTML = ""
        items.forEach(i => {
          const li = document.createElement("li")
          li.textContent = `${i.name} (${i.quantity}) - $${i.price}`
          list.appendChild(li)
        })
      }
    </script>
  </body>
</html>

Now, press “Run”, and your Replit preview window will show a simple working inventory fetcher connected to your live backend.

 

Step 5: Handling Secrets (If You Add External DB or API)

 

Go to the Secrets tab on left sidebar (key icon). Add variables like DB_URL or API_KEY. In your code, you can access them with process.env.DB\_URL. Never hardcode credentials — Replit keeps them safe this way.

 

Step 6: Collaboration and Version Control

 

For teams, turn on Replit’s Multiplayer so others can edit with you in real-time. Connect a GitHub repo from the version control panel (the ‘branch’ icon) so you can commit and push changes. Avoid committing inventory.db if data is sensitive – add it to .gitignore.

 

Common Pitfalls to Avoid

 

  • Sleeping repls: Free Repls shut down when inactive, so your inventory app won’t be “always on.” Use Replit Deployments or external hosting if you need 24/7 availability.
  • Do not use local JSON for storage — writes may fail or conflict under concurrency; use SQLite or a hosted DB.
  • Keep code modular: separating db, routes, and main server files keeps things easier to debug inside Replit’s shared IDE.
  • Use console logs wisely: Replit’s console persists output for the current session, which helps quickly debugging routes and data issues.

 

Summary

 

Your inventory system on Replit should have a clear folder structure, SQLite for lightweight persistence, Express routes for CRUD operations, and Replit secrets for any private credentials. Test via Replit webview, commit changes via built-in Git integration, and if you scale up, you can later connect to an external database and upgrade to Replit Deployments for uptime. This setup is reliable, fast to build, and fully aligns with how Replit’s environment really works.

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