Article
Data modeling in GraphQL for extending and combining types
Compose and model data from different sourcesGraphQL is very good at helping you compose your data from different data sources; for example, by using federation. When you're combining data from multiple places, you often need mechanisms to model the data to the shape or form you want it to be. In this article, we'll look at how to apply data modeling to a GraphQL schema by extending types, or handling data combinations with different formats using union types.
What are type extensions?
Type extensions can be helpful when you're using GraphQL as a data layer or as a gateway for all your data. GraphQL type extensions enable you to add or replace fields on types that already exist in your schema to better organize data from different sources.
In the GraphQL specification, you can find multiple definitions of type extensions, with object extensions being the most common. The GraphQL specification's Object Extensions section describes how you can use the extend keyword to add or replace fields to an object type.
Following is an example of extending a type:
type Person {
id: String
name: String
age: Int
}
extend type Person {
email: String
}
The type Person will now have the fields id, name, age, and email. Considering the earlier statement about data composition when working with multiple data sources, this example is ideal for a situation where the fields id, name, and age come from one source of data (for example, a database), and email comes from another data source, such as a CRM.
When you're using a service such as GraphQL API development to build your GraphQL API, the implementation schema looks like the following:
type Person {
id: String
name: String
age: Int
}
extend type Person {
email: String
@materializer (query: "getPersonEmail", arguments: [{name: "personId", field: "id"}]
}
type Query {
getPerson(id: ID!): Person
@dbquery(
type: "mysql"
table: "person"
configuration: "mysql_config"
)
getPersonEmail(personId: ID!): String
@rest (endpoint: "https://some.crm.com/api/person/$personId/email")
}
The GraphQL schema above will get the fields for the type Person from the MySQL database using a @dbquery connector. For the extended field email it will use @materializer to get the data from a CRM system using its REST API.
If the database contained an email field, you could overwrite it by using type extensions.
GraphQL also supports inheritance, which is often seen as an alternative to type extensions. Inheritance focuses on reusing rather than extending types, as you'll learn in the next section.
Extending types versus inheritance
There's a difference between type extensions and inheritance. You can use inheritance to compose your data in many programming languages or type systems, and GraphQL is no different.
With inheritance in GraphQL, you can reuse field definitions by using interfaces, meaning you can assign a set of fields to every type that implements this interface. Interface implementations only share the types of the interface.
Suppose you have a company that has both full-time employees and seasonal employees who only work in the winter or summer season:
interface Employee {
id: ID
name: String
email: String
}
type FullTimeEmployee implements Employee {
id: ID
name: String
email: String
monthlyWage: Float
}
type SeasonalEmployee implements Employee {
id: ID
name: String
email: String
hourlyWage: Float
season: String
}
The Employee interface has the fields id, name, and email; the company stores this data for every employee in, for example, a database or CRM. Employees working full-time are paid a monthly wage, while seasonal employees receive an hourly wage. The company also stores the season when the employee is active.
As both FullTimeEmployee and SeasonalEmployee are implementing the interface Employee, you have the certainty that the definitions of the fields id, name, and email are the same across all employees.
In addition to extending types for different response formats from the data sources you're connecting to, you might also want to merge these different formats into one type. In the next section, we'll look at how union types help you model data for those situations.
Handling different response formats with union types
With union types, you can make your schema more flexible. Union types provide a way to define a field that can return one of a list of types; for example, when you're building a GraphQL API that has a query to search for publications. These publications can be books, magazines, or other types of publications. But what if every publication type has a different set of fields?
Let's look at the following schema, where we have a SearchResult type that can return either a Book or a Magazine type:
type Book {
title: String!
authors: [String]!
}
type Magazine {
title: String!
}
union SearchResult = Book | Magazine
In this example, the SearchResult type can return either a Book or a Magazine type. This is useful when we want to return a list of publications that can be either a book or a magazine, especially since the Book type has an authors field, but the Magazine type doesn't.
The query that resolves to the SearchResult type can be defined as follows:
type Query {
search(query: String!): [SearchResult]!
}
If you don't use a union type, you would need to define a separate query for each publication type, resulting in a lot of duplication in your schema.
When you send a request to the GraphQL endpoint, you need to define the fields you want to be returned in case the response is of either type you want to query. GraphQL uses inline fragments to specify which fields you want to query for each type.
For example, if you want to query the title and authors fields of a Book and only the title field of a Magazine, you can define the query as follows:
query search {
search(query: "webdevelopment") {
... on Book {
title
authors
}
... on Magazine {
title
}
}
}
The GraphQL API will return a list of publications that match the search term "webdevelopment". If the publication is a book, the server will return the title and authors fields. If the publication is a magazine, the GraphQL API will only return the title field.
Summary
GraphQL is an excellent tool to help you compose your data from different data sources, and it provides you with different mechanisms for data modeling. In this article, we've looked at two: extending types and union types. Both types help you connect to multiple data sources when you want to either limit the amount of duplication in your GraphQL schema when the responses overlap, or combine the response into a single type.
What do you think? Join our Discord server to be part of the conversation and stay updated with our community.