Blog, COLDSURF

Separating GraphQL Resolvers by Domain

Recently, while revisiting an old project, I noticed several areas for improvement.
One issue that stood out was that multiple queries and mutations were all being resolved within a single file.
Here’s a comparison of the current state (as-is) and the intended state (to-be).

1. As-is

const resolvers: Resolvers = { Query: { concertList: async (parent, args, ctx) => { // Implementation }, concert: async (parent, args, ctx) => { // Implementation }, user: async (parent, args, ctx) => { // Implementation }, }, Mutation: { createConcert: async (parent, args, ctx) => { // Implementation }, updateConcert: async (parent, args, ctx) => { // Implementation }, removeConcert: async (parent, args, ctx) => { // Implementation }, updateConcertTicket: async (parent, args, ctx) => { // Implementation }, createConcertPoster: async (parent, args, ctx) => { // Implementation }, updateConcertPoster: async (parent, args, ctx) => { // Implementation }, createEmailAuthRequest: async (parent, args) => { // Implementation }, authenticateEmailAuthRequest: async (parent, args) => { // Implementation }, }, }
As shown above, resolvers for various domains were being handled in a single file.
To address this, I reorganized the structure as shown in to-be.

2. To-be

/resolvers /user.ts /staff.ts /concert.ts /concertCategory.ts /auth.ts /emailAuth.ts index.ts
Example - staffResolvers.ts
import { Resolvers } from 'your-types-path'; const staffResolvers: Resolvers = { Query: { // Add staff-related queries here }, Mutation: { // Add staff-related mutations here }, }; export default staffResolvers;
Example - concertResolvers.ts
import { Resolvers } from 'your-types-path'; const concertResolvers: Resolvers = { Query: { concertList: async (parent, args, ctx) => { // Implementation }, concert: async (parent, args, ctx) => { // Implementation }, }, Mutation: { createConcert: async (parent, args, ctx) => { // Implementation }, updateConcert: async (parent, args, ctx) => { // Implementation }, removeConcert: async (parent, args, ctx) => { // Implementation }, updateConcertTicket: async (parent, args, ctx) => { // Implementation }, createConcertPoster: async (parent, args, ctx) => { // Implementation }, updateConcertPoster: async (parent, args, ctx) => { // Implementation }, }, }; export default concertResolvers;

Final Step: Combining Resolvers in the Host File

In the main resolvers.ts file, simply combine the separated resolvers as follows:
import userResolvers from './user'; import staffResolvers from './staff'; import concertResolvers from './concert'; import concertCategoryResolvers from './concertCategory'; import authResolvers from './auth'; import emailAuthResolvers from './emailAuth'; const resolvers = { Query: { ...userResolvers.Query, ...staffResolvers.Query, ...concertResolvers.Query, ...concertCategoryResolvers.Query, }, Mutation: { ...userResolvers.Mutation, ...authResolvers.Mutation, ...emailAuthResolvers.Mutation, ...concertResolvers.Mutation, ...concertCategoryResolvers.Mutation, }, }; export default resolvers;
This setup effectively organizes your resolver code by domain, making it more maintainable and scalable.
← Go home