How a major refactoring job turned into a brand new React stack

In the year 2019 our team was facing a massive challenge. Back then a lot of processes in our warehouse were relying on tiny RaspberryPi powered touch screen terminals. Need to pack an order? Even though it sounds easy, it actually requires a lot of steps – you need to choose the right box, make sure all of the picked products are the right ones and in correct quantities, secure fragile items, attach invoices, gift messages… As the offering of our services grew, these processes grew alongside them and the terminals were there to guide our colleagues from the warehouse and make sure that no mistakes were made. 

RaspberryPi touchscreen terminal

For a long time, the brave tiny Raspberries served us well. But with every new requirement for what these terminals should do, it became increasingly clear that we are reaching the limits of what the hardware was capable of. Iterating on the software side also started becoming a problem, as the software stack was built on Python, whereas our frontend team consisted of React developers. Thus a challenge faced us. And Stations were born.

Our brand new Packing Station

We have already talked about the hardware we use to run our Stations in a previous blog post, but here’s a quick rundown – Intel Atom powered miniPC that simply plugs into a large high resolution touchscreen. But let’s talk about the software. As our frontend team is heavily invested in React the choice was clear. A brand new stack running on Node.js, strictly typed with TypeScript and powered by React 16.8 with it’s (back then) completely new Hooks technology. GraphQL with it’s Apollo client was chosen to handle network calls and Redux got implemented to handle application state. To make everything work asynchronously, we’ve decided to go with Redux middlewares.

Handling peripherals

But our Stations are not just touch screen web apps. They also need to handle things like taking photos of each packed order, printing shipping labels through Zebra printers, scanning barcodes or printing extra documents. Our goal was to abstract these actions, so the solution that we came up with was to create a Station Service. The Service is basically a graphQl server that communicates with the frontend through mutations and then executes corresponding bash scripts in the underlying Debian distro that is powering the whole miniPC. This way we can easily control all of the needed functionality of the PC right from the React app, like installing updates, handling reboots, installing printer drivers or managing the printing queue. So to recap the setup – the basic structure of our Station is a miniPC running Debian with a graphQL service providing OS functionality to a Node.js React App running in a headless chromium browser.

Packing line at our Florida warehouse

Packing App

The first app that we’ve implemented on this software stack is our Packing App. As already mentioned, the general goal of this app is to give a helping hand to our packers and guide them through the process without making unnecessary mistakes. This includes steps like verifying the picked quantity, making sure that the picked SKUs are the right ones, helping the packer decide on which box they should pack the order in and also print out a shipping label.

Happy Path walkthrough of our Packing App

The main way how packers interact with the app is through barcode scanners. When connected to a PC, they act basically the same as regular keyboards. Based on their configuration, they “type” a scanned barcode with a special character at the end (most often an Enter), so they are really easy to parse. This makes it a perfect use case for React Hooks, which we’ve used extensively for working with the scanners. Thanks to our custom Scanned Input hooks we can easily handle configuration barcodes like setting up different dev environments or logging in and out different users.

Custom React Hooks allow us things like parsing scanned input from barcode readers

The other input method is touch. Thanks to React, we’ve been able to easily implement a beautifully fast and responsive touch interface, complete with fluid animations, loading states and other features that really empower our colleagues instead of standing in the way of their work. This was one of the main goals of this whole project and nothing makes us happier than hearing positive feedback, even on miniscule improvements like customised login screen images during holiday seasons. The styling solution we went with is CSS Modules, which enabled us to easily create reusable components. It might require a notable investment of resources to create a proper component library, but once we’ve managed to do that, the cost of creating new screens and features went down significantly. And not just screens – also new apps.

Due to the proliferation of smartphones and tablets, almost everyone in the world is used to touch-enabled interaction paradigms. We’ve taken full advantage of that in designing our Station apps and thanks to that there is almost no learning curve for our warehouse workers to start working with them.

Currently our Stations run 6 different apps for various use cases in our warehouses, all using the same familiar design and programming paradigms. Switching between them is as simple as scanning a barcode, which makes it super easy to boost productivity in different parts of the warehouse based on current demand. New Station can be set up in a matter of minutes and there really is no problem for a Packer to assume a different role if needed, because they all know they can count on the Station to walk them through an unfamiliar process. In terms of implementation, every screen in every app has the same differentiation of logic, render and routing. Most screens in our apps have a pretty straightforward path of interaction – usually a button press is required to move forward, which fires a Redux action that has a corresponding middleware. These handle all network requests. Middlewares also follow a common design pattern – requests are typed with TS-Failable library, which gives us a simple way to handle network or API failures. Based on the outcome, we then use flash messages to communicate with the user and React Router to navigate through the process, which after completing it’s happy path goes back to start, ready to deal with another order.

Example middleware from our Stations codebase

Even though over time some of the apps have grown to quite significant size, we also keep up to date process maps – these are used by everyone from TPMs figuring out new feature requests, developers implementing them and especially our QA engineers making sure that it’s not just the happy path that works correctly.

Our current selection of Station Apps

Stations are just one of the many projects we have had the pleasure to work on at ShipMonk. As this project has turned out to be a big success, not long after we have followed up on it with another new React stack powering apps running on mobile devices used in our warehouses. Keep it tuned to our RnD blog to learn more about those in the future!

About author:

Matouš Valeš

FrontEnd Developer