At Revere we're creating a modern network across the commercial real estate industry — a large market network with similar data requirements to LinkedIn, Facebook, and other social platforms. Our graph of data includes employees, companies, commercial real estate deals, and much more. GraphQL has been great for building out our API and lets us iterate quickly on the frontend by removing need for tightly-coupled per-page APIs. We're using the incredible Next.js framework for our web app. Configuring the frontend build system, router, and lazy loading all take time away from product development, and our engineering team has been very happy with the quality of framework from the Next.js team. On top of these technologies we've been using Relay for data fetching throughout the app. It does a ton of heavy lifting and fixes many problems common when building a social network.
Revere Engineering has released a
relay-nextjs
, a library for
integrating Next.js with Relay Hooks. We've been using it on our platform for
a while now — first with the experimental releases of Relay Hooks and now using
the
production-ready version.
It's a lightweight library that wires Relay into the proper places for
server-side rendering, client-side navigation using React Suspense, and lazy
loading data.
When we first began developing our app we would write data fetching code using
the
getServerSideProps
API from Next.js. This worked pretty well for quite a while but we began to
notice a few issues:
Our engineering team began to look at GraphQL clients that we could use that eases these pain points. Apollo Client and urql both fixed #1 and #2, but Relay stands out as the client that fixed #3. Composing GraphQL fragments rather than making one large query allows each component to specify what data it needs and automatically update when the UI issues a mutation.
We started looking into Relay before the new Hooks API was officially released. Although the old API was workable the new hooks-based one was truly incredible. We started building on the new "unstable" foundation and found a few issues when trying to integrate it into our Next.js app. Relay was designed at Facebook and is used with their own in-house router and larger app framework so it makes sense that the core has no integration open-source solutions in a similar space, but to use it effectively we had to make sure it integrated well with Next.js. There are a few requirements our app needs that Next.js supports, and a few more to ensure that we can progressivly adopt Relay:
getServerSideProps
. We'd like to keep this behavior, otherwise it would be
a regression.After some deep research into both frameworks we were able to satisfy each of
these requirements. We abstracted this into a common library and open-sourced it
as relay-nextjs
. The main goal for
this library is to be the glue layer and not replace or paper over either
framework. We didn't want our developers looking at some (probably outdated)
internal documentation to learn how to use Relay or Next.js, instead just use
the open-source documentation. As such this library is limited in scope but
we've found it flexible enough to meet the requirements of our platform.
Let's see what an example page using relay-nextjs
looks like:
// src/pages/user/[uuid].tsx
import { withRelay, RelayProps } from 'relay-nextjs';
import { graphql, usePreloadedQuery } from 'react-relay/hooks';
import { getClientEnvironment } from './relay_client_environment';
// The $uuid variable is injected automatically from the route.
const ProfileQuery = graphql`
query profile_ProfileQuery($uuid: ID!) {
user(id: $uuid) {
id
firstName
lastName
}
}
`;
// relay-nextjs automatically makes sure this query is loaded.
function UserProfile({ preloadedQuery }: RelayProps<{}, profile_ProfileQuery>) {
const query = usePreloadedQuery(ProfileQuery, preloadedQuery);
return (
<div>
Hello {query.user.firstName} {query.user.lastName}
</div>
);
}
function Loading() {
return <div>Loading...</div>;
}
export default withRelay(UserProfile, UserProfileQuery, {
fallback: <Loading />,
createClientEnvironment: () => getClientEnvironment()!,
serverSideProps: async (ctx) => {
const { getAuthToken } = await import('lib/server/auth');
const token = await getAuthToken(ctx);
if (token == null) {
return {
redirect: { destination: '/login', permanent: false },
};
}
return { token };
},
createServerEnvironment: async (ctx, { token }: { token: string }) => {
const { createServerEnvironment } = await import('lib/server_environment');
return createServerEnvironment(token);
},
});
We can see that relay-nextjs
takes care of telling Relay to fetch our query,
managing when to show a loading state, and also gives us the necessary
configuration to allow implementing something like authentication. We have more
detailed examples and guides on how to set it up in your app in our
documentation.
We love Relay and GraphQL at Revere and are happy to share our work with the community! All of this code can be found on GitHub and we hope to release more libraries like this in the future. If this is all interesting to you and Revere sounds like somewhere you'd want to work please reach out to our development team — we'd love to hear from you!