Why we moved to GraphQL at Geckoboard

Hanna Nygård Hollström
Geckoboard: Under The Hood
7 min readOct 1, 2021

One of our long-running projects at Geckoboard is to replace one of our old APIs with a new GraphQL API — specifically the API that deals with dashboards and widgets.

Dashboards and widgets are at the core of our company. The product we develop at Geckoboard is a live dashboard tool that displays the users’ most important metrics and KPIs in real time.

Part of the reason Geckoboard is so popular is because our customers can easily create their own custom dashboards by building up a number of widgets to display their data.

Previously, we were using REST API, which was inflexible and hard to work with — it even began hindering us from developing new features.

For the front-end developers (like me) it was difficult because instead of being able to fetch the exact data I needed, it would dump an entire section of the database without clearly identifying the structure of the data or what each field represented.

It was also difficult to make any changes to the backend database because it wasn’t easy to see if, how and where any of the fields were being used.

We decided the solution was to build a new API using GraphQL.

Why GraphQL?

We wanted a new API that:

  • allows us to specify a data structure and create documentation that’s easy to access
  • is written in Node/Javascript so our front-end developers can easily design and make changes to the API — (this also means they can respond to client needs quicker)
  • only fetches the data customers need (so the product runs faster)

This all fits pretty well with the description of GraphQL:

“GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.”

Using Apollo Client

Switching to GraphQL also enabled us to use Apollo Client as part of our workflow.

Apollo Client is a comprehensive state management library for JavaScript that enables you to manage both local and remote data with GraphQL. You can use it to fetch, cache, and modify application data, all while automatically updating your UI.

We use Apollo Client to communicate with the GraphQL server in our React App, where we can make queries and mutations using React hooks.

Requests are cached and bundled automatically so that if we, for example, ask for a dashboard’s title in multiple places in the app, only one request is made to the network and subsequent requests return the title from the cache (unless directed otherwise). It also means we no longer need Redux to store data about dashboards and widgets — Apollo Client handles this for us.

As you can see below, it means there’s much less code for us to write.

With Redux:

// dashboard-reducer.js
// We use a redux reducer to store the title so that each
// component that wants to use it doesn't have to fetch it
// from the network.
export default (state = initialState, action) => {
const { type, payload } = action;
switch (type) {
case dashboardActions.setDashboardTitle.type: {
return {
...state,
dashboardTitle: payload,
};
}
}
};
// header-component.js
const HeaderComponent = ({ id }) => {
const dispatch = useDispatch();
// Read the title from the store in case we update it
// from other places in the app.
const dashboardTitle = useSelector(state => state.dashboardTitle);
const [dashboardTitleError, setDashboardTitleError] =
useState(null);
useEffect(() => {
const fetchDashboardTitle = async () => {
// Fetch title when component mounts if it's not
// been fetched yet
if (!dashboardTitle) {
try {
const dashboard = await oldApi.getDashboard(id);
const { title } = dashboard;
// Store the title in redux in case we access it
// from other places in the app
dispatch(dashboardActions.setDashboardTitle(title));
} catch (e) {
// If it fails, we just want to show an error
// in this component, so store it in state.
setDashboardTitleError(e);
}
}
};
fetchDashboardTitle();
});
if (dashboardTitleError) {
return 'Could not load dashboard title';
}
if (!dashboardTitle) return 'Loading'; return <h1>{dashboardTitle}</h1>;
};

With GraphQL and Apollo Client, we can do the same thing with less code and only needing the component file:

const HeaderComponent = ({ id }) => {
// Get the dashboard title from the GraphQL server.
// It will automatically be stored in the cache for other
// parts of the app to use, and if it's changed
// the component will update automatically.
const { data: { dashboard } = {}, loading, error } = useQuery(
gql`
query dashboard($id: ID!) {
dashboard(id: $id) {
id
title
}
}
`,
{ variables: { id } },
);
if (error) {
return 'Could not load dashboard title';
}
if (loading) return 'Loading'; return <h1>{dashboard.title}</h1>;
};

Designing the schema

At the centre of the GraphQL API and server is the schema. In the schema, we define what queries and mutations are available, their inputs and return types. Already, this is a big improvement on our old API because we can easily see the queries we can make and what they return. The schema also lets us create proper documentation via its comments feature.

To design the schema, a small team of front-end and backend developers worked together. We audited which types of data we already had, then designed an API based on how the client actually uses the data, and not on how it was stored or exposed by the current service.

In the past, when viewing a dashboard, we would effectively have to fetch a big blob of widget data. Whereas now we only fetch exactly what we need to render the dashboard. Thanks to the schema, it’s a lot easier to find out what data we can request, with documentation that provides far more meaning and context.

We found this resource to be very helpful when we were designing the schema.

GraphiQL

I couldn’t talk about the work we’ve done with GraphQL, schema and documentation, and not mention GraphiQL. GraphiQL is a development tool that you can install on a GraphQL server.

In GraphiQL, we can browse the schema along with its documentation. This is great for finding out what queries and mutations are available, what each type looks like and what their fields represent. We can also execute queries and mutations, along with autocomplete for all the fields. GraphiQL has been a great help in developing our API and trying out new features.

GraphQL puts our front-end developers in the driving seat

Writing our GraphQL server in Javascript has made it really easy for us front-end developers to work on the API. This has helped a lot when we want to develop a feature’s backend and front-end at the same time. We’re no longer stuck waiting for the API to be exposed and be returning data. Instead, we can create the queries and mutations we need and return mock data until the backend service is ready to be hooked up.

Our backend services are written in Go and expose gRPC endpoints. However one of the benefits of GraphQL is that the backend can be written in any language and expose any interface — and our front-end apps never have to know about them. All we need to do is query the GraphQL API and the GraphQL server takes care of fetching the data from the right places and formatting it into a response, based on the schema.

New features

We’re already beginning to see benefits in terms of the new features we‘ve developed. We’ve built a new platform, which makes it easier for us to add 3rd party integrations to the app. We’ve developed a dynamic new feature that allows users to group their widgets. And we’ve even developed a Slack integration in just a few weeks. With our old API, all of this would have been a lot more difficult for us and taken a significantly longer time.

What were the major difficulties?

The biggest challenge in replacing the API was its sheer size — because it’s so fundamental to our product, there was no way we could replace it all in one go. Instead we have migrated one part of the app at a time, which means we’ve had to keep two databases in sync. At the moment, most of the app reads from GraphQL, but a few mutations still go through it. So, for example, when we make changes to a widget, we have to trigger a change in both the legacy database and the new one, so that all places reading from GraphQL reflect the changes.

What’s next?

We’re still in the process of fully replacing the legacy API with the new GraphQL version. It’s been a long process, but that’s because our number one priority has been to make sure our customers haven’t been negatively affected by any of the changes.

It’s an exciting project — I love the fact it has many parts that all need to fit together. As we move more toward GraphQL, we’re designing new schema and mutations based on user actions. We’re going to redesign some old React components, which had to deal with the old, confusing, data structure. Now they can query GraphQL for the exact data they need instead. This will all keep improving our workflow and make it easier to work with the front-end app as well improving old components and removing a lot of redux code we no longer need.

Visit our website to find out more about working at Geckoboard

--

--