This is part of an ongoing series I am writing as I work my way through the modern web stack from a WordPress developer’s perspective. It is for people who, like me, have built on the web for years and still hit words in a modern stack they could not confidently define. The goal is broad literacy, not deep mastery: by the end you should be able to read any modern stack list and know what each piece does.
Each post comes with an audio companion (10-15 minutes, generated via NotebookLM) for gym or commute listening. Press play below if that suits you better than reading.
If you read the previous fifteen modules in order, you now know what Next.js, React, Drizzle, Supabase, Auth.js, Vercel, GitHub Actions, and Tailwind each are. You can describe what each tool replaces from the WordPress world. You can hold a conversation about modern stack without flinching.
There is still one thing you cannot do: read a real Next.js codebase and follow how all those pieces actually wire together. The component code is React. The database queries are Drizzle. The auth check is Auth.js. The deploy comes from Vercel. The data flows from Supabase. But how does one request from a browser turn into HTML that shows the user their dashboard? Where does the environment variable go? Who calls whom? In what order?
This is the keystone module. The integration tour. By the end of this post you will have followed one request from browser to database and back, and you will know where each tool in the stack actually fits.
A modern web request flows through more layers than a WordPress request does. To make it concrete, let me walk through one specific scenario: a logged-in user loads their dashboard page on a Next.js + Supabase + Drizzle + Auth.js + Vercel app.
The request lifecycle:
- Browser hits the URL.
https://app.example.com/dashboard - DNS resolves to Vercel’s edge network. The request lands on the nearest edge node geographically.
- Edge middleware runs. This is the first piece of your code. Auth.js’s middleware checks the session cookie. If the user is not logged in, the middleware redirects to
/loginbefore the page ever renders. - Routing to the server component. The page route is
app/dashboard/page.tsx. Next.js routes the request to this server component. - Server component executes. Your code runs on Vercel’s serverless runtime. It calls Drizzle to query the database.
- Drizzle issues SQL to Supabase. A network call from the Vercel region to Supabase’s region (ideally co-located).
- Supabase Postgres returns rows. Through Drizzle, the rows become typed TypeScript objects.
- Server component renders HTML. React renders the component tree to HTML on the server.
- HTML streams to the browser. Vercel sends the initial HTML response.
- Client components hydrate. Interactive parts of the page (forms, buttons) load their JavaScript and become responsive.
- User interacts. Clicking a button triggers a server action, which is a function on the server reachable from the client.
- Server action runs. Same flow as step 5-7, but kicked off from a button click rather than a page load.
WordPress’s version of the same request is dramatically simpler: Nginx → PHP-FPM → WordPress → MySQL → HTML → browser. Six steps. The modern version has twelve because the work is more distributed and the pieces are more specialised. The complexity is the cost; the benefits are everything we have covered in the previous fifteen modules.
What Lives Where
A typical project layout in 2026:
my-app/
├── app/ # Next.js App Router pages
│ ├── (auth)/ # Route group: login/signup pages
│ ├── dashboard/ # Authenticated dashboard
│ │ ├── page.tsx # Server component, queries Drizzle
│ │ ├── loading.tsx # Loading skeleton while page loads
│ │ └── actions.ts # Server actions for this route
│ ├── api/ # API routes (REST endpoints)
│ ├── layout.tsx # Root layout, wraps every page
│ └── globals.css # Tailwind directives + custom CSS
├── components/
│ ├── ui/ # shadcn/ui components (Button, Card, etc.)
│ └── dashboard/ # App-specific components
├── lib/
│ ├── db/
│ │ ├── schema.ts # Drizzle schema (tables as TypeScript objects)
│ │ ├── index.ts # Drizzle client instance
│ │ └── migrations/ # Generated migration SQL files
│ ├── auth.ts # Auth.js configuration
│ └── utils.ts # Shared helpers
├── public/ # Static assets served as-is
├── drizzle.config.ts # Drizzle Kit configuration
├── next.config.mjs # Next.js build configuration
├── tailwind.config.ts # (or CSS-based config in Tailwind 4)
├── tsconfig.json # TypeScript configuration
├── package.json # Dependencies and scripts
└── .github/
└── workflows/
└── deploy.yml # GitHub Actions CI/CD pipeline
Each folder maps to a tool or a concern:
app/is Next.js (routing, server components, server actions, API routes).components/ui/is shadcn/ui (copied component primitives).lib/db/is Drizzle (schema, client, migrations).lib/auth.tsis Auth.js (session strategy, providers, callbacks).tailwind.config.tsplusglobals.cssis Tailwind..github/workflows/is GitHub Actions (CI/CD).
You can read any modern stack repo by mentally mapping its folders to this layout. Some details vary, but the shape holds.
Environment Variables: the Glue
The single most confusing topic for WordPress veterans is how secrets flow through the stack. In WordPress, secrets live in wp-config.php on the server. Done.
In a modern stack, you have at least three places secrets need to exist:
Local development. .env.local in the project root. Listed in .gitignore. Never committed. Contains your local Supabase keys, Auth.js secret, Stripe test keys, etc.
Vercel (or your hosting platform). Each project on Vercel has an “Environment Variables” panel. You set the production values there (production Supabase keys, production Auth.js secret, etc.). Vercel injects them when functions run.
GitHub Actions secrets. If your CI pipeline needs to access production services (e.g., to run database migrations during deploy), the credentials live in GitHub’s secret store, scoped to the repo.
The same secret might exist in all three places with different values: a development Supabase URL locally, a staging URL in Vercel’s preview environment, a production URL in Vercel’s production environment.
A few rules that help:
- Always prefix secrets with
NEXT_PUBLIC_only if they are meant to ship to the browser. Otherwise they stay server-side. - Use a service like Doppler or 1Password’s Secrets Automation if you have a team. Manual copy-paste between Vercel’s UI and
.env.localfiles gets out of sync fast. - Document what each environment variable is for in a
.env.examplefile committed to the repo. New developers should be able to clone the repo, copy.env.exampleto.env.local, and fill in values from the team’s password manager.
The Deploy Flow, End to End
A typical “I pushed code to main” lifecycle:
- You push to GitHub. A branch or main.
- GitHub Actions kicks off. The workflow in
.github/workflows/deploy.ymlstarts running. - CI runs. Install dependencies, type-check, lint, test, build. If anything fails, the pipeline stops.
- If the branch is a PR, Vercel creates a preview deploy. A unique URL per PR (e.g.,
https://my-app-git-feature-team.vercel.app). The PR comment updates with the preview link. - You review the preview. Click around, test the feature, share the URL with the client.
- You merge the PR to main.
- GitHub Actions runs again on main. Same CI steps as before.
- Vercel deploys to production. The production URL updates atomically. No downtime.
- Sentry receives a release marker. Errors after this deploy get tagged with the new release version.
- You watch the metrics for ten minutes. If something breaks, you can revert in Vercel’s dashboard with one click; the previous deploy comes back.
Compared to WordPress’s “FTP files, hope for the best, refresh production to see what broke,” this is genuinely different. The shape of the work changes once you have it.
What This Changes for WordPress People
Once you have the integration model in your head, three things compound.
The first is your reading of any modern repo gets dramatically faster. You stop being lost in the file tree because you know what each folder is. You stop wondering where the database lives because you know to look in lib/db. You stop being confused by deploy pipelines because the YAML is just a structured version of what you would do manually.
The second is your scoping conversations with clients change. You can say “this needs Auth.js for the auth, Drizzle on Neon for the database, Stripe for billing, Vercel for hosting, GitHub Actions for the deploy pipeline” — and have a real conversation about each piece’s cost and trade-offs, instead of nodding through the vocabulary.
The third is that AI dev tooling becomes genuinely useful. AI agents are extremely effective at producing modern stack code because the patterns are so consistent. Once you know the patterns yourself, you can review and direct AI-generated code competently. Without modern stack literacy, AI tools produce output you cannot evaluate.
The cross-cutting traps
A handful of cross-cutting traps to brace for.
Region mismatches kill performance. Vercel functions in iad1 and a Supabase database in eu-west-1 introduce 100-200ms of round-trip latency on every query. Pair the runtime and the data layer in the same region.
Environment variable drift kills your weekends. A .env.example file that gets out of sync with reality is worse than no file at all. Audit the env vars every release.
The “works locally, breaks in production” class of bug is real. Server components run differently in local dev (longer-running Node process) than in production (cold-starting serverless function). Test against a Vercel preview deploy before merging to main, not just locally.
Cost gets distributed and harder to forecast. Vercel + Supabase + Sentry + Resend + Clerk = five bills. Each has its own free tier, each has its own paid tier, each has its own way of spiking. Build a small cost model per project and revisit monthly.
Lock-in is gradual. Each tool you pick has its escape hatches, but escapes are work. Choose tools deliberately; cycle out the ones that have stopped paying for themselves, but resist swapping tools just because a new one trends on Twitter.
Learn it by reading code
If you want to internalise the integration model rather than just recognise it, the best thing to do is read code.
Repos worth opening:
- The Vercel templates gallery: vercel.com/templates. Clone “Next.js + Supabase + Drizzle” or similar and read it end to end. The starter repos are intentionally clean and show the patterns clearly.
- Any open-source SaaS-style project on GitHub built with Next.js. Search for “open-source SaaS Next.js” and pick a project with thoughtful structure. Reading other people’s code is the highest-leverage way to understand the integration.
YouTube, gym or commute friendly:
- “Build a Full-Stack Next.js App” by Lee Robinson or Code With Antonio (long, but worth it). Walks through every layer of a real project.
- “The T3 Stack” by Theo (t3.gg, various videos). Strong opinionated take on the modern stack and how the pieces fit.
Official docs worth bookmarking:
- Next.js docs on Vercel. The “Deploying” section ties together what Vercel adds beyond the framework.
- The 12-factor app principles. Old, but they describe the conceptual baseline that modern hosting was designed around.
This is the last module of the main course. From here you can read any modern stack codebase, hold any modern stack conversation, and pick the right tools for new projects. The next step is whichever side of this you find most exciting: shipping a small Next.js app yourself, hiring contractors who use this stack and knowing what to ask for, or learning the AI dev tooling layer that compounds with this literacy.
The vocabulary is yours. The map is yours. Time to put it to work.

Leave a Reply