Day 4 of 5
⏱ ~60 minutes
GraphQL in 5 Days — Day 4

Authentication and Context

Pass auth tokens via headers, validate them in context, and restrict resolvers by role.

The Context Function

Context setup
import { ApolloServer } from '@apollo/server';
import { expressMiddleware } from '@apollo/server/express4';
import jwt from 'jsonwebtoken';
import User from './models/User.js';

const server = new ApolloServer({ typeDefs, resolvers });
await server.start();

app.use('/graphql', expressMiddleware(server, {
  context: async ({ req }) => {
    const token = req.headers.authorization?.split('Bearer ')[1];
    let currentUser = null;
    if (token) {
      try {
        const { id } = jwt.verify(token, process.env.JWT_SECRET);
        currentUser = await User.findById(id);
      } catch {}
    }
    return { currentUser };
  }
}));
Using context in resolvers
const resolvers = {
  Query: {
    me: (_, __, { currentUser }) => {
      if (!currentUser) throw new Error('Not authenticated');
      return currentUser;
    },
  },
  Mutation: {
    createPost: (_, { input }, { currentUser }) => {
      if (!currentUser) throw new Error('Not authenticated');
      if (currentUser.role !== 'author') throw new Error('Not authorized');
      return createPost({ ...input, authorId: currentUser.id });
    }
  }
}
💡
Build a reusable requireAuth(context) helper function that throws if currentUser is null. Call it at the top of every protected resolver. Consistent error messages make debugging easier.
📝 Day 4 Exercise
Add Auth to Your GraphQL API
  1. A
  2. d
  3. d
  4. a
  5. l
  6. o
  7. g
  8. i
  9. n
  10. m
  11. u
  12. t
  13. a
  14. t
  15. i
  16. o
  17. n
  18. t
  19. h
  20. a
  21. t
  22. r
  23. e
  24. t
  25. u
  26. r
  27. n
  28. s
  29. a
  30. J
  31. W
  32. T
  33. .
  34. A
  35. d
  36. d
  37. c
  38. o
  39. n
  40. t
  41. e
  42. x
  43. t
  44. t
  45. o
  46. y
  47. o
  48. u
  49. r
  50. s
  51. e
  52. r
  53. v
  54. e
  55. r
  56. t
  57. h
  58. a
  59. t
  60. r
  61. e
  62. a
  63. d
  64. s
  65. a
  66. n
  67. d
  68. v
  69. e
  70. r
  71. i
  72. f
  73. i
  74. e
  75. s
  76. t
  77. h
  78. e
  79. t
  80. o
  81. k
  82. e
  83. n
  84. .
  85. A
  86. d
  87. d
  88. a
  89. p
  90. r
  91. o
  92. t
  93. e
  94. c
  95. t
  96. e
  97. d
  98. m
  99. e
  100. q
  101. u
  102. e
  103. r
  104. y
  105. .
  106. T
  107. e
  108. s
  109. t
  110. a
  111. u
  112. t
  113. h
  114. i
  115. n
  116. G
  117. r
  118. a
  119. p
  120. h
  121. i
  122. Q
  123. L
  124. b
  125. y
  126. p
  127. a
  128. s
  129. s
  130. i
  131. n
  132. g
  133. t
  134. h
  135. e
  136. t
  137. o
  138. k
  139. e
  140. n
  141. i
  142. n
  143. t
  144. h
  145. e
  146. H
  147. e
  148. a
  149. d
  150. e
  151. r
  152. s
  153. p
  154. a
  155. n
  156. e
  157. l
  158. .

Day 4 Summary

  • Context runs on every request and is passed to every resolver as the third argument.
  • Verify the JWT in context. If invalid, set currentUser = null — don't throw.
  • Throw authorization errors inside resolvers, not in the context function.
  • AuthenticationError → 401. ForbiddenError → 403. Use the right error type.
Finished this lesson?