Investment Nerds
- Frontend Architecture
- Design System
- Web App Development
- API Integration
- Admin Tooling
- Client work
- Self-hosted, not publicly reachable
Four apps, seven shared packages, 406 files. Markets data, portfolios, a moderated forum and the admin console behind them.
An investment platform is not one product. A visitor being convinced, a new user being verified, an investor tracking a portfolio and a moderator removing a post are four different applications with four different security postures, and they still have to look and behave like one company.
Contracted to build the frontend. The backend, the market data collector and the database schema were owned separately; my work was everything the browser runs and the contract between it and the API.
- Four applications in one Yarn and Turborepo workspace
- Shared component library, theme tokens and layout primitives
- API types generated from OpenAPI, validated with zod
- RTK Query data layer with a websocket client for live market data
- Centralised auth with route guards across all four apps
- Admin console for stocks, currencies, exchanges, brokers and users
Four apps, one company
The landing site has to load fast for somebody who has never heard of the product. The auth app handles registration, verification and password resets, and is the one surface where a mistake is a security incident rather than a bug. The client app is where investors spend their time. The admin console is where the team runs the business.
Building those as one application means the marketing page ships the admin bundle. Building them as four separate codebases means four component libraries drifting apart until the product stops looking like itself. So they are four applications in one workspace, sharing seven packages: the design system, the layouts, the auth client, the API layer, the generated types, the hooks and the build config.
The practical test of that decision is a colour change. Adjusting a token in the shared theme moves all four apps at once, and nobody has to remember that the admin console also has a button.
The contract with the backend
The frontend and the backend were built by different people, which makes the API the place where a project like this usually goes wrong. A field gets renamed, the frontend keeps compiling, and the failure arrives in production as an empty column nobody notices for a week.
So the types are not written by hand. They are generated from the backend’s OpenAPI description and paired with zod schemas, which means a renamed field is a build error on my side rather than a blank space on somebody’s portfolio page. It moves the cost of a breaking change from the user back to the developer, where it belongs.
Above that sits a single RTK Query layer shared by every app, with a websocket client for the data that cannot wait for a refetch. Market prices move; the components that read them subscribe rather than poll.
The console nobody sees
The admin app is a third of the frontend and none of the marketing. Stocks, currencies, exchanges, brokers, companies, users, API keys and forum moderation each need list, detail, create, edit and audit, and every one of them is somebody’s job rather than a demo.
It is also where the shared packages earn their keep. The console is built almost entirely from the same component library as the client app, so the surface the team uses all day gets the accessibility work, the keyboard handling and the loading states that were done once for everybody.
Where it stands
The platform is self-hosted on the company’s own infrastructure rather than deployed to a public host, so there is no link on this page to click. The frontend ships as Docker images built from the workspace, one per app.
Want to talk about the work?
Tell me what you have in mind and what it has to do. I answer within a day, and I will say plainly if I am not the right person for it.