Model richer domains in your schema — fixed choices with enums, polymorphism with interfaces and unions, and reusable shapes with input types.
Why: when a field can only be one of a known set — a status, a role, a direction — an enum makes that explicit in the schema. The server and tooling enforce it, so an invalid value is rejected before any resolver runs.
enum Status {
DRAFT
PUBLISHED
ARCHIVED
}
type Post {
id: ID!
title: String!
status: Status!
}Why: when a field returns an interface, you select the shared fields normally and reach into concrete types with "... on Type". __typename tells you which concrete type each result actually is — useful for rendering. This is GraphQL polymorphism in action.
query {
search(term: "space") {
__typename
id
title
... on Movie {
runtime
}
... on Podcast {
episodes
}
}
}Why: passing many arguments to a field is unwieldy, and you cannot reuse an object type as an argument. An input type is a dedicated shape for arguments — essential for mutations (next lessons) that accept structured data.
input CreateBookInput {
title: String!
pages: Int
status: Status = DRAFT # arguments can have defaults
}
type Mutation {
createBook(input: CreateBookInput!): Book!
}