Skip to content

GraphQL Enums — Fixed Value Sets for Type-Safe APIs

DodaTech Updated 2026-06-28 6 min read

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

GraphQL enums define a fixed set of allowed values for a field, providing autocomplete, validation, and type safety that plain strings cannot match.

What You'll Learn

You will learn how to define enums in SDL, use them in types and arguments, handle enum values in resolvers, map enums to backend values, and design effective enum types.

Why Enums Matter

Strings are flexible but error-prone. A typo like "CRITCAL" instead of "CRITICAL" passes string validation but breaks logic downstream. Enums restrict fields to exact values — GraphQL validates at the API boundary and rejects invalid inputs automatically. DodaTech's Durga Antivirus Pro uses enums for Severity, DeviceStatus, ScanResult, ThreatType, and UserRole — eliminating an entire class of bugs caused by misspelled strings.

flowchart LR
    A["SDL: enum Severity {\n  LOW\n  MEDIUM\n  HIGH\n  CRITICAL\n}"] --> B["Client sends:\nthreats(severity: CRITICAL)"]
    B --> C["GraphQL validates\nagainst enum values"]
    C -->|"Valid"| D["Resolver receives\nstring 'CRITICAL'"]
    C -->|"Invalid"| E["GraphQL error:\n'CRITCAL' is not\na valid enum value"]
    style A fill:#dbeafe,stroke:#2563eb
    style C fill:#fef3c7,stroke:#d97706
    style E fill:#fca5a5,stroke:#dc2626
â„šī¸ Info

Prerequisites: GraphQL SDL and basic resolver knowledge.

Defining Enums in SDL

enum Severity {
  LOW
  MEDIUM
  HIGH
  CRITICAL
}

enum DeviceStatus {
  ONLINE
  OFFLINE
  QUARANTINED
  DELETED
  PENDING_UPDATE
}

enum ScanResult {
  CLEAN
  INFECTED
  SUSPICIOUS
  ERROR
  SCANNING
}

Enum values must be uppercase by convention (UPPER_SNAKE_CASE) and must start with a letter or underscore.

Using Enums in Types and Arguments

type Threat {
  id: ID!
  name: String!
  severity: Severity!
  status: ThreatStatus!
  detectedAt: DateTime!
}

type Query {
  threats(severity: Severity): [Threat!]!
  threatsBySeverity(severities: [Severity!]!): [Threat!]!
}

type Mutation {
  updateThreatSeverity(id: ID!, newSeverity: Severity!): Threat!
}
# Queries using enum values
query CriticalThreats {
  threats(severity: CRITICAL) {
    id
    name
    detectedAt
  }
}

query MultipleSeverities {
  threatsBySeverity(severities: [HIGH, CRITICAL]) {
    id
    name
    severity
  }
}

# Expected response
{
  "data": {
    "threatsBySeverity": [
      { "id": "thr-001", "name": "Emotet", "severity": "CRITICAL" },
      { "id": "thr-002", "name": "Adware", "severity": "HIGH" }
    ]
  }
}

Enum Resolvers and Internal Mapping

Sometimes your backend uses different values (integers, lowercase) than your GraphQL enum:

const resolvers = {
  Severity: {
    LOW: 1,
    MEDIUM: 2,
    HIGH: 3,
    CRITICAL: 4,
  },
  ThreatStatus: {
    ACTIVE: 'active',
    RESOLVED: 'resolved',
    FALSE_POSITIVE: 'false_positive',
  },
  Query: {
    threats: (_, { severity }) => {
      // severity is the enum string (e.g., "CRITICAL")
      // Map to internal value if needed
      const severityMap = { LOW: 1, MEDIUM: 2, HIGH: 3, CRITICAL: 4 };
      const internalSeverity = severity ? severityMap[severity] : null;
      
      return threats.filter(t => 
        !internalSeverity || t.severityLevel >= internalSeverity
      );
    },
  },
};

If your enum values match exactly between GraphQL and your backend, you don't need explicit enum resolvers — GraphQL uses the default resolver.

Enums vs String Unions

