DEV Community

Daniel Ioni
Daniel Ioni

Posted on

๐Ÿš€ Building a Fullโ€‘Stack Decentralized Marketplace with Node.js, React, MongoDB and Monero

๐Ÿš€ Building a Fullโ€‘Stack Decentralized Marketplace with Node.js, React, MongoDB and Monero

In this guide, Iโ€™ll walk you through the complete development and deployment of MyZubster โ€“ a productionโ€‘ready marketplace where users can tokenize realโ€‘world assets, trade them in a P2P order book, and pay with Monero (XMR). The entire stack runs on a single VPS, with a React frontend, an Express API, MongoDB, and a Monero wallet RPC.

๐ŸŒ Live demo: https://myzubster.com
๐Ÿ“ฆ Source code: github.com/DanielIoni-creator/MyZubsterGateway
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“Œ Table of Contents

Why I built this

Architecture overview

Core features

Tech stack

Setup & installation

Environment configuration

Running the project locally

Deploying to production with Nginx

Integrating Monero payments

Challenges & solutions

Whatโ€™s next

Resources
Enter fullscreen mode Exit fullscreen mode

๐Ÿ’ก Why I built this

The idea was simple: create a platform where anyone can tokenise a physical asset (real estate, equity, art, etc.) and trade it in a secure, private marketplace. I wanted real cryptocurrency payments (not just a fiat gateway) and a smooth user experience. Monero was the obvious choice for its privacy and low fees.

