Data Fetching in Next.js with higher-order functions
An advanced pattern that speeds up the development of pages by reusing common data and behavior requirements.
Originally published on Medium (Jun 6, 2022).
An advanced pattern that speeds up the development of pages by reusing common data and behavior requirements.
In web applications, different pages often share data requirements such as information for the currently logged-in user — and behaviors such as authorizing or not a user to see a page.
Imagine a social media app and consider that all the authenticated pages have the following requirements:
- The user must be logged in to see the content; otherwise, they are redirected to the login page.
- If the user is authenticated, they should see their information in the page header.

Now, this seems somehow familiar, right? You can easily see how many pages would implement this logic and how this can grow in complexity as you add more data and behaviors shared by authenticated pages.
The trivial Next.js way of doing this
With Next.js, a common way of fulfilling those requirements is in the server-side data fetching layer: getInitialProps, getServerSideProps, or getStaticProps. On an authenticated page, it is more likely you would use getServerSideProps, so let’s stick with that one.
Initially, a straightforward implementation could be:

This code is as simple as it seems: do a current-user lookup; if the user can’t be found, redirect to /login. In the end, the username is returned as a prop to the page so it can be displayed in the header.
Now, imagine you want to set a caching header in the response. The implementation becomes something like this:

At this point your getServerSideProps has 16 lines of code that will very likely show up on many pages, and they do not say anything specific about what the page does — it is just boilerplate.
Higher-order functions to the rescue
What if there was a nicer way to write the same code in a more declarative style and “collapse” the irrelevant details for the page at hand? You are in luck, because that exists, and you can do it by encapsulating small, shared pieces of getServerSideProps in HOFs (higher-order functions).

Before looking at the implementation, here is what it can look like once it is refactored:

Nice, huh? You have enriched getServerSideProps with authorization and caching with barely one extra line of code.
You have composed two HOFs: withAuthentication and withCache. The first handles authorization, injects the user into the context, and returns the username in props. The second sets the Cache-Control header according to the time argument you pass in (2h).
How to implement
There are three main pieces:
- The HOFs (the syntax and behavior)
- The
composefunction - The
nextfunction
The HOFs: withAuthentication

withAuthentication takes a function and returns another function. Both the argument and the return value match the getServerSideProps shape.
It runs the same authentication flow as before and calls a small next helper with the updated context and the props you want to return.
The HOFs: withCache

withCache takes an extra argument, cacheTime, which acts as configuration. Its return value works like withAuthentication: it is a function that takes a function, both in the getServerSideProps shape.
It sets a Cache-Control header for the given cache time and then calls next.
The compose

This looks a bit busy, but it is only function composition: it applies the result of each function to the next. By using reduceRight you start from the right, so the getServerSideProps defined in the page runs after everything else, with its context already enriched (for example with user).
The next utility

This utility takes a function, the context, and props. It applies the function to the context and merges the result with the given props. That is it.
The source for the code walkthrough was published with the original on Medium, including any linked demo the post references.
Writing with TypeScript
To write the same in TypeScript, the complexity goes up, because the functions above need generics and must line up with getServerSideProps’s type constraints. It is doable, but a separate post is a better place to walk through the TypeScript version.
Next steps
This code as an npm package
There is a work-in-progress package with utilities to build composable server-side data fetching in Next.js: github.com/fernandoabolafio/ss-compose. It is very early, so use it at your own risk — suggestions are welcome.
Naming ideas floating around: mixins, enrichers, augmenters. If you have a clearer name, say so in the Medium post’s discussion.
Remix
Remix loads data per route in a similar way, so the same idea should carry over to Remix loaders.
Wrapping up
Thanks for reading. Higher-order functions for server-side data fetching in Next.js have been useful in practice: they add clarity and let you focus on what is specific to each page instead of repeating the same cross-cutting behavior over and over.