GraphQL is an often misunderstood framework that went from being one of the most popular development innovations to an unfairly maligned one. In truth it has probably gotten over the initial wave of hype and misuse and found its rightful niche. As a Senior Software Development Engineer at Amazon, I spent a lot of the last year working with it and aim to explain through my experiences. Namely how useful it can be when used correctly, and the issues it can bring when used wrong.
GraphQL is a query language for APIs and a runtime for executing those queries against your data. It was developed by Meta (then Facebook) in 2012 and open-sourced in 2015. It enables clients to request exactly the data they need, reducing over-fetching and under-fetching. Unlike REST, which relies on multiple endpoints for different resources, GraphQL operates through a single endpoint and allows clients to specify the shape of the response. Meta, the creators of graphQL have used this since 2012. Various other companies like Netflix, Shopify and PayPal also use graphQL and have published their reasoning and experiences in blogs. I’ll link some of these interesting real world use cases at the end of the article.
Choosing a framework for any enterprise solution is an expensive one, and one we should strive to get right upfront. Thorough investigation and comparisons to alternatives should always be done. Keep in mind the educational cost when choosing a novel framework like graphQL, if your team/company does not already use it. Data modeling and resolution in graphQL requires a slightly different mindset to traditional REST applications. Knowing how the framework handles query complexity is a key factor. Every new team member, or anyone working on this codebase will have to ramp up on a new framework. These are real world costs, so ensure graphQL solves a problem before choosing it as a solution.
That being said, I do think there are some initial questions that can help you begin the decision making process. You should use graphQL if one of more of the following is true:
The primary benefit GraphQL provides is that it prevents over-fetching of data. This is when you collect a large set of information when the caller only needs a smaller set. This can happen in traditional REST applications where an API was originally designed for a specific use case or a specific client. Over time, more data, use cases and features are added to it. More clients start onboarding as the domain grows, each wanting to tweak the API slightly to meet their needs. This isn’t a bad thing, encouraging re-use and evolving over building novel solutions for each use case is more maintainable and allows for faster time to market. It’s better to set up to handle these cases, instead of avoiding it. GraphQL also helps with this scaling as it is self documenting. Instead of relying on API documentation to be up to date, graphQL uses introspection to allow clients to query the schema itself. Since there aren’t strict API request and response shapes, clients can update their queries as their requirements change while application owners can add to the schema as more data becomes available. These changes can happen safely without breaking / blocking each other. The benefits can be best explained using a real world use case.
GraphQL is frequently used as monolithic graphQL where a single large graphQL application queries multiple DBs or performs its own business logic. This is how Meta uses it today. For this example I am going to assume the enterprise uses micro service architecture which is a fairly common practice today.
Let’s consider a use case for a healthcare provider network consisting of multiple data sources, including patient records, medical imaging, billing, and insurance claims, each managed by separate services and databases. Various clients—such as doctor portals, patient mobile apps, and insurance dashboards—require different combinations of this data, and each data source has strict permission requirements due to healthcare privacy regulations (e.g., HIPAA, GDPR).
We can assume a situation like this will have the following data sources:
Electronic Health Records (EHR) Database
Medical Imaging Service
Billing & Claims Service
Appointment Scheduling Service (Database: Appointments & Availability)
The situation can be visualized as follows:
The application could have a schema that looks like follows (simplified)
type Query {
patients(type: PatientQueryType!): [Patient]
allAppointments(filter: AppointmentFilter): [Appointment]
availableDoctors: [Doctor]
insuranceClaims(id: ID): [InsuranceClaim]
billing: Billing
}
# Enum to define the type of patient query
enum PatientQueryType {
ME
ASSIGNED
ALL
}
type Patient {
id: ID!
name: String!
medicalHistory: MedicalHistory
latestImaging: ImagingReport
billing: Billing
insuranceClaims: [InsuranceClaim]
upcomingAppointments(filter: AppointmentFilter): [Appointment]
pastAppointments(filter: AppointmentFilter): [Appointment] filtering
vitalSigns: VitalSigns
}
type Appointment {
id: ID!
date: Date!
time: String!
location: String!
doctor: Doctor
patient: Patient
notes: String
}
input AppointmentFilter {
dateRange: DateRangeFilter
doctorId: ID
patientId: ID
status: AppointmentStatus
}
This schema advertises any/all data that clients can request, while the graphQL application takes on the burden of permissions and effectively routing queries from these schemas to their respective data providers. Most data can be requested in bulk, or be filtered using query arguments. The schema is an important piece to get right, and there are various ways to organize the data. You want to take both the relationship between the data and the relationship between the data providers into account when designing the schema. The schema can be better explained with an example.
Consider a user of the patient app wants to get their upcoming appointments. The query will look as follows:
query {
patients(type: ME) {
id
name
upcomingAppointments(filter: { dateRange: { startDate: "2023-10-23", endDate: "2023-10-29" } }) {
id
date
time
location
doctor {
id
name
specialty
}
}
}
}
In this case, the caller is not worried about billing or medical imaging information, and the graphQL application can dynamically decide to skip these calls.
You can consider other use cases where graphQL would help:
Using GraphQL, each client can request only the data it needs, optimizing network efficiency while maintaining role-based access control (RBAC). This flexibility makes GraphQL an ideal solution for complex, multi-service healthcare ecosystems.
Even in the right use cases, you will need to be careful to avoid some common issues, the most common being the “N+1 problem”. This boils down to inefficient data retrieval from dependencies. In a naive implementation consider the service calls if a caller requests allAppointments in the top level query along with upcoming appointments within the Patient schema. Here are some articles that elaborate and how to avoid it
Think of your data structure as a tree and avoid relationships between data at the same level of depth in a tree. You could result in a situation where a client requests nested queries that can crash the server. For example you could design a schema where An insurance claim has a Patient that has a list of insurance claims and you can see how a client can wreak havoc with a poorly designed query here. Here are some articles on avoiding this.
GraphQL is schema driven and therefore does not have the concept of API versioning. Be very careful when making schema changes and follow pre established patterns of backward compatibility when making updates.
Finally when it comes to performance, graphQL does not work well with traditional HTTP caching. RESTful APIs use the URL which is the global unique identifier used to identify each GET request. This URL can be leveraged to build a cache. The URL serves as the key in the key-value pair in the cache storage and the response becomes the value. Unlike REST, GraphQL does not rely on URL-based responses, making traditional caching mechanisms less effective and requiring other solutions. Nevertheless, caching is still possible and some options will be explained in articles below.
The final thing I’d like to discuss is designing federated architecture using GraphQL. The above example already shows micro service interaction where graphQL acts as a gateway. This concept can be taken to the max with graphQL federation where each micro service is an independently managed graphQL service itself. Each service can define its own “sub graph” which is stitched into a massive schema that clients can query. The gateway service routes the requests to the correct subgraphs and it allows for scalable distributed domain ownership while offering a data aggregation layer for clients. This is the architecture that Netflix currently uses and is explained well
The overall setup will be very similar to the example in terms of inter service interaction. However each service being a sub graph is a great way to separate ownership between domain owners. Instead of a single team owning the entire schema, each subgraph can innovate and expand by themselves and the main graph automatically picks up updates.
You can find more information on GraphQL federation
So there you have it. Hopefully this article has helped in understanding graphQL and knowing when to use it. GraphQL is a powerful and flexible API query language that addresses many of the limitations of REST by allowing clients to request exactly the data they need. Throughout this article, we’ve explored what GraphQL is, why it’s beneficial, and how it works through a practical example. We’ve also examined some of its potential pitfalls, such as the N+1 problem. While GraphQL provides greater efficiency, flexibility, and developer experience, it also requires careful query optimization, authorization handling, and performance monitoring to avoid common challenges. By understanding both its strengths and weaknesses, teams can make informed decisions about when and how to adopt GraphQL in their applications.
Whether you’re building a modern frontend experience, aggregating data from multiple sources, or simply looking for a more efficient way to handle APIs, GraphQL is a compelling option. With the right implementation practices, it can significantly enhance both API performance and developer productivity.