Challenge 2: Design a Payload Type With Errors — Possible Solution ==================================================================== // Schema type UserError { field: String! message: String! } type CreatePostPayload { post: Post errors: [UserError!]! } type Mutation { createPost(input: CreatePostInput!): CreatePostPayload! } // Resolver Mutation: { createPost: (parent, args, context) => { const errors = []; if (args.input.title.trim().length === 0) { errors.push({ field: "title", message: "Title cannot be empty" }); } if (errors.length > 0) { return { post: null, errors }; // no post created — validation failed } const post = db.posts.create(args.input); return { post, errors: [] }; // success — empty errors array }, } WHY THIS WORKS -------------- - CreatePostPayload's post field is deliberately NULLABLE (Post, not Post!) — this is what allows the mutation to return a "no post was created" result when validation fails, without needing to throw a GraphQLError or leave data.createPost.post as an unexplained null. The errors field, by contrast, is [UserError!]! — always present, always a real array (even if empty), following the same nullability discipline from Chapter 2's list-type table. - UserError's two fields — field (which specific input field the problem relates to) and message (a human-readable explanation) — give the client enough structure to show a validation message directly next to the relevant form input, rather than just a generic top-level failure banner. This is exactly the "recoverable, user-facing" category of error this chapter distinguishes from systemic failures. - The resolver NEVER throws a GraphQLError for this validation case — it always returns successfully (HTTP 200, no entry in the top-level errors array), with the validation problem communicated entirely through the payload's own errors field instead. This is the core distinction the chapter draws: expected, recoverable problems travel through the payload; only genuinely unexpected/systemic failures (a database connection dying mid-write, for instance) would still be appropriate to surface via a thrown GraphQLError and the top-level errors array. - A client querying this mutation would write something like createPost(input: ...) { post { id } errors { field message } } — checking payload.errors.length first, and only trusting payload.post to exist if that array came back empty.