Two years ago, I made a decision that significantly simplified my projects: clearly separating the frontend from the backend. My interfaces live in Next.js, while my APIs remain responsible for business logic and data access.
This is not mandatory for every project. For me, it becomes especially valuable when an application has multiple clients, significant business logic, or several teams working on different layers.
Separating responsibilities
Frontend — Next.js
The frontend focuses on user experience:
- rendering and navigation;
- React components;
- server-state management;
- forms;
- client-side validation;
- loading states and errors;
- accessibility.
With TanStack Query, remote data is treated as server state instead of a collection of scattered useEffect calls. React Hook Form and Zod keep forms typed and predictable.
Backend — Laravel, Django, Express, or NestJS
The backend becomes the source of authority:
- authentication and authorization;
- business rules;
- transactions;
- PostgreSQL access;
- external integrations;
- server-side validation;
- observability.
The frontend should never decide that an operation is authorized simply because a button is visible.
A clear API contract
The key to decoupling is the contract between both worlds.
A request should have a predictable shape:
POST /api/orders
Content-Type: application/json
Authorization: Bearer <token>
The response should be stable enough for the frontend to evolve without knowing the internal database structure.
I prefer exposing business resources rather than directly mirroring SQL tables.
Decoupling does not mean duplication
A common mistake is copying business logic into React.
The frontend can perform ergonomic validation to show immediate feedback, but the backend must always validate the data again.
The rule is simple:
The frontend improves the experience. The backend guarantees integrity.
Why keep multiple backend frameworks?
I do not consider Laravel, Django, and NestJS absolute competitors.
Laravel / Django are excellent when I want to move quickly with mature ecosystems.
NestJS / Express are particularly useful when I want to stay entirely within the TypeScript ecosystem.
The frontend remains relatively independent from the backend choice.
When I prefer this architecture
I favor this separation when:
- multiple applications consume the same API;
- the business domain becomes significant;
- frontend and backend should evolve independently;
- a mobile application is planned;
- multiple teams need to work without blocking each other.
For a small internal CRUD application, a monolith can of course be simpler.
My hybrid architecture
The best compromise is not necessarily “decouple everything.”
I can keep a backend monolith while exposing a clean API to the frontend. This creates a strong logical boundary without turning the project into microservices.
That is the approach I prefer: separate responsibilities while keeping complexity under control.
