Skip to content

GraphQL Interfaces — Shared Field Contracts Across Types

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you will learn about Graphql Interfaces. We cover key concepts, practical examples, and best practices to help you master this topic.

GraphQL interfaces define a contract of shared fields that multiple types can implement, enabling polymorphic queries where a single field returns different object types.

What You'll Learn

You will learn how to define interfaces in SDL, implement them on object types, query interface fields with inline fragments, and design effective interface hierarchies.

Why Interfaces Matter

Without interfaces, each type defines its own fields even when they share common patterns. Every Device, Threat, and User has an id, createdAt, and updatedAt — but without an interface, each defines them separately. Interfaces enforce that every implementing type includes these fields, making schemas more predictable. DodaTech's Durga Antivirus Pro uses a Node interface for all timestamped entities and a Event interface for all notification events, ensuring every event type has id, timestamp, and severity.

flowchart TB
    A["interface Node {\n  id: ID!\n  createdAt: DateTime!\n  updatedAt: DateTime!\n}"] --> B["type Device implements Node"]
    A --> C["type Threat implements Node"]
    A --> D["type User implements Node"]
    A --> E["type Scan implements Node"]
    B --> F["Query { search: [Node!]! }"]
    style A fill:#dbeafe,stroke:#2563eb
    style B fill:#fef3c7,stroke:#d97706
    style C fill:#fef3c7,stroke:#d97706
    style D fill:#fef3c7,stroke:#d97706
    style E fill:#fef3c7,stroke:#d97706
â„šī¸ Info

Prerequisites: GraphQL types and schema design. Understanding of object-oriented polymorphism is helpful.

Defining an Interface

interface Node {
  id: ID!
  createdAt: DateTime!
  updatedAt: DateTime!
}

interface Event {
  id: ID!
  timestamp: DateTime!
  severity: Severity!
  message: String!
}

Interface fields must be implemented exactly as defined — same name, same type, same nullability.

Implementing an Interface

type Device implements Node {
  id: ID!
  createdAt: DateTime!
  updatedAt: DateTime!
  name: String!
  os: String!
  user: User!
}

type Threat implements Node & Event {
  id: ID!
  createdAt: DateTime!
  updatedAt: DateTime!
  timestamp: DateTime!
  severity: Severity!
  message: String!
  name: String!
  threatType: ThreatType!
}

type Scan implements Node & Event {
  id: ID!
  createdAt: DateTime!
  updatedAt: DateTime!
  timestamp: DateTime!
  severity: Severity!
  message: String!
  status: ScanStatus!
  duration: Int!
}

A type can implement multiple interfaces. It must include all required fields from each interface.

Querying Interface Fields

interface Searchable {
  id: ID!
  score: Float!
}

type Query {
  search(query: String!): [Searchable!]!
  recentEvents: [Event!]!
}
# Query that returns an interface type
query RecentAlerts {
  recentEvents {
    id
    timestamp
    severity
    message
    # Type-specific fields need inline fragments
    ... on Threat {
      name
      threatType
    }
    ... on Scan {
      status
      duration
    }
  }
}

# Expected response
{
  "data": {
    "recentEvents": [
      {
        "id": "evt-001",
        "timestamp": "2026-06-28T10:00:00Z",
        "severity": "CRITICAL",
        "message": "Emotet detected on Office-PC",
        "name": "Emotet",
        "threatType": "RANSOMWARE"
      },
      {
        "id": "evt-002",
        "timestamp": "2026-06-28T10:05:00Z",
        "severity": "INFO",
        "message": "Full scan completed on Dev-Macbook",
        "status": "CLEAN",
        "duration": 45000
      }
    ]
  }
}

Interface Resolvers

When a field returns an interface type, you need __resolveType to tell GraphQL which concrete type to use:

