Skip to content

Solution

When to build an API and when to build the web app

Almost every project eventually asks whether to build the API first. The honest answer depends on one thing, and it is not architectural purity, it is whether a second consumer actually exists.

The short answer

Build the web application first, with server rendered pages and server actions, when there is one client and one team. Build the API first when a second consumer already exists, when the consumers ship on different cycles, or when the API is itself the product.

What an API boundary actually costs

The discussion usually skips this, so it is worth listing what you take on the moment you introduce one.

Serialisation both ways. Every object leaving the server is shaped, and every payload arriving is parsed and validated. Twice the code for the same movement of data.

Validation twice. The client validates for the user experience. The server validates because the client cannot be trusted. Two implementations of the same rules, which will drift, and the drift shows up as a form that accepts something the server rejects.

Versioning, forever. The moment anything you do not control consumes the API, you cannot change it freely. Every future change carries a compatibility question.

Types across the wire. Either you generate them from a specification, which is machinery, or you hand maintain them, which is a lie waiting to be discovered at runtime.

A second surface to secure. Authentication, authorisation, rate limiting and audit on the API, in addition to whatever the application already has.

None of that is wasted if something needs the boundary. All of it is waste if nothing does.

Build the web application first when

There is one client and one team. A page renders on the server, calls a function, and returns HTML. No serialisation, one validation, no versioning, no generated types. Fewer moving parts for the same product.

The interaction is mostly forms and reads. Which describes most operational software honestly. A form posts, the server does the work, the page re-renders. That is a solved problem and it needs no API.

You are still discovering the shape of the data. This is the strongest reason. Early on, the shape changes weekly. Changing a function signature is a rename. Changing an API contract is a negotiation with yourself in two places.

You are one person. Every boundary is a place to context switch. Alone, boundaries cost more than they save until something forces one.

Build the API first when

A second consumer already exists. A mobile application in development, a partner integration under contract, a data pipeline that is already funded. Existing, not planned.

Consumers ship on different cycles. A mobile app cannot be deployed simultaneously with the server, and an old version will be in use for months. That constraint requires versioning, which requires a real contract.

The API is the product. If customers pay for programmatic access, it is not an implementation detail. It gets designed, documented and versioned first because it is the thing being sold.

Different teams own the two halves. The boundary is then also an organisational contract, and it is doing work beyond the technical.

The test that settles it

One question: can you name the second consumer, and is somebody currently being paid to build it?

If yes, build the API. If the answer is that one is planned, that is not a consumer. Plans change, and designing against an imagined client means designing against imagined requirements, which is how you get an API with endpoints nobody ever calls in the shape nobody ended up needing.

The middle path, which is what I actually do

Build the web application, and keep the business logic in a service layer that knows nothing about HTTP.

route or page action        thin, handles input and output
  -> service function       all the business logic, no HTTP knowledge
       -> repository        data access

The page calls the service directly. No network, no serialisation, no versioning.

When a real second consumer appears, the API is a thin adapter over services that already exist and are already tested.

POST /api/invoices  ->  parse, authorise  ->  the same service function

That is a day of work, not a rewrite, and crucially the business logic is not duplicated because it was never in the route.

The discipline this requires is small and specific: no business logic in a route handler, ever, and no HTTP types below the route. If a service function takes a request object, the boundary has already leaked and the day of work becomes a month.

The failure mode of getting it wrong each way

Building the API too early costs you speed during the period when speed matters most, and you end up maintaining a contract whose only consumer is your own frontend, which you also control, which means the contract is protecting you from yourself.

Building it too late, with logic spread through route handlers, means the second consumer arrives and the logic has to be extracted from a dozen places under time pressure, usually while the mobile team is waiting.

The service layer discipline is what makes the second failure cheap, which is why it is the actual recommendation rather than either extreme.

What good looks like

New features are written in one place. Adding a client later costs an adapter. No rule about where logic lives has to be remembered, because a service function that imports an HTTP type does not compile.

Questions people actually ask

Does building the web app first mean you can never add an API?
No, and this is the crux. Keep the business logic in a service layer that the page calls, and adding an HTTP surface later is a thin adapter over code that already exists. What you avoid is paying for the boundary before anything needs it.
What if a mobile app is coming next year?
Next year is not a consumer. Plans change, and building for a client that does not exist means designing against imagined requirements. Keep the logic separable and decide when the mobile work is actually funded.
Is this not just avoiding good architecture?
A boundary you do not need is not good architecture, it is cost. Serialisation, versioning, duplicated validation and a second set of tests are all real, and they are worth it when they buy something.

Published June 2026, updated July 2026.

Hiring someone to own work like this?

I am open to senior backend and full stack roles, remote and permanent.

Get in touch / Read the CV