Laravel as the API, Next.js as the Frontend
How to arrange the two frameworks as one system, what separating them actually costs, and when not to separate them at all.
The common arrangement: Laravel owns the data, the business rules and authentication and exposes them as JSON; Next.js owns presentation and user experience and fetches that data over HTTP. They run as two separate applications.
Who owns what
| Concern | Owned by |
|---|---|
| Database schema and migrations | Laravel |
| Business rules and validation | Laravel |
| Authentication and authorisation | Laravel |
| Page routing and navigation | Next.js |
| Presentation, animation, interaction | Next.js |
| SEO and meta tags | Next.js |
Where the data should be fetched
Calls to the Laravel API can come from a Server Component or from the browser. Both are legitimate, but they carry different consequences, and choosing without noticing is a common source of security problems.
- From a Server Component — server to server. The token never reaches the browser, and the page can be rendered complete before it is sent, which is good for SEO. This is the right default for data you display.
- From a Client Component — browser to Laravel. Necessary for data that changes after user interaction. The API then has to allow CORS, and whatever the request carries is visible to the user.
Authentication with Sanctum
Laravel Sanctum offers two modes that are easy to confuse. SPA mode uses session cookies and only works when both applications sit under the same parent domain — a good fit for internal applications. API token mode sends a token in the Authorization header, does not depend on domains, and suits a frontend and backend deployed far apart.
// Runs on the server: the token is never sent to the browser
async function getVisitors() {
const res = await fetch(`${process.env.API_URL}/api/visitors`, {
headers: { Authorization: `Bearer ${process.env.API_TOKEN}` },
});
if (!res.ok) throw new Error(`Failed to load: ${res.status}`);
return res.json();
}What it costs
- Two applications to deploy, monitor and upgrade instead of one.
- CORS has to be configured correctly, and misconfiguring it produces browser errors that read as nonsense.
- The shape of the JSON becomes a contract between the two sides; changing it in Laravel can break the frontend with no warning at all.
- Authentication is more involved than a plain session inside a single application.