This is a translated version of the original post published in my Spanish blog. The translation was generated using ChatGPT and manually reviewed for clarity and accuracy.
When I set out to build my personal blog with Next.js 15, I was excited to finally take advantage of the famous ⨠Server Components āØ. I had never really given them a proper try before, and I thought, āThis is the future.ā What I didnāt know is that I was about to step into a world of bugs, broken builds, missing sessions, and documentation that was⦠confusing?
This post isnāt a tutorial ā it's more of a story about how not to lose your mind when things donāt go as expected with Server Components, and how (unsurprisingly) AI can be overly agreeable unless you steer it toward the actual solution. Spoiler: I ended up learning more than I thought.
The first problem: the fetch that never came
It all started when my homepage, where I wanted to show the latest five blog entries, just⦠didnāt render anything. For context, I was using Next.js API routes with the App Router to handle the "backend" side of things.
The homepage is a server component generated statically at build time. It tries to fetch the latest posts via an internal API⦠but when I deployed it to Vercel, the build logs threw this error:
ā Internal Server Error
Error: Internal server error at a (.next/server/app/page.js:1:5127) at async p (.next/server/app/page.js:1:3031)
The build didnāt fail entirely, but the log was concerning. When I checked the live site, the list of posts was completely empty.
I thought this project would be "simple", but here I was ā asking ChatGPT, then Grok because ⨠free tier āØ, taking a coffee-break āļø when I finally remembered that I do have a brain, and that back in the day we used Google and StackOverflow to solve things. Then came the eureka moment:
How can I expect a static page to fetch posts from an API that **isnāt running yet?
It made total sense. Locally, the Node server is always running, so API routes are available. But during a deploy, when npm run build
executes, thereās no server running yet ā thereās nothing to fetch from.
The solution: I had to query the database directly from the Server Component. It feels like Iām breaking an abstraction, but it worked.
Session confusion
On another page, I needed to fetch an individual post. In this case, fetching from the internal API worked fine ā it was also a Server Component, but the key difference is that this route is dynamic. It fetches data when someone visits the post. The server handles the query and sends back the full page. So far, so good.
The issue came when I needed to validate some logic based on the user's session. I kept trying different approaches, but the session inside the API routes always came as null
.
And again⦠failed attempts asking an AI to solve my life. Something I couldāve solved if I had just read the official docs š
Still, I kept trying with Grok, trying to guide it toward the right idea. Hereās the prompt I used (donāt judge my broken English ā I promise that I can do better, but since this thing is smarter than me, I don't bother to correct my grammatical issues š¤£):
it didn't work, still coming as null, but doesn't this have to do that I am using an internal API route to fetch a post, but this post is being fetched in a server component. I just tried to get the session data in the fetch itself and not inside my API routes and it worked
I know what you're thinkingāI shouldāve told the AI all of this context from the start. But in my experience, if you throw too much info at once, it ignores half of it and replies with whatever it wants anyway...
The solution: When doing fetch from a Server Component, the browserās cookies šŖ are not sent automatically. And without cookies, thereās no session. You have to pass them manuallyāand thankfully, Next.js has the tools to do just that.
Note: This depends on the type of authentication your app uses. I use
JWT
(JSON Web Token) along with NextAuth.js, which stores session data in cookies.
import { cookies } from "next/headers";
const cookieStore = cookies();
const cookieString = cookieStore
.getAll()
.map((cookie) => `${cookie.name}=${cookie.value}`)
.join("; ");
const response = await fetch(`http://localhost:3000/api/post/${params.id}`, {
headers: {
Cookie: cookieString,
},
});
ā ļø Docs - 1 | AI - 0
Since I'm apparently becoming a pseudo "vibe coder", let me highlight this moment where AI didnāt give the best answer.
In Grokās response, it built a cookie string using.map
and.join
, which works. But if you look at the official Next.js docs, youāll see thecookies()
method already has a built-in.toString()
that does this for you. So yeah... always double check šļø
So⦠what did I learn?
Server Components are great, but full of gotchas. You need to understand where and how your code runs.
Fetching your own API vs. querying the DB directly from Server Components is a nuanced decision. Context matters.
Cookies aren't sent automatically with server-side fetch. If you need session data, pass cookies manually.
Don't blindly rely on AI. Sometimes the old-school way (reading docs) saves you hours.
Things I could improve
It still feels a bit off to query the DB directly from a Server Component. If you also feel that ⨠itch thing ⨠when something seems wrong⦠trust it.
Every time Iāve ignored that gut feeling, I get code reviews or manager feedback pointing out exactly what I already suspected. Iām working on that ā and this is a great chance to put it into practice.
š¬ I was looking through my Twitter bookmarks and found this tweet from @asidorenko_ explaining why you should separate your DB calls. I knew Iād read that somewhere.
Pro tip: when it comes to frontend, anything can be exposedāso be careful.
Uncle Juan's final thoughts
I remember when Server Components first showed up, the very nice and not-at-all toxic Twitter / X community was comparing them to old-school PHP, calling it a step backwards.
Personally (as with most tech trends), I didnāt jump on the hype train right away. I resisted Tailwind, daily AI use, search engine replacements, and Server Components (yepāmy old Next.js projects always started with use client
at the top). But eventually, I gave in. I tried them. And I liked them.
My point is: trying new things expands your understanding and helps you face real challenges. It's a good practice to reflect on the why behind what you're doing. Push yourself ā try applying a design pattern or avoid using libraries for everything.
I once read (maybe on Reddit or Twitter) that a common question is: āWhen should I use use client
?ā The answer was simple: use server components by default, and only move the interactive parts to client components when absolutely necessary ā even if it feels silly.
That advice stuck with me. And building my blog from scratch gave me the chance to follow it. It helped me write better component compositions (funny enough, thereās a React pattern called Composition that aligns perfectly with this approach). If I had just slapped use client
everywhere, I wouldnāt have run into these issues ā or learned from them.
Thatās why experience matters when companies hire. Itās not just about knowing the concepts ā itās about the challenges youāve faced and how you solved them. And that only comes from working on real-world projects that go beyond a JS calculator or a basic React to-do list.
š¬ While writing this post, I found another tweet by @asidorenko_ explaining how to separate the interactive part into a new component. I swear Iām not sponsored by him ā go follow him if you're into Next.js content.
Also, look into the Composition pattern. Now that you've made it this far, you know it exists. Youāve probably already used it without realizing it. Being aware of it, studying it, and applying it consciously will score you points in interviews. š
At the end of the day, this post is more of a war diary than a tutorial. But if I helped you save even a couple of hours of debugging, then it was all worth it.
Have you also wrestled with Server Components? Let me knowāIād love to hear your experience š
Suggestions and corrections are welcomeāI'm far from perfect, but Iām not afraid to be transparent and show my thought process when tackling problems.
Top comments (0)