Challenge 1: Write requireOwnerOrAdmin — Possible Solution ==================================================================== import { GraphQLError } from "graphql"; function requireOwnerOrAdmin(context, resourceOwnerId) { const isOwner = context.currentUser?.id === resourceOwnerId; const isAdmin = context.currentUser?.role === "ADMIN"; if (!isOwner && !isAdmin) { throw new GraphQLError("You don't have permission to do that", { extensions: { code: "FORBIDDEN" }, }); } return context.currentUser; } // Example usage: Mutation: { updatePost: (parent, args, context) => { const post = db.posts.findById(args.input.id); requireOwnerOrAdmin(context, post.authorId); // throws if unauthorized return db.posts.update(args.input.id, { title: args.input.title }); }, } WHY THIS WORKS -------------- - This directly generalizes the chapter's inline check inside User.email's resolver (isOwner || isAdmin) into a standalone, reusable function — the exact same two boolean conditions, just extracted so any resolver needing this check can call one function instead of re-writing the same two comparisons every time. - requireOwnerOrAdmin takes resourceOwnerId as a parameter rather than assuming it's always parent.id — this makes it usable for ANY resource with an owner (a post, a comment, etc.), not just the User type the chapter's own example happened to check. The caller is responsible for figuring out and passing in whichever owner ID is relevant to whatever it's protecting. - Using the optional chaining context.currentUser?.id (rather than context.currentUser.id) means an unauthenticated request (where currentUser is null, per Chapter 8's own context example) doesn't throw a separate "cannot read property of null" error — it just safely evaluates isOwner to false, which correctly falls through to the FORBIDDEN throw below (since an unauthenticated user is neither the owner nor an admin). - Using extensions.code: "FORBIDDEN" (rather than "UNAUTHENTICATED") is a deliberate distinction from requireAuth's error code in the chapter's own example — FORBIDDEN communicates "you ARE identified, but you're not allowed to do this specific thing," which is different from UNAUTHENTICATED's "you haven't proven who you are at all." A client can use this distinction to show a different message (e.g. "you don't own this post" vs "please log in"). - Returning context.currentUser on success (rather than nothing) lets the calling resolver reuse that user object afterward if it needs it, mirroring exactly how the chapter's own requireAuth example returns context.currentUser for the same reason.