ADMN
Laravel API + Next.js

Preview environments for Laravel + Next.js

Two services, one ephemeral environment per pull request: a Laravel API and a Next.js App-Router frontend running as a real Node server. Built, migrated, seeded and wired — the API base URL resolved for both Server Components and the browser — behind a single HTTPS URL, then torn down clean.

  • App Router + RSC
  • SSR & ISR per env
  • Internal + public API URL
One request, two services

How a page renders in your preview

A decoupled stack has a server hop the browser never sees. Each preview wires that hop for you so Server Components hit the API directly while the browser uses the public origin.

  1. BrowserReact client

    Hydrated components call NEXT_PUBLIC_API_URL — the environment’s public HTTPS origin.

  2. Next.js · NodeSSR + Server Components

    RSC and route handlers fetch over API_INTERNAL_URL on localhost — no public round-trip.

  3. LaravelAPI + queue

    PHP serves JSON, runs the queue:work worker and the scheduler.

  4. DataMySQL / Postgres · Redis

    Isolated, migrate + seeded on boot; Redis for cache, sessions, queues.

Why previewing this stack is fiddly

A decoupled Next + Laravel PR is two deploys to coordinate

You are not standing up one app — you are standing up an API and a Next.js Node server that have to find each other. The API URL has to be right twice (server and browser), CORS or cookie domains have to line up, and any ISR cache from a previous run has to be gone.

Two deploys, kept in sync

Bump the API and the Next build separately and they drift — the frontend ships expecting a route the deployed API doesn’t have yet, on whichever staging box was free.

The API URL is wrong twice

Server Components want an internal address; the browser needs the public one. Set one and forget the other and you get hydration mismatches or server-side fetch failures.

Stale ISR & CORS surprises

A cached revalidate page shows last branch’s data, or cross-origin requests get blocked because the cookie domain and SANCTUM_STATEFUL_DOMAINS never matched the preview’s host.

Both services, fully wired

What every Laravel + Next.js preview includes

One VPS runs the PHP API and the Next.js Node server side by side, with backing services and the cross-service URLs set for you.

api

Laravel API

  • composer install, app booted under a real web server
  • migrate + seeders on isolated MySQL / Postgres
  • Redis, queue:work worker and the scheduler
  • APP_URL, fresh APP_KEY and DB creds injected
web

Next.js frontend

  • npm ci then next build (App or Pages Router)
  • Runs as a Node server — standalone output or next start
  • API_INTERNAL_URL for RSC, NEXT_PUBLIC_API_URL for the browser
  • SSR + ISR with a fresh data cache every environment
The build, automated

Both services, declared once and run on every push

You give us the commands for each service and the two cross-service URLs. We run them in clean containers — no bespoke CI, no SSH, no juggling two pipelines.

# api — Laravel
composer install --optimize-autoloader
php artisan migrate --seed     # isolated DB, realistic data
php artisan queue:work & php artisan schedule:work

# web — Next.js (App Router, standalone output)
npm ci
NEXT_PUBLIC_API_URL=https://acme-shop-pr82.admn.cloud \
API_INTERNAL_URL=http://127.0.0.1:8000 next build
node .next/standalone/server.js   # or: next start
✓ ready https://acme-shop-pr82.admn.cloud
How it works

From branch to a wired two-service URL

  1. 01

    Declare both services

    Point ADMN at your repo and set the build commands for the API and the Next.js app, plus your database template — once.

  2. 02

    Pick a branch

    We provision one VPS, build both services, migrate and seed the database, and inject the internal and public API URLs so the server hop and the browser both resolve.

  3. 03

    Share the URL

    A live HTTPS environment with SSR, ISR and a real API behind it. Renew the lease to keep it, or let it expire and tear down clean.

Questions

Laravel + Next.js preview environments, answered

App Router or Pages Router — does it matter?

Either works. We run your declared build (next build) and start the Node server, so whether you fetch data in App Router Server Components and route handlers or in getServerSideProps / API routes under the Pages Router, the preview behaves the same. The defaults below assume the App Router because that is where most new Laravel + Next teams are, but nothing here is App-Router-only.

How do Server Components reach the Laravel API?

Server Components and route handlers render inside the Node process on the same VPS as the API, so they call the API over an internal base URL — set a server-only variable such as API_INTERNAL_URL (e.g. http://127.0.0.1:8000) and read it in your server code. The browser never sees that value; client components use NEXT_PUBLIC_API_URL, which we point at the environment’s public HTTPS origin. Two URLs, one for the server hop and one for the browser, both injected automatically.

What happens to ISR and revalidate in a short-lived environment?

Each preview is a brand-new Node server with an empty Next data cache, so the first request to an ISR or revalidate route renders fresh against that environment’s seeded database — there is no stale cache carried over from another branch or from production. On-demand revalidation (revalidatePath / revalidateTag) and route handlers work normally, and the whole cache is discarded when the environment tears down.

Should I use output: "standalone"?

We recommend it. With output: "standalone" in next.config, next build emits a self-contained .next/standalone server you can boot with node server.js — smaller, faster to start and well suited to running as the long-lived Node process behind the preview’s HTTPS URL. If you do not set it we simply run next start instead; both are supported.

How does auth work across two origins?

Both patterns are covered. For token auth (JWT), the Next server attaches the bearer to its internal API calls and the browser stores/sends it as you already do. For Sanctum cookie auth, the API and frontend share the environment’s domain, so first-party cookies and CSRF flow cleanly; set SANCTUM_STATEFUL_DOMAINS and the session domain to the generated host, which we expose in the env. No third-party-cookie workarounds needed.

Where does the database live?

With the API. Each environment gets its own isolated MySQL or PostgreSQL instance that we migrate with php artisan migrate and seed on first boot, plus Redis for cache, sessions and queues. The Next.js service never talks to the database directly — it only ever calls the Laravel API, exactly like production.

Ship your next Laravel + Next.js PR with a live URL

Connect GitHub and get both services — API and Next.js frontend — provisioned, wired and seeded for every branch, then destroyed without a trace.