Getting rid of Redux / apollo-link-rest with Next.js

written by daniel in frontend on 26 Mar 2020

TL;DR I ditched Redux in favor of Apollo. This is how I set up apollo-link-rest.

Having worked with a Next.js setup with Apollo, Redux and a ton of other libraries recently, I grew somewhat impatient with the seconds-long hot-reload build times, minutes-long production builds and a 500 MB+ Docker context. So, while setting up a frontend stack for a work project recently, my #1 aim was to make it as light-weight as possible. This led me to reconsider every dependency in my setup.

Now Redux is not a great example of a bloated library, with its 348 KB including all dependencies. But it does add complexity when mixed together with Apollo and SSR. I reviewed what I actually used Redux for in my last project and decided to cut it.

Redux use cases

I found that I had these two use-cases for Redux:

For global state, Apollo was an obvious choice. Apollo supports client-side state management out of the box since Apollo Client 2.5. At first I found using queries and mutations to manage state somewhat cumbersome, but it’s not too too bad and there are bright sides as well: no actions, resolvers instead of reducers, being able to query local state in the same query as remote data. And from my (brief) experience, there is only so much global state to manage anyways. Authentication, global notifications, and… that’s it? Most of the time I try to keep state in components.

As for async logic - most of my thunks did not need access to Redux store anyway and could therefore be replaced with async event handlers. A custom hook will take care of the rest.

Handling REST requests

I decided to give apollo-link-rest a try, since I had Apollo already set up. Apollo-link-rest allows you to write GraphQL queries against REST endpoints. Because it’s an Apollo link, there is no need for separate error and authorization handling in addition to your GraphQL queries — apollo-link-error and apollo-link-context work for both GraphQL and apollo-link-rest requests.

The setup is as simple as creating a RestLink and adding it to you Apollo client. I’m using Next.js ^9.3.1 with @apollo/client ^3.0.0-beta. This is my createApolloClient function based on the with-apollo example by Next:

import { RestLink } from 'apollo-link-rest'

// …

const restLink = new RestLink({
  uri: process.env.REST_URL,
})

// …

return new ApolloClient({
  ssrMode: Boolean(ctx),
  link: ApolloLink.from([authLink, errorLink, restLink, graphqlLink]),
  cache,
  typeDefs,
  resolvers: {},
})

This is the example Next.js + Apollo setup with an extra authLink and errorLink, and typeDefs and resolvers for local state management.

Beware of ordering of the links — HttpLink eats everything thrown at it and therefore needs to be last in the list. We want to have AuthLink and ErrorLink applied to the other two, so they go first.

Aaaand it just works! Great.

Screenshot of ReferenceError: Headers is not defined

Turns out SSR can reliably break just about anything at all. Apollo-link-rest is apparently not intended for SSR use and relies on the browser Headers API which is missing in our Node.js environment. Let’s use Header from node-fetch as a polyfill: yarn add node-fetch

// polyfill Headers API server-side for apollo-link-rest
// https://github.com/apollographql/apollo-link-rest/issues/182#issuecomment-453209304
if ((global as any).Headers === null) {
  // eslint-disable-next-line @typescript-eslint/no-var-requires
  const fetch = require('node-fetch')
  ;(global as any).Headers = fetch.Headers
}

And there we have it 🥳 We can now do this:

query TestRestQuery {
  healthcheck @rest(type: "Healthcheck", path: "/healthcheck") {
    status
  }
}