Interfaces in GraphQL
Interfaces let you define a common set of fields that different types can share. They're useful when you have multiple types that share similar properties but aren't identical.
Think of interfaces like a blueprint. They define what fields a type must have, and any type that implements the interface must include all those fields.
Defining an Interface
You define an interface using the interface keyword. It looks like an object type but represents a contract that other types must follow.
interface Node {
id: ID!
}
interface Searchable {
searchTerm: String!
matches(query: String!): Boolean!
}
type User implements Node & Searchable {
id: ID!
name: String!
email: String!
searchTerm: String!
matches(query: String!): Boolean!
}
type Post implements Node & Searchable {
id: ID!
title: String!
content: String!
searchTerm: String!
matches(query: String!): Boolean!
}
Both User and Post implement Node and Searchable, so they must have all the fields defined in those interfaces.
Querying Interfaces
When you query an interface, you can use inline fragments to get type-specific fields:
# Query that works with interfaces
query {
search(query: "graphql") {
... on User {
name
email
}
... on Post {
title
content
}
}
}
# Response
{
"data": {
"search": [
{ "name": "Alice", "email": "alice@example.com" },
{ "title": "GraphQL Basics", "content": "Learn GraphQL..." }
]
}
}
The ... on User syntax tells GraphQL: "If this result is a User, give me the name and email fields."
Real-World Example
A common use case is a notification system where different notification types share some fields:
interface Notification {
id: ID!
message: String!
createdAt: String!
read: Boolean!
}
type EmailNotification implements Notification {
id: ID!
message: String!
createdAt: String!
read: Boolean!
subject: String!
from: String!
}
type SMSNotification implements Notification {
id: ID!
message: String!
createdAt: String!
read: Boolean!
phoneNumber: String!
}
type PushNotification implements Notification {
id: ID!
message: String!
createdAt: String!
read: Boolean!
title: String!
body: String!
}
All notification types share common fields (id, message, createdAt, read) but each has its own unique fields.
Interface vs Union
Interfaces and unions are similar but serve different purposes. Interfaces require all implementing types to share common fields. Unions don't have any shared fields.
// Interface: Common fields required
interface Media {
id: ID!
url: String!
}
// Union: No shared fields required
union SearchResult = User | Post | Product
Use interfaces when types genuinely share common properties. Use unions when types are completely different but can appear in the same context.