
Internal build
SpinFlow.ai: building our own application frontend under real load
SpinFlow.ai is our own enterprise AI platform. We built its frontend on the same stack we recommend to clients, so every constraint we describe in a sales conversation is one we have already hit ourselves.
- SpinFlow.ai is a production product, not a demo. It runs continuously and carries real accounts, real data, and real cost per request.
- The frontend has to hold authenticated state, stream AI output, render large data tables, and enforce role scoping at the same time.
- We chose the stack before we started selling it to clients. The platform came first.
Last reviewed 2026-08-10
What SpinFlow.ai solves
SpinFlow.ai exists because internal teams needed a single place to run AI-assisted workflows against structured business data without copying that data into a third-party tool. It combines document intake, model orchestration, and reporting into one authenticated application rather than a chain of disconnected scripts.
The problem was never generating text. Any API can do that. The problem was giving a user a live view of a long-running job, letting them intervene mid-run, and keeping that view correct if they refresh the page, lose connection, or open the same job in a second tab.
Why a page builder was never an option
A marketing site can live on a template platform because the content is static between deploys. SpinFlow.ai cannot. Every screen depends on who is logged in, what their role permits, what job is currently running, and what changed in the last few seconds. That is application state, not page content, and page builders have no model for it.
We needed a real frontend framework with server rendering, typed data loading, and a client runtime that can hold a persistent connection open. That ruled out static site generators and static AI-search-optimized markup as the entire solution. Rendering is table stakes here, not the hard part.
Architecture decisions
We built the frontend on TanStack Start with React 19. Routes are file-based and each one defines its own server-side loader, so the data a page needs is fetched on the server and streamed down already shaped for the view instead of being assembled client-side after a spinner.
The database is Postgres with row level security enforced at the database layer, not just in application code. A query issued from a compromised or buggy route still cannot return rows outside the caller's tenant, because the database itself checks the policy before returning anything. Application-layer checks are a second layer, not the only layer.
Styling uses Tailwind v4 with design tokens rather than a component library skinned after the fact. Tokens let us keep dense data views, dashboards, and the marketing-facing parts of the product visually consistent without duplicating spacing and color decisions in three places.
Handling real state
Most of SpinFlow.ai's complexity is not in any single screen. It is in keeping many screens honest about the same underlying state. A job status shown on a dashboard card, a detail page, and a notification badge all have to agree, and they have to agree even when the job changes state while the user is looking at something else.
We handle this with server-driven revalidation tied to route loaders rather than a client-side global store trying to mirror the server from memory. When the server says a job finished, every view that depends on that job refetches its own slice. It is slower to build than a single client store but it does not drift.
Streaming AI output
Model responses are streamed token by token to the browser over a persistent connection, rendered incrementally so the user sees progress instead of a blank screen followed by a wall of text. Streaming has to survive the user navigating away mid-response, and it has to cancel cleanly server-side when they do, or the job keeps burning tokens against nobody.
We run the streaming endpoints on an edge runtime specifically because cold starts on a traditional server would add visible lag to the first token. Edge functions start close to the user and close to where the request originates, which matters more for a chat-style interface than for a page that just needs to render once.
Auth and role scoping
SpinFlow.ai has multiple roles inside a single account: administrators who configure workflows, operators who run them, and reviewers who only need to approve or reject output. Each role sees a materially different set of screens and actions, not just a hidden button.
Role checks happen in three places on purpose: the database policy, the server loader, and the UI. Redundant, but each layer fails differently. The UI hiding an action is a convenience. The loader refusing to return data is a real boundary. The database refusing the row is the backstop if both of those have a bug.
Large data tables and latency
Reporting views in SpinFlow.ai can return tens of thousands of rows across a quarter of activity. Rendering that client-side as a single React list is a known way to freeze a browser tab. We paginate and virtualize on the server side, so the client only ever holds the rows currently visible plus a small buffer.
Latency budget matters more once you are streaming AI output and paginating large tables in the same interface. A slow database query on one panel should never block a fast one on another. We isolate loaders per route segment so one expensive query cannot stall the rest of the page.
Why we built it on the stack we sell
We could have shipped SpinFlow.ai faster on a simpler stack and sold clients something different. We did not, because the platform work is where we find the failure modes before a client does. Every constraint described above, streaming cancellation, row level security, edge cold starts, was a real bug or a real design meeting first.
This also keeps our sales conversation honest. When we tell a prospective client that a template platform cannot hold their application state, we are describing our own daily experience running SpinFlow.ai, not a claim we read somewhere.
What it proves
It proves the stack we recommend holds up under a workload with real users, real cost, and real consequences for a bug, not just a portfolio piece. It proves row level security at the database layer is workable in production, not just a talking point. It proves server-rendered streaming interfaces can be built without a client-side state management library carrying the whole app.
It does not prove every client needs this exact stack. A five-page brochure site does not need row level security or streaming. What it proves is narrower and more useful: when an application actually needs authenticated state, live data, and role scoping, this is the architecture that holds.
Decisions
The calls that shaped it
Each one had a real alternative. This is why we went the way we did.
Server loaders over a global client store
Each route fetches and owns its own data server-side. Slower to wire up initially, but state cannot drift between screens because there is no client copy to go stale.
Row level security at the database, not just the app
Postgres policies enforce tenant and role boundaries as a backstop independent of application code, so a bug in a route handler cannot leak another account's rows.
Edge runtime for streaming endpoints only
We did not move the whole app to the edge. Only the latency-sensitive streaming paths run there, because that is where cold start time is visible to the user.
Per-route data isolation
Expensive queries are scoped to their own route segment so a slow report cannot stall a fast dashboard card rendered on the same page.
What is SpinFlow.ai and why does Lingows use it as a reference build?
SpinFlow.ai is Lingows's own enterprise AI platform, used internally to run AI-assisted workflows against real business data. We reference it because its frontend runs on the same architecture we recommend to clients: TanStack Start, React 19, server rendering, Postgres with row level security, and edge functions for latency-sensitive streaming.
- Authenticated, role-scoped state across administrators, operators, and reviewers.
- Streamed AI output over an edge runtime chosen for low cold-start latency.
- Large reporting tables paginated and virtualized server-side.
- Row level security enforced at the database layer as a backstop to application checks.
What it proves
The takeaway
- Server rendering and streaming AI output are not in tension when the streaming path runs on infrastructure built for low cold-start latency.
- Row level security enforced at the database layer is workable in a live product, not just a theoretical best practice.
- A frontend can hold authenticated, role-scoped, real-time state without a heavy client-side global store, if the server owns the source of truth.
- The architecture we recommend to clients is one we depend on for our own operating product, not a stack we only describe in proposals.
Where this connects
Related work
Each link explains why it is relevant, not just where it goes.
Want the same architecture behind your own surface
Start with a diagnosis. You get the roadmap and the quote before anyone builds anything.