At Yelp, new web pages and app screens are powered by GraphQL for fetching data.

This blog post describes the Dream Query – a pattern our feature teams use when refactoring or creating new pages.

(Check out our previous blog post to see how we dynamically codegen DataLoaders to implement the server layer!)

Scoping a new feature with GraphQL

Let’s jump in with an example!

Imagine your team is tasked with creating the new version of the “Header component” for the website (we’ll use the Yelp.com website in our example). You may receive a design mock that looks like this:

Mock Header Component

Mock Header Component

Your mission (should you choose to accept, of course): turn this into code.

Alongside the usual planning activities such as OKR docs and estimated timelines, we’ve found it particularly helpful to write out a theoretical GraphQL query that could power the page or component - aka the “Dream Query”.

Writing a Dream Query

The first step is to identify what dynamic data we need to display. In our case of the Header component above, we can see the UI showing:

  • Number of unread inbox messages
  • User’s profile photo

Therefore we might write something like this: 👇

query {
    loggedInUser {
        profilePhoto(size: "small") {
           src
        }
        inbox {
            unreadMessageCount
        }
    }
}

The idea of the dream query is to let developers “just write” the query they wished they could write to power the page - with as low barriers to entry as possible. In other words, imagine you’re writing the UI code, and you magically have everything already available to you. What query would you write?

Here’s a few points to keep in mind:

  1. Try to use real types that already exist in the schema. (Use GraphiQL’s docs tab to search.)
  2. It’s ok if you don’t get this perfect! Large schemas can contain hundreds of types, and it’s easy to miss stuff. (This will ideally be caught in review.)
  3. It’s ok to query for types that don’t exist yet. (That’s kind of the point here!)
  4. It’s ok if your team is totally new to GraphQL. Don’t worry if you aren’t super confident in the syntax yet - this will at least provide a great starting point for reviewers.
  5. Write the Dream Query before the real application code is written - ideally as part of the scoping or planning phase. This cuts down on the overall iteration cycle, since we aren’t writing any real code to implement resolver methods yet.

Review

Once written, share the Dream Query widely. We do this in a Google Doc, so folks can comment line by line.

The goals here are to:

  • Refine the query such that it meets our schema design guidelines. (At Yelp, we have a community-driven schema review group specifically set up for this.)
  • Find other teams who may be stakeholders in the types being created.
  • Understand the time investment needed for the backend portion of the project (i.e. for creating new GraphQL resolvers).

graphql-faker

During review, it’s important not to block feature development. We want to be able to parallelize the backend and frontend work.

We’ve found graphql-faker to be really helpful. It’s a super nifty tool for mocking up a schema, such that you can make “real” queries and iterate on the Dream Query in a live GraphQL playground.

This also lets client developers hook up the graphql-faker endpoint inside their application - meaning we can use a Dream Query in development to build the view layer while the schema is still in review.

Incrementally using the Dream Query

When writing new large pages, or incrementally refactoring a non-GraphQL page to use GraphQL, we may want to roll things out incrementally. Perhaps not all the resolvers can be implemented straight away.

We’ve found it helpful to paste in the whole Dream Query into the app:

const GET_HEADER_DATA = gql`
  query GetHeaderData {
    loggedInUser {
      city
      displayName

      # TODO: Uncomment and use when each field is supported
      # profilePhoto(size: "small") {
      #   src
      # }
      # inbox {
      #   unreadMessageCount
      # }
      # yearsElite
    }
  }
`;

function MyPage() {
  const { data, loading, error } = useQuery(GET_HEADER_DATA);

  if (error) throw error;
  if (loading) return null;

  const { displayName, city } = data;

  return <Header displayName={displayName} city={city} />;
}

Tickets can be created to uncomment specific fields. This provides a way to break up, parallelize and track how much work there is left to complete the migration. When a type becomes available in the schema, we can uncomment the lines and use it in production.

Why a dream query and not a dream schema?

Schema proposals are great to see too! We recommend starting with the query first, since this maps directly the interface the product will be using. It also allows those unfamiliar with the schema to quickly get an understanding of the shape of data the client is requesting, without having to go through all the options that the existing/proposed schema allows for.

Takeaways

The Dream Query

  • is used as a way to communicate what data the component or page needs, and what new backend work needs to be done
  • forces us as developers to think critically about what types we’re adding and encourages reuse of existing schema
  • provides an opportunity for schema reviewers to catch issues early, before iteration cycles are spent committing to suboptimal schema design
  • provides a way to chunk up migrations to GraphQL

Written By

Mark Larah, Software Engineer (@mark_larah)

Become an Engineer at Yelp

We work on a lot of cool projects at Yelp, if you're interested apply!

View Job

Back to blog