How do you connect a simple front end to a typed API?
Teams waste months arguing frameworks when the real question is simpler: how does the browser talk to the source of truth without lying about shapes and permissions? You can answer that with a heavy SPA and a custom backend. You can also answer it with a thin UI and a typed API that stays honest as the product grows.
This is the seam between "ship a screen" and "own a system."
Start from the contract, not the widgets
Pick the contract first:
- What resources exist?
- Who can read or write them?
- What does an error look like?
- Which fields are stable enough to generate types from?
UI libraries come and go. A muddy contract stays muddy in every library.
How types flow from the database to the client covers the vertical slice. Here the focus is the front edge of that slice.
Pattern A: schema driven HTTP
If your API is mostly table shaped, PostgREST or a Supabase style layer can expose Postgres directly. The front end calls HTTP. Types can be generated from OpenAPI or from a shared schema package.
This shines for admin tools and internal products. It struggles when the screen needs a choreography the database should not own.
Pattern B: small API, shared types
A modest Node or Bun service owns routes. Types are shared from a package or generated from Zod, TypeBox, or similar. The front end imports those types or OpenAPI clients.
You pay for a thin backend in exchange for a place to put workflows, webhooks, and rules that are not row shaped.
Pattern C: HTML first with progressive enhancement
A simple front end does not have to mean a giant client runtime. Server rendered HTML plus small bursts of JavaScript (or HTMX style swaps) can talk to the same typed API. The browser gets documents and fragments. The API still enforces auth and shapes.
This site uses that philosophy for content. Product apps can use the same instinct: keep the client humble when the server already knows the truth.
What "typed" should mean to a founder
Typed does not mean "we use TypeScript somewhere." It means a change in the data contract fails loudly in CI before customers feel it. If your UI can send fields the server no longer accepts and nobody notices until production, you do not have a typed boundary. You have vibes.
A practical wiring checklist
- Auth before cosmetics. Tokens, cookies, or session headers with clear rules.
- One base URL per environment. Staging and production are not optional.
- Generated or shared types in CI. Break the build when the contract drifts.
- Error shapes the UI can show. Silent JSON failures train users to distrust the app.
- Idempotency on money and invites. Retries happen. Plan for them.
Choosing simplicity on purpose
If your team is small, prefer the least moving parts that still protect the contract. A simple front end plus a clear API beats a fashionable client that reimplements business rules in three stores.
When REST from the database is enough, use it. When it is not, read When is REST from the database enough? and put a real service on the awkward paths only.
Work with Kleto
I am James Cowan, a product engineer and the founder of Kleto. Kleto is a product engineering agency that ships production software from strategy through handoff. We help teams wire thin clients to honest APIs without accidental architecture. If that matches your stack, contact Kleto and we will scope a sensible first step.