What we learned while designing and launching an external GraphQL API

Eli Badgio
Open House
Published in
7 min readMar 2, 2020

At Opendoor, we think getting an offer on your home should be as simple as the click of a button. We are building an API platform so that other online real estate companies can build on top of our automated home valuation services and add a button like ours to their sites.

Internally at Opendoor Engineering, we use GraphQL to develop nearly all of our application APIs. Although REST is still the dominant player for building external facing APIs, we decided to double down on GraphQL and use it to build out our external API platform. In this article, we cover the things we learned while designing and launching this API.

Best practices for schema design

In a traditional REST API, data fetching is structured around the set of endpoints that your API makes available to the client. In GraphQL, however, it is structured around your API’s GraphQL Schema.

Design your schema with client use cases top of mind

A core principle of GraphQL is giving power to the client. GraphQL makes this possible by having a declarative syntax that allows clients to describe exactly what data they want from the server then ask for and receive it in a single request. When designing your schema, it is important to think about the use cases of your clients so that these benefits that GraphQL can provide are maximized.

Currently, the primary use case of our API is to provide an external application answers to the following questions for an arbitrary home address:

Would Opendoor make an offer on this home, and if so what is the estimated value of that offer? Furthermore, if the home is eligible and the value estimate is attractive to the customer, how can I refer said customer to Opendoor so they can begin to request an actual offer while also tracking our application as the origin of this transaction?

Since our API is using GraphQL, our mindset needs to be that client applications can ask and receive an answer to these questions with only one request to our API. If they cannot do that, then this is a big red flag that we did something wrong when designing our schema. Finding a type/schema structure that allows for answering these questions all in one response body in an intuitive manner is not a trivial task. It is, however, an achievable goal when this mindset is maintained throughout the entire design process. Along the way we found several actionable ways for how you can keep your client use cases as the north star signal while designing your API:

  1. Hold design reviews. Hold one of these for each new proposed addition/change to your schema, and constantly ask each other whether or not this makes things the implementation easier or more intuitive for the client use cases.
  2. Get external feedback. Show your schema to an engineer who does not already have context, and ask what does and doesn’t make sense to them about it. This will help you understand what the people developing client applications would think.
  3. Build an integration. Create a very simple client application and attempt to integrate your API with it. The pain points you may or may not face while implementing will greatly improve your understanding of how usable your API is. This task in particular has helped us a lot with building more usable APIs.

Keep your schema flexible

Another very important consideration to make while designing your schema is maintaining flexibility. This was particularly important for our use case. For the first launch of our API, we only needed to support the idea of customers wanting to sell their home directly to Opendoor. Moving forward, though, we will need to support a variety of products that we can offer for a given home. This created a very important requirement for how our schema needed to be designed. We now needed to design an API that intuitively answers the questions from above, but also is flexible enough that we can easily add new products for new questions in addition to the simple direct sell one.

Luckily, GraphQL is designed to make handling these types of requirements easier, and it is important that you take advantage of this fact. There are many ways to develop a GraphQL API that achieves both our goal of use case oriented while maintaining significant flexibility. In the end we arrived at the following schema:

Our schema and response types are designed to handle our responses in terms of Products from the outset, but in the initial launch there is only a single product entity type that we call homeSell. Each product will have product-specific data about it that we want to provide back in the response to clients. So, for each product we will create a custom product type object, with SellProduct being our first

The SellProduct object in turn includes eligibility and our value estimate of that home. Furthermore we generate a referral url that can send a client application customer directly into our direct home sell user flow for the provided address and accurately track what client application they originally came from. Also, since we capture these data fields as distinct GraphQL types, we can reuse ValueEstimate and Referral for other products that we add to our schema in the future.

The case for having good documentation

We have all very likely heard the common claim that all of us developers repeat to one another at some point in our careers: good code is self documenting. There is definitely a lot of truth to this statement. Well written code that uses intuitive naming conventions and function structure is self documenting in the sense that someone who reads this code will probably be able to discern what it does just from the code itself.

When we consider the case of an API, it is still somewhat true that a well designed API should, to a certain degree, be self documenting. With a GraphQL API, if the schema design and type naming is intuitive, then in theory an external developer working on a client application should be able to derive a sufficient understanding just from reading the schema. But here is the issue with that statement: it is only true in theory.

In practice, however, that is rarely the case and it is very important that external developers are provided with robust, well written documentation. We have learned this fact the hard way. Already client applications that have integrated our API since launch just a couple months ago have made key mistakes in their implementation that have led to less than ideal user experiences for their customers. We could simply chalk this up to being a result of lack of care on the part of the external developer, but in doing so we would be making a categorical error.

Fundamentally, there is no real difference between an external developer making a mistake while implementing a use case of your API, and a random user improperly interacting with your website or application.

In the case of the latter, we would always treat this as a UX failure of our application and attempt to remedy the situation by improving usability and user guidance within the application. The former should be handled in a similar manner.

These sort of implementation errors that external developers might make can and should be remedied by creating robust, clear, and well maintained documentation. This is something we failed to do when first launching our API, but we have learned from our mistakes and have since been trying to improve our documentation in every way we can think of. We have already seen less confusion among developers working on recent integrations, and we believe this is a direct result of our vastly improved documentation, but there is still a lot work to be done to eliminate the confusion and mistakes entirely.

Create a developer experience that scales

When building any kind of API, whether it be GraphQL or REST, it is important to create a good developer experience, in particular one that scales with an increasing number of developers using your API. At launch, and during the first couple of months following, we would work directly with developers at other companies to help them to get started and launch an integration of our API. This included us semi-manually creating and entering details regarding their application that our system needs to know about, including everything from authentication keys to webhook endpoints for us to hit with notifications regarding the referrals that we are tracking from their application. As you can imagine, that leads to a lot of back and forth, which creates a poor developer experience that definitely does not scale to a large number of integrations.

To fix this, we decided to create a Developer Dashboard that all external developers can use to interact with data regarding their application, such as create/deleting API keys, adding/editing webhook endpoints, and viewing logs of requests from their application. Furthermore, our dashboard allows them to turn on Test Mode, and do all of that using just test data. We very recently launched this dashboard and although we have not yet gathered feedback to see whether this had led to an improvement in experience, we have already reaped the benefits it provides to us from a scalability standpoint. One client application recently needed to rotate the API keys for their application, which previously would have meant us needing to coordinate with them and do it manually, but now they were able to successfully do everything from the dashboard without any help from us. We view this as a huge win!

Some final thoughts

It is difficult to try and build something from scratch without having a ton of experience in the area, and especially without having many examples to go off of. We hope that what we learned along the way can help make things easier for you, should you ever find yourself building an external facing GraphQL API, or any API for that matter. If you have any questions or general thoughts, please share them in the comments below.

We’re hiring! If building an API platform to help people move rapidly and freely is something that interests you, consider joining our team.

--

--