Using Error Union Pattern

1. Understanding Error Unions

AspectDetail
IdeaModel expected errors as part of the schema, not exceptions
BenefitType-safe handling, exhaustive switch, better UX
WhenUser-recoverable errors (validation, conflicts)

2. Creating Success and Error Types

Example: Result union

type RegisterSuccess { user: User!, token: String! }
type EmailAlreadyTaken implements UserError { message: String!, field: String! }
type WeakPassword implements UserError { message: String!, requirements: [String!]! }

union RegisterResult = RegisterSuccess | EmailAlreadyTaken | WeakPassword

3. Returning Typed Errors

Example: Resolver returning union

register: async (_, { input }, { db }) => {
  if (await db.user.findUnique({ where: { email: input.email } })) {
    return { __typename: "EmailAlreadyTaken", message: "Email taken", field: "email" };
  }
  if (input.password.length < 8) {
    return { __typename: "WeakPassword", message: "Too short", requirements: ["8+ chars"] };
  }
  const user = await db.user.create({ data: input });
  return { __typename: "RegisterSuccess", user, token: signJWT(user) };
}

4. Querying Error Unions

Example: Client query

mutation { register(input: { ... }) {
  __typename
  ... on RegisterSuccess { user { id } token }
  ... on EmailAlreadyTaken { message field }
  ... on WeakPassword { message requirements }
} }

5. Handling User Errors

TypeStrategy
ValidationReturn typed error per field
Business ruleSpecific error type (e.g., InsufficientFunds)
ConflictVersionMismatch, DuplicateKey

6. Handling System Errors

TypeStrategy
DB downThrow GraphQLError → top-level errors
TimeoutThrow → log, mask
BugThrow → log with stack, mask response

7. Designing Error Hierarchies

Example: Common error interface

interface UserError {
  message: String!
  code: String!
}

interface FieldError implements UserError {
  message: String!
  code: String!
  field: String!
}

8. Providing Error Details

FieldUse
messageHuman-readable
codeMachine-readable enum
field / pathWhich input failed
retryableBoolean hint
retryAfterSeconds until retry safe

9. Implementing Recoverable Errors

Error TypeRecovery
RateLimitExceededShow retryAfter, retry automatically
VersionMismatchReload entity, retry
VerificationRequiredPrompt user for second factor

10. Combining with Exceptions

StrategyDetail
Typed errorsExpected, recoverable, in payload
GraphQLErrorUnexpected/system errors → top-level errors[]
Mixed payloadBoth data and errors can coexist