An important piece of the Kindbody mission is to make fertility healthcare accessible and intuitive. In support of that goal, one of our important projects on the Kindbody Engineering team is the Patient Portal. The Patient Portal lets patients book appointments, view their results, update their personal information, and more.

Our Patient Portal is a frontend React project that relies on data from our KindEMR. When thinking about the patient experience via the portal, we have two big priorities:

  1. Fast changes. Developers can easily, quickly and confidently make changes to both the API and the React app. 
  2. Fast requests. The portal delivers a speedy experience for our patients. 

For these reasons we’ve elected to use GraphQL and Apollo to request data from an API within our KindEMR. 

Intro to GraphQL

GraphQL is both a query language and a runtime for executing those same queries. In GraphQL, the queries themselves control what data they get back*, rather than relying on the server. The way this works is by defining a GraphQL schema in your API from which queries can be built.

For example, you may define a simple model in a schema like this: 

name ‘Appointment’

field :id, !types.ID
field :start_time, Types::DateTime
field :end_time, Types::DateTime
field :location, Types::LocationType

Then the resulting GraphQL queries could be any subset or combination of the defined attributes. 

query appointments {
id
start_time
location {
id
name
}
}

Here we are starting to see where GraphQL gets really interesting. In this example, Locations are their own model and a `LocationType` is also defined in the GraphQL schema. Since `location` is a field on appointments, we can now nest queries for locations, grabbing only the information we care about in the context of appointments. (In our backend logic this nesting can be represented by a “belongs to” or “has many” relationship between classes.)

Our setup for these GraphQL schemas within the KindEMR also ensures that the user making a specific request is authorized to receive that data. If the requester is not authorized, the GraphQL endpoint will simply return an error.

Intro to Apollo

Apollo is a tool that allows you to combine APIs, databases, and microservices into a single data graph that you can query with GraphQL. We use the Apollo React client to combine the queries mentioned above. Apollo provides us with easy-to-use React hooks to make and respond to GraphQL queries and mutations. (If you are not yet using the latest React version with hooks, Apollo also provides a `<Query>` component which can wrap your components with GraphQL logic.)

We created a super lightweight Apollo Express server that stitches together GraphQL schemas from different parts of our KindEMR and abstracts away configuration details. This allows us to make calls from the rest of the React app without having to worry about specifying where to fetch from. We can even pull data from different parts of the KindEMR within a single GraphQL query. Apollo also handles a layer of user authentication. As a part of our build process, we automatically deploy updates to this Apollo service if any part of the KindEMR GraphQL schemas has been updated. This ensures our GraphQL schemas are always in alignment between the frontend and backend.

Since Apollo handles so much of the legwork, the code that is actually in our React components is quite simple. It looks something like this:

function MyComponent() {
const { loading, error, data } = useQuery(APPOINTMENTS);

const [bookAppointment, { data }] = useMutation(BOOK_APPOINTMENT);
}

Fast changes, Fast requests

Fast Changes

The GraphQL schema ensures a form of type checking which has proven itself very valuable. If you attempt to request a nonexistent field, the GraphQL will immediately return an error. We recently migrated our React codebase to use TypeScript, which was made significantly easier by our existing GraphQL code. Because we already had predefined GraphQL types for our most important data structures, it was simple to migrate to TypeScript. The type checking benefits of GraphQL and TypeScript prevent bugs and save us developer time.

Fast Requests

In addition to the technical simplicity of GraphQL, its functional benefits are clear. GraphQL gives us an intuitive way to request the data we actually care about for the Patient Portal, without worrying too much about how that data is structured within the KindEMR. This is a much more performant way to query, too. We can make only one request, but return multiple resources. We also receive only the fields we actually want to use with no excess, minimizing payloads. These two massive benefits are the primary reason we chose to use GraphQL over a traditional REST API. Using GraphQL also helps us keep our React code clean because we don’t have to do any excess filtering on the data responses.

Example

To illustrate how GraphQL can support a complex feature, here is a recent feature in the Patient Portal. The Journey shows patients going through a cycle (aka a round of Egg Freezing, IVF, or IUI) a highly personalized look at exactly what steps are in their current Journey and where they are within them. The Journey pulls data from many different parts of the KindEMR – appointments, payments, medication instructions, and more depending on the type of cycle and the specific patient’s situation.

We’ve written highly specific GraphQL queries that pull only the necessary information for the Journey components to ensure that every relevant detail is present without slow load times. For example, we defined an endpoint in our GraphQL schema that returns only the appointments that occur as a part of the current cycle, not past cycles or otherwise irrelevant appointments. We can write the frontend code for the Journey without worrying about the backend structure of various models and relationships, counting on the GraphQL queries to provide the data it wants in the most efficient manner.

Summary

GraphQL allows us to efficiently request and update data from our KindEMR. We can do this with straightforward, easy to understand, type-checked JavaScript, and minimal configuration via Apollo. This speeds up development of new features, prevents bugs, and most importantly, keeps our Patient Portal lightning fast by minimizing both payload size and number of requests.

* to an extent. Of course, if the API protects certain data, then it will not be returned with the response even if requested.

Mallory Bulkley
Mallory Bulkley
Senior Software Engineer Mallory is an engineer with experience building complex full-stack web applications. At Kindbody she works on the KindEMR Rails applications improving workflows for the clinical staff, and the Kindbody patient portal improving the patient experience.