Implementing Resolvers
1. Understanding Resolver Function
| Signature | Detail |
|---|---|
| (parent, args, context, info) | Standard 4-arg form |
| Returns | Value, Promise, AsyncIterator (subs) |
| Default | If omitted, returns parent[fieldName] |
2. Implementing Field Resolvers
Example: Field resolver map
const resolvers = {
Query: { user: (_, { id }, { db }) => db.user.findUnique({ where: { id } }) },
User: {
posts: (parent, _, { db }) => db.post.findMany({ where: { authorId: parent.id } }),
fullName: (parent) => `${parent.firstName} ${parent.lastName}`
}
};
3. Using Parent Argument
| Aliases | parent / root / source |
|---|---|
| Top-level (Query/Mutation) | Usually undefined or rootValue |
| Nested fields | Result of parent field's resolver |
| Mutation | undefined at root |
4. Using Args Argument
| Property | Detail |
|---|---|
| Type | Plain object with declared field args |
| Coercion | Already coerced to GraphQL types |
| Defaults | Applied automatically |
5. Using Context Argument
Example: Context with auth + DB
const server = new ApolloServer({
schema,
context: async ({ req }) => ({
user: await verifyJWT(req.headers.authorization),
db: prisma,
loaders: createLoaders()
})
});
6. Using Info Argument
| Property | Use |
|---|---|
| fieldName | Currently resolving field |
| fieldNodes | AST of selection |
| path | Full response path |
| schema | Reference to GraphQLSchema |
| parentType | Owning type |
| variableValues | Operation variables |
7. Returning Resolver Values
| Return | Effect |
|---|---|
| Value | Used directly |
| Promise | Awaited |
| null | Field becomes null (or errors if non-null) |
| Throw | Field becomes null + error in errors[] |
8. Implementing Default Resolvers
| Behavior | Detail |
|---|---|
| Property lookup | parent[fieldName] |
| Function | If property is a function, called with (args, ctx, info) |
| Override | Provide explicit resolver to customize |
9. Chaining Resolvers
Example: Resolver chain
// Query.user → User.posts → Post.author → User.posts...
// Each level resolves before its children execute.
// Use DataLoader to batch sibling requests.
10. Using Async Resolvers
| Pattern | Detail |
|---|---|
| async/await | Preferred |
| Promise.all | Parallelize independent fetches |
| DataLoader | Batch within an event-loop tick |
11. Implementing Computed Fields
Example: Computed field
User: {
fullName: (u) => `${u.firstName} ${u.lastName}`,
age: (u) => Math.floor((Date.now() - u.dob) / 31557600000)
}
12. Mapping Data Sources
| Source | Pattern |
|---|---|
| SQL | Prisma, Knex, TypeORM |
| REST | RESTDataSource (Apollo) |
| gRPC | Stub call inside resolver |
| Cache | Redis fronted, write-through |