This year marked the beginning of a fundamental shift in the way I build applications. Thanks to GraphQL and a technique known as universal rendering (also known as isomorphic rendering), I’ve completely changed the way I interact with and render data inside my applications for the better.
This shift, however, was not without its challenges. When I first adopted GraphQL and universal rendering in my development practice, I found myself spending far more time setting up build processes than I was building actual applications. There were so many added complexities I had to deal with for the sake of performance.
Apollo is a GraphQL client that’s concerned exclusively with the data layer of our application; it cares about efficiently fetching our data. Next, on the other hand, is a minimalistic framework for server-rendered React applications that’s concerned exclusively with the UI layer of our application; it cares about efficiently rendering our UI.
In this post, I’m going to attempt to cover the merits of using Apollo and Next independently, followed by the amazing performance and productivity gains they provide if used in tandem.
First off: Apollo.
To understand Apollo, we must first understand GraphQL.
GraphQL is a technology spec released by Facebook in 2015 that has the potential to replace REST. It’s a query language for our API that makes it easier to declaratively fetch and mutate data without having to worry about backend implementation details. Unlike a classic REST architecture, it lets clients fetch lots of related data in a single request.
Apollo is a GraphQLclientthatabstracts away a lot of the implementation details when querying a GraphQL server; this allows us to concentrate on what we do best: writing our application. It also provides all the amazing features GraphQL enables, such as caching, optimistic UI, subscriptions, pagination, prefetching, and more.
Here’s how to fetch data with Apollo inside a React application:
Neat, right? We can now effortlessly fetch the exact data we need for our component and put our queries exactly where we need them.
Now that we have this amazing new way to fetch data, what’s the best practice for rendering it inside our application? This is usually where we’re forced to make a choice: We can either write a single-page application (SPA) and sacrifice performance and (potentially) SEO, or we can write a server-rendered application and sacrifice interactivity, user experience, and developer experience. Seems like a lose-lose, doesn’t it?
The first thing I’m compelled to point out is a fairly common false dichotomy. That of “server-rendered apps vs single-page apps”. If we want to optimize for the best possible user experience and performance, giving up one or the other is never a good idea.
The good news is, thanks to universal rendering, we no longer have to give up one or the other. We can now enjoy the best of both worlds.
There are many reasons to universally render your application, but they mostly boil down to performance and user experience.
A single-page application requires many additional round trips to fetch scripts, styles, and subsequent API requests on initial page load, often leaving our users staring at a loading indicator until the app eventually renders on the client. Server rendering the initial page load requires far fewer round trips and gives our users instant feedback.
In addition to an improved perceived load time, universal rendering unlocks some cool user experiences. Check out the following experience:
Notice that a single route can be attached to two distinct components depending on where a route originates. When originating from the client, our component gets rendered inside a modal, however, when originating from the server it gets rendered independently. Having the ability to determine which environment our users are coming from allows us to serve them more congruent user interfaces.
Universal Rendering Is Hard…Until Now
package.json file in our project root and install packages inside a directory called
./node_modules. We do this because certain tools, like NPM, make assumptions about our project structure in order to make our jobs easier. Next makes an assumption of its own by introducing a
pages/ subdirectory for our top level components. With a little magic behind the scenes, every
.js file inside
pages/ becomes a route that gets automatically processed and server-rendered.
pages/index.js gets mapped to
pages/about.js gets mapped to
Using the file system as an API is a wonderful default. Not only does it give us server-rendered pages, but by removing the boilerplate associated with routing and server-rendering it increases our development velocity dramatically as well. I believe in a year from now a
pages/ directory will become as commonplace as a
./node_modules directory and a
There are a ton of other features that make Next mind-blowingly awesome, such as automatic code splitting, prefetching, and hot code reloading, but let’s move on to the crux of this post: you should most definitely be using Next with Apollo!
Apollo + Next = ❤
Part of what makes Apollo so flexible is that it makes no assumptions about our stack. It’s concerned exclusively with the data layer. This makes it a perfect companion to Next, a framework that makes no assumptions about the data layer!
When I realized this, the first thing I did was to search the web for best practices on how to render data with Apollo inside a Next application. But to my surprise, there were none. The Next framework was still so new.
*Note that my example features my preferred CSS-in-JS solution, Emotion, whereas the one I submitted to the Next examples repo features styled-jsx, the CSS-in-JS solution that comes bundled with Next (for those who prefer a more traditional CSS syntax).
My goal with this example was to make it as simple as possible to use Apollo inside a Next application. I abstracted all the implementation details that go into initializing the client into a sub-directory called
./lib so that all we have to do is point to our GraphQL server inside
./lib/initApollo.js. Anytime we want to fetch data from our GraphQL server, we simply wrap our top level pages with a higher order component like so:
This allows us to safely write GraphQL queries inside any component declared inside a universally rendered page. It’s that easy. Are you feeling powerful? You should. This is powerful stuff!
How It Works
Behind the scenes, when we wrap our top level component, we pass down a central store of query result data created by Apollo on the server into our React component hierarchy.
We’re able to accomplish this thanks to a special lifecycle method provided by Next called
getInitialProps, an async static method that enables us to asynchronously fetch data while on the server and, as the name suggests, populate our props.
getInitialProps, we take advantage of an Apollo method called
getDataFromTreewhich returns a promise and recursively checks our entire component tree for GraphQL queries to execute. At the point where the promise resolves, our Apollo store is completely initialized and ready to be handed off for store rehydration on the client.
Remember, while it’s important to understand how this works behind the scenes, all that logic is abstracted away in the example’s
./lib sub-directory. All we have to do to get started is point our application to our GraphQL server and import a single, higher-order component.
In many ways, Apollo and Next are two sides of the same coin. Apollo is the best-in-class tool for interacting with data, while Next is the best-in-class tool for rendering it. Together, they represent the culmination of a collective pursuit to empower front-end engineers to build delightful applications with ever-greater performance, development velocity, and user experiences…all without sacrifice.
Stay in our orbit!
Become an Apollo insider and get first access to new features, best practices, and community events. Oh, and no junk mail. Ever.
Make this article better!
Was this post helpful? Have suggestions? Consider so we can improve it for future readers ✨.