Desktop6 min read
Building a restaurant POS that keeps up with the floor
A desktop point-of-sale for restaurants and bars: tables, split bills, kitchen and bar tickets, and a cashier's day that closes cleanly.
Written by Balekage Gael
A restaurant on a busy night doesn't forgive slow software. Waiters need to take an order in seconds, the kitchen needs the right ticket right away, and the cashier needs the numbers to add up at closing time.
That's what I set out to build: a desktop point-of-sale for restaurants and bars, with a React interface running in Electron and a local Express API with Prisma.
Built for the floor
The app is designed for a touchscreen at the counter, with an on-screen keyboard so staff never need a physical one. A waiter's flow looks like this:
- Pick a table it's marked as occupied and linked to that waiter.
- Create the order and add items, searching by name or scanning a barcode.
- Add or remove items as the table changes its mind.
- Split the bill when a group wants to pay separately, or apply a discount.
Every removed item is recorded as a cancellation, with who removed it and when so the end-of-day numbers can always be explained.
The right ticket on the right printer
A restaurant isn't one room. Drinks are made at the bar, food in the kitchen. In the POS, each product belongs to a station (the kitchen, the bar…), and each station has its own network thermal printer.
When an order is sent, its items are grouped by station and each group prints on that station's printer:
const { ThermalPrinter, PrinterTypes } = require("node-thermal-printer");
// One ticket per station: kitchen items to the kitchen, drinks to the bar.
for (const { printer, items } of groupByStation(order.items)) {
const ticket = new ThermalPrinter({
type: PrinterTypes.EPSON,
interface: `tcp://${printer.ip}:${printer.port}`,
});
ticket.println(`Table ${order.table.number}`);
items.forEach((item) => ticket.println(`${item.quantity} x ${item.name}`));
ticket.cut();
await ticket.execute();
}
The cook sees only what the cook needs to make, and nothing gets lost between the counter and the kitchen.
The cashier's day
The cashier has their own screen, built around the money:
- Receive payments for an order and record how it was paid.
- Signed bills for trusted customers who settle later, so the debt is tracked instead of forgotten.
- Expenses paid out of the till during the day.
- Close the day with a summary of sales, so the till and the system agree before going home.
A server in the back room
The API runs on a computer in the restaurant as a Windows service, so it starts with the machine and keeps running in the background. The tills connect to it over the local network, which keeps the POS fast and working even when the internet connection isn't. The data lives in PostgreSQL or MySQL, chosen when the system is set up.
What I learned
- Speed is a feature. Every extra tap is felt on a busy night.
- Every change needs a trail. Cancellations, discounts and signed bills are what make the day's numbers trustworthy.
- Printers are part of the product. Getting the right ticket to the right station matters as much as the screens.