March 23, 2017

What’s Next.js for Apollo

Adam Soffer

Adam Soffer

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.

I almost gave up until two open source projects, released within months of each other, were brought to my attention: Apollo and Next.

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.

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:

The `graphql` wrapper executes a GraphQL query and makes the results available on the `data` prop of the wrapped component (PostList here)

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?

Guillermo Rauch, creator of several popular Node.JS open source libraries like socket.iomongoose, and, most recently, Next, put it best:

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.

Universal Rendering

So what exactly is universal rendering? A universally rendered JavaScript application is characterized by a sequence in which an application’s first request made by the web browser is processed by the server and subsequent requests are processed by the client. It’s a technique made possible by universal JavaScript* — shared code that can run on the server and client.

There are many reasons to universally render your application, but they mostly boil down to performance and user experience.

Performance

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.

User Experience

In addition to an improved perceived load time, universal rendering unlocks some cool user experiences. Check out the following experience:

Image taken from https://github.com/zeit/nextgram

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

The concept of universal rendering has actually been around for a few years, but implementing it has been far from trivial — that is, until Next was open sourced.

*Note that there’s a distinction between universal JavaScript and universal rendering. Universal JavaScript refers to code that can run in two or more environments such as a server, browser, native device, or embedded architecture whereas universal rendering refers to a technique that enables the rendering of an application in two or more environments.

Next

Next is a small framework for server-rendered universal JavaScript web apps built on top of React, Webpack and Babel. It was developed by a few smart people over at a company called Zeit and it makes spinning up a universally rendered JavaScript application a total breeze. What makes it so easy to use is that it requires zero setup and makes clever use of the file system as an API.

Pages

Generally, when we create a JavaScript application, we create a 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.

For example, pages/index.js gets mapped to / with:

and pages/about.js gets mapped to /about with:

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 package.json file.

There are a ton of other features that make Next mind-blowingly awesome, such as automatic code splittingprefetching, 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.

So I got to work on my own example, and after a few days of working out the kinks I open sourced it here*. (It’s since been merged into the Next examples/ directory as an official showcase.)

*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).

The Example

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:

The `withData` higher-order component passes down a central store of query result data created by Apollo into the React component hierarchy

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 <a href="https://github.com/zeit/next.js#fetching-data-and-component-lifecycle" target="_blank" rel="noreferrer noopener">getInitialProps</a>, an async static method that enables us to asynchronously fetch data while on the server and, as the name suggests, populate our props.

While inside getInitialProps, we take advantage of an Apollo method called <a href="http://dev.apollodata.com/react/server-side-rendering.html#getDataFromTree" target="_blank" rel="noreferrer noopener">getDataFromTree</a>which 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.


Conclusion

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.

Written by

Adam Soffer

Adam Soffer

Read more by Adam Soffer