const resolvers = {
  Event: {
    __resolveType(event) {
      // Determine type based on a discriminator field
      if (event.threatType) return 'Threat';
      if (event.status) return 'Scan';
      if (event.channel) return 'Notification';
      return null; // Unknown type — GraphQL will error
    },
  },
  Query: {
    recentEvents: () => {
      return [
        { id: 'evt-001', timestamp: new Date(), severity: 'CRITICAL', 
          message: 'Threat detected', threatType: 'RANSOMWARE', name: 'Emotet',
          createdAt: new Date(), updatedAt: new Date() },
        { id: 'evt-002', timestamp: new Date(), severity: 'INFO', 
          message: 'Scan complete', status: 'CLEAN', duration: 45000,
          createdAt: new Date(), updatedAt: new Date() },
      ];
    },
  },
};

Interface vs Union

# Interface — types share common fields
interface Event {
  id: ID!
  timestamp: DateTime!
  severity: Severity!
}

# Union — types may have no common fields
union SearchResult = Device | Threat | User

# Query
type Query {
  recentEvents: [Event!]!       # Guaranteed id, timestamp, severity on all
  search(query: String!): [SearchResult!]!  # No guaranteed common fields
}

Use interfaces when types share fields. Use unions when types are completely different but can appear in the same context.

Common Mistakes

1. Forgetting __resolveType

Without __resolveType, GraphQL cannot determine which concrete type to return for an interface field. Every interface must have a type resolver.

2. Omitting Interface Fields in Implementing Types

Every field declared in the interface must be present in the implementing type with the exact same signature (name, type, nullability). Missing fields cause schema errors.

3. Not Using Inline Fragments in Queries

Interface queries return only interface-level fields. To access type-specific fields, use ... on Threat { ... } or ... on Scan { ... }.

4. Overusing Interfaces for Unrelated Types

If types share no meaningful common fields, use a union instead. Forcing unrelated types under an interface creates a misleading contract.

5. Deep Interface Hierarchies

Interfaces implementing interfaces creates complex resolution chains. Limit to one level of interface inheritance unless absolutely necessary.

Practice Questions

  1. What is the difference between an interface and a union?
  2. What is __resolveType and when is it needed?
  3. Can a type implement multiple interfaces?
  4. What happens if an implementing type omits an interface field?
  5. How do clients access type-specific fields from an interface query?

Answers:

  1. Interfaces define shared fields that all implementing types must include. Unions define a set of possible types with no guaranteed common fields.
  2. __resolveType is a resolver function on the interface that returns the name of the concrete type for a given value. It's required for every interface.
  3. Yes — type Threat implements Node & Event implements both interfaces. The type must include fields from both.
  4. The schema is invalid and won't load. Every interface field must be present in the implementing type with the exact same signature.
  5. Using inline fragments: ... on Threat { threatType }. The client checks __typename or uses fragments to access type-specific fields.

Challenge: Design an interface hierarchy for DodaTech's event system. Create a base Event interface with id, timestamp, severity, and source. Implement SecurityEvent (threatType, affectedDevice), SystemEvent (serviceName, status), and UserEvent (userId, action). Write a query that fetches recent events and displays type-specific details. Implement __resolveType for the interface.

FAQ

Can I add fields to an interface after deployment?

Yes — adding optional fields to an interface is backward-compatible. Existing implementing types must add the field, but clients are not required to query it.

Can an interface implement another interface?

Yes — GraphQL supports interface inheritance. However, this adds complexity to __resolveType chains. Keep hierarchies shallow.

How do I test interface resolvers?

Write unit tests for __resolveType with mock objects. Test that each discriminator value maps to the correct type name. Integration test full queries with inline fragments.

Do I need interfaces for every shared field?

No — if you never query fields polymorphically (returning mixed types from one field), interfaces add complexity without benefit. Use them when you need to return different types from the same query field.

Can interfaces have arguments on fields?

No — interface fields cannot have arguments. Only the implementing types can add arguments to fields that override the interface definition.

Mini Project

Create a GraphQL schema for DodaTech's notification system with an Notification interface implemented by EmailNotification, SMSNotification, PushNotification, and WebhookNotification. Implement a notifications query that returns all types and uses inline fragments for type-specific rendering.

What's Next

Topic Description
Union Types Polymorphic types without shared fields
Enums in Depth Fixed value sets for type safety
Input Types Structured mutation arguments
âŦ… Enums in Depth
➡ Union Types

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro