Product Blog

Quartz is (still) an API

In 2015, about three years into the existence of Quartz, Zach Seward — then Chief Product Officer, now CEO — wrote an article for Nieman Lab declaring that “Quartz is an API”. It’s a great read about the power of APIs, both as a metaphor and as a literal way to connect with our readers, and it still resonates with anyone who has worked at Quartz.

The first Quartz API, referenced in that piece, was ahead of its time. It was a JSON API built on top of WordPress — before the official REST API was even conceived — and its architecture was widely admired and copied. Sadly, all cutting-edge tech eventually grows weary and must be replaced and, last year, it was quietly and solemnly retired.

Don’t fret, though: we replaced it with something even better! Quartz’s new API is powered by GraphQL, a robust query language that gives API consumers even more capability.

An example app

We believe that the best way to interact with our new API is to build a small application that connects to it. This post will walk you through that process — in about ten minutes, you’ll start to see the power and flexibility Quartz’s API.

We’ll be using React and JSX to render our app. React development can be intimidating, but if you’ve tinkered with JavaScript and the command line before, I promise you’ll be able to follow along. First, you’ll need NodeJS version 10 or higher installed on your computer.

Instead of using the (excellent) create-react-app, we’ll stay a little closer to the code and show how simple this can be.

Create a folder and initialize the project. The last command will ask you a few questions; you can accept the defaults except for “entry point”: specify src/index.js, which is where we’ll put our code in a bit.

# Make a project folder with some subfolders for our code.
mkdir -p qz-api-app/src qz-api-app/public
cd qz-api-app

# Create some placeholder files.
touch src/index.js src/App.js public/index.html

# Initialize the project.
npm init

One last step before we’re ready to code! We need to install a few dependencies, including React and Apollo, which is the glue between our app and Quartz’s API.

npm i --save react react-dom react-apollo apollo-boost graphql

Open public/index.html in your favorite text editor. First, we need some HTML to serve as the skeleton for our React app. We don’t need anything fancy. The most important thing here is <div id="root">— we’ll hook into this later.

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8">
		<title>Quartz 0.1</title>
	</head>
	<body>
		<div id="root"></div>
	</body>
</html>

Now let’s create the application at src/index.js, which was the entry point we specified when we initialized the app:

import React from 'react';
import ReactDOM from 'react-dom';
import ApolloClient from 'apollo-boost';
import { ApolloProvider } from 'react-apollo';
import App from './components/App';

// Create an Apollo client and point it at the Quartz GraphQL endpoint.
const client = new ApolloClient( { uri: 'https://cms.qz.com/graphql' } );

const AppWithApollo = () => (
	<ApolloProvider client={client}>
		<App />
	</ApolloProvider>
);

ReactDOM.render( <AppWithApollo />, document.getElementById('root') );

What have we done here? We’ve created an Apollo client and pointed it to Quartz’s API. We’ve used ApolloProvider to provide the Apollo client to any React component that might want to use it. And finally, we’ve used ReactDOM to render our app and insert it into our root div from earlier.

Next, let’s create the App component that we just referenced. Name this file src/components/App.js:

import React from 'react';
import Latest from './Latest';

export default () => (
	<main>
		<h1>Latest Quartz Stories</h1>
		<Latest />
	</main>
);

Not too much going on here: just giving the page a heading. Let’s write the Latest component (src/components/Latest.js):

import React from 'react';
import { Query } from 'react-apollo';
import LatestQuery from './LatestQuery';

export default () => (
	<Query query={LatestQuery}>
		{
			( response ) => {
				if ( response.loading || response.error ) {
					return null;
				}

				return response.data.posts.nodes.map( ( post ) => (
					<article>
						<h3><a href={post.link}>{post.title}</a></h3>
						<p>{post.excerpt}</p>
					</article>
				));
			}
		}
	</Query>
);

Now we’re getting somewhere. We’re using Apollo’s extremely powerful Query component to query the Quartz API for the latest stories. We use a render prop (which is just a function) to process the query response: if the query returned an error or is still loading, return nothing. Otherwise, loop over the posts and render links to the stories.

We’ve left the best for last! Our final step is to write a GraphQL query to fetch the latest posts from the Quartz API (src/components/LatestQuery.js):

import { gql } from 'apollo-boost';

export default gql`
	query {
		posts {
			nodes {
				title
				excerpt
				link
			}
		}
	}
`;

That’s it! Part of GraphQL’s appeal is that you can ask for just the information you need for your application. For a simple app like this one, that’s not much: the title, excerpt, and link. We’re also relying on the fact that the posts endpoint returns the latest posts by default—we could also leverage GraphQL’s powerful capabilities for more advanced queries.

You’ll also notice that the structure of the query here (e.g., posts -> nodes -> title) matches the structure of the returned data that we navigated in the Latest component. This is another thing that makes GraphQL easy to work with.

We’re ready to start our application and see the results! The react-scripts package (borrowed from create-react-app) makes it easy to start a server that compiles JSX:

npx react-scripts start

The server you just started provides hot-reloading, so you can continue to edit and experiment and see your changes instantly. The code we’ve written tracks pretty closely with Apollo’s getting started guide, so if you’re ready to dive into the docs and learn more about querying, you should be in good shape.

This code is also available in its own GitHub repository in case you’ve gotten stuck (or found a mistake!). And here’s a secret: the app you just built is a tiny version of the code that powers qz.com. If working on apps like these excites you, get in touch!