# Bad — string without validation
type Threat {
  severity: String!  # Accepts any string: "CRITICAL", "critical", "CRITCAL"
}

# Good — enum with validation
enum Severity { LOW MEDIUM HIGH CRITICAL }
type Threat {
  severity: Severity!  # Only these exact values accepted
}

Enums provide autocomplete in GraphiQL, schema validation, and type generation. String unions provide neither.

Enums with Descriptions

"Severity level of a detected threat"
enum Severity {
  "Low priority — informational only"
  LOW
  "Medium priority — requires attention"
  MEDIUM
  "High priority — potential breach"
  HIGH
  "Critical priority — immediate action required"
  CRITICAL
}

Descriptions appear in GraphiQL documentation, making the API self-documenting.

Common Mistakes

1. Using Strings Instead of Enums for Fixed Values

If a field can only be one of 3-5 values, it should be an enum. Strings accept any value, including typos and unexpected inputs.

2. Making Enums Too Granular

Separate enums for Severity, Priority, Urgency tend to have the same values. Use one enum per domain concept.

3. Changing Enum Values in Production

Removing an enum value is a breaking change — existing clients may send the removed value. Add new values only (backward-compatible).

4. Forgetting to Handle Enum Mapping in Resolvers

If your database stores severity as integers (1-4) but your enum uses string values, you must map between them in resolvers.

5. Not Documenting Enum Values

Without descriptions, developers must guess what each value means. Add descriptions for every enum value, especially non-obvious ones.

Practice Questions

  1. What naming convention should enum values follow?
  2. Can enum values have spaces or special characters?
  3. How do you map GraphQL enums to different backend values?
  4. Is removing an enum value a breaking change?
  5. What is the advantage of enums over String types?

Answers:

  1. UPPER_SNAKE_CASE by convention. Values must start with a letter or underscore and can contain alphanumeric characters and underscores.
  2. No — only letters, digits, and underscores. No spaces, hyphens, or special characters.
  3. Add a resolver for the enum type with key-value pairs mapping GraphQL values to backend values: Severity: { LOW: 1, MEDIUM: 2, ... }.
  4. Yes — existing clients may send the removed value in queries or variables. Only add new values, never remove or rename.
  5. Enums provide validation at the API boundary, autocomplete in GraphiQL, type generation for TypeScript, and documentation for each value.

Challenge: Design enums for DodaTech's full threat management system. Include enums for EventType (THREAT_DETECTED, SCAN_COMPLETED, DEVICE_STATUS_CHANGE), NotificationChannel (EMAIL, SMS, PUSH, Webhook), and AlertRuleOperator (GT, LT, EQ, CONTAINS, MATCHES). Add descriptions to all values and implement resolvers that map to internal codes.

FAQ

Can enum values be numbers?

No — enum values must be strings (identifiers). They must start with a letter or underscore and contain only alphanumeric characters and underscores.

How do I handle enums in TypeScript?

Use GraphQL Code Generator — it produces TypeScript union types from your enum definitions: type Severity = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL'.

Can I use the same enum in multiple types?

Yes — enums are reusable types. Define them once and reference them in as many fields as needed. This is one of the advantages over inline string values.

What happens if a resolver returns an enum value not in the schema?

GraphQL throws a validation error for that field. The value must match one of the defined enum values exactly.

Should enum values be singular or plural?

Use singular — Severity not Severities, DeviceStatus not DeviceStatuses. The enum itself represents a category of values.

Mini Project

Create a GraphQL schema for DodaTech's alert routing system. Include enums for AlertChannel (EMAIL, SMS, PUSH, WEBHOOK, SLACK), AlertPriority (LOW, MEDIUM, HIGH, CRITICAL), AlertState (TRIGGERED, ACKNOWLEDGED, RESOLVED, SUPPRESSED). Add input types that use these enums and implement resolvers with internal mapping.

What's Next

Topic Description
Interfaces Guide Shared fields across types
Union Types Polymorphic return types
Schema Definition Language SDL syntax fundamentals
âŦ… Custom Scalars
➡ Interfaces Guide

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro