I've recently found myself as a lead developer on a Next.js project. There's a .NET API that acts as the backend, which is much more within my comfort zone, but I'm definitely finding myself on the less-prepared side of things on the frontend.
Server-side rendering has always been a bit mysterious to me: I struggle to properly grasp the benefit of something until I've got a use-case that's meaningful to me. With quite a lot of my experience being for business-facing enterprise web apps, the benefits Next.js adds are usually fairly low down on the priority list, so it was easier for me to dismiss it as just the latest fad in web development. Until recently.
Recently, something clicked. Fairly often, I'd have have an idea for a personal project and due to my experience as a full-stack dev it's not long before I start thinking about things like databases & integrations. This is what usually stops me; when an idea becomes multiple executable applications, not to mention their own CI/CD requirements, it feels like too much hassle for me to do in my spare time. It was only when I considered a Next.js application like two separate applications, backend and frontend, rolled into one I immediately found a use case that I was excited about.
My use case
I thought it would be cool for my website to show what I'm currently listening to on spotify. From their API docs it seemed like a relatively straightforward integration. When playing around with their sandbox I was horrified to learn that Five Little Ducks had made it into my top 5 songs of all time (don't have kids). All I needed to do was generate a client id / secret and start messing about with it, the only problem is I don't want to expose any secrets in my website.
In this case, it's quite easy to find the secrets by checking the network tab for network requests to the spotify API:
Now, I can't imagine you're going to do much more damage to my music library than my two-year-old currently is, but it still isn't good practice. I decided I'd migrate my React application to Next.js so that this integration could take place entirely on the server and I could keep my secrets just that: secret.
Migrating to Next.js
This guide for migrating my app to Next.js took me about 30 minutes, the only obstacles I had here was fixing eslint errors introduced when following the guide. Because my CI/CD pipeline just does npm run build
, I had my website migrated over to Next.js in no time. I've been developing long enough to know how rare it is for a migration to go this smoothly.
Job done, right? No. Just because I've migrated my app to Next.js doesn't mean the necessary parts are server-side rendered. Indeed, those pesky token requests continued to happen because I bundled my whole App
in the client using the "use client"
directive.

I reached a fork in the road here where I felt I needed to choose between the following:
- Move the boundary between server and client to a higher level so I can be pickier about which components are rendered where. At the point of writing, the only responsive part of my application is related to the floating objects (see here, and the 'fun fact' shuffle thing; the rest is just static text.
- Use server actions to make an authenticated request with spotify under the hood without exposing the configured secrets.
I decided on the latter because that pretty much solves my problem entirely without rearchitecting my (albeit small) application. Despite most of my website being static text; I reckon rendering all that on the server-side sounds like a problem for future Owen.
Adding an API route
Next step was to make a http request to the next.js backend for it to make an onward authenticated request with spotify. I create the following file at src/app/pages/api/test.js
:
export default function handler(req, res) {
res.status(200).json({ message: 'Hello from the backend!' })
}
Not so fast, computer says no:
⨯ API Routes cannot be used with "output: export". See more info here: https://nextjs.org/docs/advanced-features/static-html-export
Indeed, removing the output: 'export'
in my next.config.mjs
file sorted the issue. As much as I'd love to tell you I had some well-founded reason for doing this other than just to get it to work, I'm just trying to prove the concept at this page:
Now if my years of software engineering have tought me anything it's to be wary of making changes to files containing the word config
, so I pushed the commit and checked I could still deploy the site. I had already checked I could deploy since the next.js migration, so this would prove that this change to the config file caused any potential issues. My hunch, sadly, paid off:
Serves me right for blindly changing things without understanding the implications of doing so. So what did I actually do, and how did this break the site? Let's compare the output directory when removing this from the config:

LHS is with output: 'export'
, RHS is without. Crucially, the left hand side has an index.html
, which explains why previously the site would load without a 404
. Adding the following netlify.toml
file resolved this issue for me:
[build]
command = "npm run build"
publish = ".next"
[[plugins]]
package = "@netlify/plugin-nextjs"
Hey presto, the API route is working on production:
Now I can replace this hello-world endpoint with an implementation to the spotify API, and my client-side just call my new API route without exposing any secrets:
.> Edit: I noticed that the API route would never change when I skipped the song on spotify, which wasn't ideal. Looking into it, I found that it was because I wasn't using Dynamic APIs, and adding `cache: 'no-store' fixed the problem!
Final touches
Now I've got a working API route, with secrets nicely hidden. All that's left is to throw together some questionable CSS and get something pretty showing on my homepage. I won't go into the weeds of that, but you can take a look for yourself: