Enums
Enums declare a closed set of named values. Clients get autocomplete and type-safety; servers refuse anything outside the allowed list. They’re the right tool for status fields, roles, and any field whose values are known at schema design time.
Declare, query, evolve, deprecation
EXAMPLE
// 1) Declare
type Query {
posts(status: PostStatus): [Post!]!
}
enum PostStatus { DRAFT PUBLISHED ARCHIVED }
enum Role { GUEST USER EDITOR ADMIN }
enum Currency { USD EUR AUD GBP JPY }
// Convention: SCREAMING_SNAKE_CASE for values.
// 2) Use as argument and field
type Mutation {
setRole(userId: ID!, role: Role!): User!
}
type User { role: Role! }
// 3) Resolvers — mapping enum to internal values
const resolvers = {
PostStatus: {
DRAFT: 'draft',
PUBLISHED: 'published',
ARCHIVED: 'archived',
},
Role: {
GUEST: 0,
USER: 1,
EDITOR: 2,
ADMIN: 3,
},
Query: {
posts: (_p, { status }, ctx) => ctx.db.posts.find({ status }),
},
};
// Now in code we use 'draft' / 'published' (or 0..3 for Role), but the schema exposes
// the canonical NAMES. Clients NEVER see the internal numeric representation.
// 4) Query syntax — enum values are unquoted identifiers
query {
posts(status: PUBLISHED) {
title
}
}
// In variables — string in JSON, but matched to enum name on validation:
// query Posts($s: PostStatus!) { posts(status: $s) { title } }
// variables: { "s": "PUBLISHED" }
// 5) Deprecation — soft remove a value
enum PostStatus {
DRAFT
PUBLISHED
ARCHIVED
HIDDEN @deprecated(reason: "Use ARCHIVED instead. Removal: 2025-12-01")
}
// Tools show a warning; old clients still work.
// 6) Adding new values — almost always safe
// Adding a new enum value is BACKWARDS-COMPATIBLE for inputs the SERVER returns
// (clients may not handle the new value, but they don't crash).
// For CLIENT-SENT enum values, adding values is also safe; removing is breaking.
// 7) Internationalisation
enum SupportedLocale {
EN_AU
EN_US
EN_GB
FR_FR
DE_DE
JA_JP
}
// Better than a free-form string — clients can't typo, server can't drift.
// 8) Enum vs union vs scalar — quick rules
// enum → small closed set of named values
// union → mutually exclusive object types (rich shapes)
// scalar → free-form value with custom validation (e.g. Email, UUID)
// string + validation → only when the set is open-ended or comes from data
// 9) Code generation
// TypeScript: @graphql-codegen produces:
export enum PostStatus {
Draft = 'DRAFT',
Published = 'PUBLISHED',
Archived = 'ARCHIVED',
}
// Use the generated enum everywhere instead of string literals; refactor-safe.
// 10) Schema evolution patterns
// • Add new value — safe
// • Rename value — BREAKING; deprecate old + add new for one release
// • Remove value — BREAKING; clients sending it get an error
// • Change semantics — BREAKING in spirit even if shape is unchanged; rename instead
// 11) Multiple enums vs one big enum
// Smaller, focused enums beat one big multi-purpose enum:
// enum OrderStatus { PENDING PAID SHIPPED CANCELLED }
// enum AccountState { ACTIVE SUSPENDED CLOSED }
// Versus:
// enum Status { ORDER_PENDING ORDER_PAID ACCOUNT_ACTIVE … }
// One big enum couples unrelated concepts and makes server-side switches ugly.
// 12) Pattern: enum + filter input type for complex queries
input PostFilter {
status: [PostStatus!]
authorId: ID
createdAfter: DateTime
}
type Query { posts(filter: PostFilter): [Post!]! }
// Lets you pass arrays (status: [PUBLISHED, ARCHIVED]) without overloading args.
// 13) Default values
type Query {
posts(status: PostStatus = PUBLISHED, first: Int = 20): [Post!]!
}
// Defaults are part of the contract — changing them is a soft breaking change.
// 14) Custom scalar vs enum — when to use which
// Currency could be enum (closed list of supported currencies) or scalar (ISO 4217 string).
// Pick enum if your business only supports a few; scalar if any ISO code is valid.
// 15) Common bugs
// • Enum value with lowercase or mixed case — accepted, but breaks convention; pick one casing
// • Resolver returns a value NOT in the enum → 'Expected value of type X' error
// • Sending enum names quoted in queries ("PUBLISHED") → syntax error; unquoted only
// • Adding an enum value to client without updating server — input rejected
// • Treating enum names as user-facing copy — they're identifiers; map to display in the client
// • Storing the enum NAME in the database — fine if the schema is the source of truth; otherwise store an ID
// • Trying to delete a value clients still send — track usage before removing
Why it matters
Enums make “magic strings” impossible — pick them whenever the set of values is small and known at design time, especially for status, role, and currency fields. Adding values is backwards-compatible; removing or renaming is a breaking change, so deprecate with @deprecated and a removal date instead.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Discussion
Loading…