Skip to content
Back to projects

Cross-Platform POS & Inventory System

Project Overview

A comprehensive, cross-platform Point of Sale system featuring a responsive Flutter frontend. Built with a high-performance Node.js and Express backend, the system utilizes Prisma ORM and PostgreSQL for secure, real-time data synchronization and robust inventory management.

Technologies Used

Flutter Node.js Express Prisma PostgreSQL Dart

Key Features

  • Real-Time Inventory Tracking – Automated stock level updates after every sale to prevent overselling.
  • Secure Authentication – Role-based access control (Admin, Manager, Cashier) protecting sensitive business data.
  • Dynamic Dashboard – Instant visualization of daily revenue, top-selling products, and sales metrics.
  • Automated Sales Generation – Unique short-code generation for every transaction to simplify record keeping.
  • Cross-Platform Accessibility – Seamless performance across mobile and desktop environments via Flutter.

Challenges & Solutions

A point-of-sale system is unforgiving: money and stock are on the line in real time, and a bug doesn’t just look bad — it costs the business. Connecting a Flutter front end to a Node.js and Express back end under those stakes is where this project really tested me.

Keeping stock accurate when sales happen at once

My first version updated inventory by reading the current count, subtracting, and writing it back — which is fine until two sales of the same product land at nearly the same moment and one quietly overwrites the other, leaving the stock count wrong. I reworked the checkout so the sale record and the stock deduction run inside a single Prisma transaction. Either both succeed together or neither does, so the inventory can never drift out of sync no matter how the sales overlap. That bug is where atomic transactions stopped being textbook theory and became something I genuinely understand.

Real role-based access, not just hidden buttons

It’s tempting to “secure” an app by just hiding admin buttons from a cashier — but anyone who knows the API can call it directly. I built the Admin / Manager / Cashier roles so the permissions are enforced on the Express API, on every sensitive endpoint, not only in the Flutter UI. The interface adapts to who’s logged in, but the real gate is the server, so a cashier’s app physically can’t reach manager-only actions. It permanently changed how I think about security: the UI is a convenience, the back end is the boundary.

Making Flutter and the Node.js API speak the same language

Wiring a Flutter client to an Express REST back end surfaced all the messy in-between problems — loading and error states, a dashboard that shouldn’t freeze while data arrives, and responses that needed a predictable shape. I settled on a clear contract between the two: consistent JSON from the API and explicit handling of every loading and failure case on the client. The result is a UI that stays responsive while it waits and tells the user what’s happening instead of hanging on a blank screen. Owning both sides of that boundary taught me more about how real apps fit together than any single tutorial could.