DEV Community

Steven Wallace
Steven Wallace

Posted on

Building a Full-Stack Stats Utility App with Node, React, Rust, and Python

📊 A containerized statistics toolkit that runs Rust + Python microservices behind a Node backend and React frontend.

🧠 Built with TypeScript, Axum, FastAPI, and Docker Compose.

💾 Repo: https://github.com/swallace100/stats-utility-app


Intro

Data analysis usually involves juggling multiple tools, such as Pandas for stats, R for tests, and Matplotlib for plots.

I wanted something simpler, so I made a single, containerized app where I could upload a CSV with numeric data and get common summary stats with plots.

The Stats Utility App is a lightweight, polyglot toolkit that runs four services:

  • React (frontend)
  • Node.js (backend)
  • Rust (stats engine)
  • Python (plot server)

The app runs completely in Docker and the backend orchestrates all cross-service communication.

In this post, I’ll show how it’s structured, how it runs, and what I learned while piecing four languages together.


Tech Stack

• Frontend: React + Vite + Tailwind + shadcn/ui

• Backend: Node.js (Express + TypeScript)

• Rust Microservice: Axum + serde for high-performance numeric kernels

• Python Microservice: FastAPI + Matplotlib for rendering plots

• Orchestration: Docker + Docker Compose

• Validation: Zod + shared JSON schemas

Everything runs locally in containers and no database is required.

Jobs are stored in memory, keeping it simple and fast to rebuild or demo.


Architecture Overview

frontend/     # React + Tailwind + Vite UI
backend/      # Express API gateway
stats_rs/     # Rust microservice for stats
plots_py/     # Python microservice for plots
docker/       # Compose file + build config
Enter fullscreen mode Exit fullscreen mode

Service flow:

React → Node (Express) → Rust (Axum) → Python (FastAPI)
Enter fullscreen mode Exit fullscreen mode

Each service exposes its own /health endpoint. Docker Compose ensures startup order and readiness before serving the frontend.


Backend Flow

The backend acts as an orchestrator.

When you upload a CSV file, it:

  1. Reads and validates metadata (Zod schema)
  2. Sends JSON { values: [..] } to the Rust service
  3. Waits for summary or distribution results
  4. Forwards the data to the Python plotter
  5. Serves JSON + images back to the frontend

Example route:

app.post("/analyze/summary", textCsv, async (req, res) => {
  const csv = req.body as string;
  const out = await fetchJSON(`${RUST_SVC_URL}/api/v1/stats/summary`, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({ values: csv.split("\n").map(Number) }),
  });
  res.json(out);
});
Enter fullscreen mode Exit fullscreen mode

Frontend

The UI (React + Vite) lets users drag-and-drop a CSV file and instantly view:

  • Common summary stats (mean, median, sd, IQR, etc.)
  • Distribution and ECDF plots
  • QQ diagnostic plots

It calls /analyze/* and /plot/* endpoints on the backend, showing a live “Analyzing…” state while the microservices process the request.

Example Output
✅ Summary statistics (mean, median, std, min/max)
📈 Histogram + ECDF + QQ plots
🧮 All computed in Rust and rendered with Matplotlib
💡 Runs entirely in Docker, so setup takes minutes

Stats Utility App screenshot


Environment Setup

# build and run all services
make up
# or manually:
docker compose -f docker/docker-compose.yml up --build
Enter fullscreen mode Exit fullscreen mode

Open http://localhost:8085 to access the app.

Services:

Service Port Description
frontend 8085 React UI (served by Nginx)
backend 8080 Express API
stats_rs 9000 Rust microservice
plots_py 7000 Python microservice

Lessons Learned

  • Rust’s type safety and Axum’s ergonomics make it a good match for numeric microservices.
  • FastAPI is ideal for plotting and quick JSON endpoints.
  • Zod and Pydantic together make schema validation simple across languages.
  • Docker and Docker Compose gave me the most issues out of all aspects of this project, but solving them gave me a much better understanding on how to work with added Docker-related complexity.
  • Storing data in memory instead of a database is a quick option for calculations that don't need to be saved.

Repository + License

📂 Full source: https://github.com/swallace100/stats-utility-app
⚖️ License: MIT

Top comments (0)