The project evolved from a simple API to a fullโ€‘stack application with a modern React frontend, a robust orderโ€‘book engine, and a fully automated Monero payment flow.
๐Ÿงฑ Architecture overview
text

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Client (Browser) โ”‚
โ”‚ https://myzubster.com โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚
โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Nginx (reverse proxy) โ”‚
โ”‚ โ€“ serves static frontend โ”‚
โ”‚ โ€“ proxies /api/* to Node.js โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚ โ”‚
โ–ผ โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ React + Vite โ”‚ โ”‚ Node.js + Express โ”‚
โ”‚ (Frontend) โ”‚ โ”‚ (Backend API) โ”‚
โ”‚ Port 5173 (dev) โ”‚ โ”‚ Port 3000 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚ โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ MongoDB โ”‚
โ”‚ (Users, Tokens, Holdings, Orders, Payments) โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚
โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Monero RPC Wallet โ”‚
โ”‚ โ€“ Generates subaddress per order โ”‚
โ”‚ โ€“ Monitors incoming payments โ”‚
โ”‚ โ€“ Autoโ€‘completes orders on confirmation โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

All services run on a single Ubuntu VPS (Aruba Cloud). The frontend is built with Vite and served statically by Nginx. The Node.js gateway handles all business logic and talks to MongoDB and the Monero wallet RPC.
โœจ Core features
Feature Description
JWT Authentication Secure login/register with bcryptโ€‘hashed passwords
Asset Tokenisation Create tokens with metadata (name, symbol, supply, asset type, location, description)
Order Book Users can create sell orders (/api/marketplace/sell) and buy from open orders (/api/marketplace/buy/:orderId)
Holdings Track user balances with lockedAmount for tokens currently in active orders
Monero Payments Each order gets a unique subaddress; the system automatically monitors confirmations and completes the order when payment is detected
Admin Dashboard (Work in progress โ€“ but the API already supports user management and transaction logs)
Production Ready Nginx, systemd, firewall, and SSL (optional)
๐Ÿ› ๏ธ Tech stack
Backend

Node.js (v20) + Express.js

MongoDB + Mongoose ODM

JSON Web Tokens for authentication

Axios for Monero RPC calls

Systemd for service management
Enter fullscreen mode Exit fullscreen mode

Frontend

React (functional components + hooks)

Vite (fast build tool)

React Router for clientโ€‘side routing

Axios for API calls (with JWT interceptors)
Enter fullscreen mode Exit fullscreen mode

Infrastructure

Nginx as reverse proxy and static server

UFW for firewall

Certbot (optional) for Letโ€™s Encrypt SSL
Enter fullscreen mode Exit fullscreen mode

Cryptocurrency

Monero stagenet (testnet) โ€“ the RPC wallet runs alongside the gateway.
Enter fullscreen mode Exit fullscreen mode

๐Ÿš€ Setup & installation

  1. Clone the repository bash

git clone https://github.com/DanielIoni-creator/MyZubsterGateway.git
cd MyZubsterGateway

  1. Install backend dependencies bash

npm install

  1. Install frontend dependencies bash

cd ../myzubster-frontend # if you have it in a separate folder
npm install

If you prefer to keep everything in one repo, you can place the frontend inside a frontend/ directory.
๐Ÿ”ง Environment configuration

Create a .env file in the backend root:
env

Server

PORT=3000
JWT_SECRET=your_super_secret_key

MongoDB

MONGODB_URI=mongodb://localhost:27017/myzubster

Monero (stagenet)

MONERO_WALLET_RPC_URL=http://localhost:18083/json_rpc
MONERO_DAEMON_RPC_URL=http://localhost:18081/json_rpc
MONERO_RPC_USER=myzubster
MONERO_RPC_PASSWORD=your_secure_password
MONERO_NETWORK=stagenet

๐Ÿƒ Running the project locally
Start MongoDB
bash

systemctl start mongod

Start the backend (development)
bash

cd ~/MyZubsterGateway
node server.js

or with nodemon: npm run dev

Start the frontend (development)
bash

cd ~/myzubster-frontend
npm run dev -- --host

Now you can access:

Frontend: http://localhost:5173

API: http://localhost:3000/api/health
Enter fullscreen mode Exit fullscreen mode

๐ŸŒ Deploying to production with Nginx

  1. Build the frontend bash

cd ~/myzubster-frontend
npm run build

This creates the dist/ folder

  1. Move the build to a standard location bash

mkdir -p /var/www/myzubster-frontend
cp -r dist/* /var/www/myzubster-frontend/
chown -R www-data:www-data /var/www/myzubster-frontend
chmod -R 755 /var/www/myzubster-frontend

  1. Configure Nginx

Create /etc/nginx/sites-available/myzubster:
nginx

server {
listen 80;
server_name myzubster.com www.myzubster.com;

root /var/www/myzubster-frontend;
index index.html;

location / {
    try_files $uri $uri/ /index.html;
}

location /api/ {
    proxy_pass http://localhost:3000/api/;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
}
Enter fullscreen mode Exit fullscreen mode

}

Enable the site and reload Nginx:
bash

ln -s /etc/nginx/sites-available/myzubster /etc/nginx/sites-enabled/
nginx -t
systemctl reload nginx

  1. (Optional) Add SSL with Letโ€™s Encrypt bash

apt install certbot python3-certbot-nginx -y
certbot --nginx -d myzubster.com -d www.myzubster.com

๐Ÿช™ Integrating Monero payments

The gateway communicates with a Monero wallet RPC. Hereโ€™s how it works:

  1. Start the Monero daemon (stagenet) bash

./monerod --stagenet --detach

  1. Create or open a wallet bash

./monero-wallet-cli --generate-new-wallet myzubster_wallet --password 'YourPassword' --daemon-address localhost:18081 --stagenet

  1. Start the wallet RPC bash

nohup ./monero-wallet-rpc \
--rpc-bind-port 18083 \
--daemon-address localhost:18081 \
--wallet-file ./myzubster_wallet \
--password 'YourPassword' \
--disable-rpc-login \
--trusted-daemon \
--stagenet > monero-wallet-rpc.log 2>&1 &

  1. The backend service

The moneroService.js uses axios to call the RPC methods:

create_address โ€“ generates a unique subaddress per order

get_transfers โ€“ checks incoming payments

get_balance โ€“ verifies wallet balance
Enter fullscreen mode Exit fullscreen mode

When a user initiates a payment, the system creates a MoneroTransaction record with status: pending. A background monitor (paymentMonitor.js) runs every 30 seconds, checks for new transfers, and if the amount matches, it marks the transaction as confirmed and automatically completes the order โ€“ transferring tokens from the seller to the buyer.
๐Ÿงฉ Challenges & solutions
๐Ÿ” Permission denied on /root

Nginx runs as www-data and cannot read files inside /root. The solution: move the frontend build to /var/www and set proper permissions.
๐Ÿ”Œ Monero RPC connection issues

The wallet RPC failed to connect to stagenet.community:38081. I switched to a local daemon or used node.moneroworld.com:38081. Also, ensure the wallet is not locked (kill any other wallet processes and remove *.lock files).
๐Ÿ“ฆ Missing monero-js package

The package monero-js is not available on npm. Instead, I used axios to call the JSONโ€‘RPC endpoints directly โ€“ simpler and more reliable.
๐Ÿงต Race conditions in orders

To prevent doubleโ€‘spending, the TokenHolding model uses a lockedAmount field. When a sell order is created, the amount is locked. When the order is filled or cancelled, the lock is released atomically using MongoDB updates.
๐Ÿ”ฎ Whatโ€™s next

HTTPS โ€“ already planned with Letโ€™s Encrypt.

Admin dashboard โ€“ realโ€‘time stats, user management, and transaction logs.

Multiple cryptocurrencies โ€“ add Bitcoin and Ethereum support.

Smart contract settlement โ€“ for fully onโ€‘chain trades.

Mobile app โ€“ with React Native.
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“š Resources

GitHub Repository: DanielIoni-creator/MyZubsterGateway

Live Demo: https://myzubster.com

DEV.to Profile: @danielioni

Monero RPC Docs: getmonero.org
Enter fullscreen mode Exit fullscreen mode

๐Ÿ™Œ Conclusion

Building MyZubster was an incredible journey that touched every aspect of fullโ€‘stack development โ€“ from database design and API architecture to blockchain integration and production deployment. The system is now live, stable, and ready for realโ€‘world use.

If youโ€™re interested in building something similar or want to contribute, feel free to open an issue or pull request on GitHub.

Happy coding! ๐Ÿš€

Built with โค๏ธ by @danielioni

Top comments (0)