GraphQL to Apollo Resolver Generator

Convert GraphQL schema SDL to Apollo Server resolver code with TypeScript types.

What is GraphQL to Apollo Resolver Generator?

Apollo Server resolvers are the functions that tell a GraphQL server how to fetch the data for every field in your schema. This tool reads your GraphQL Schema Definition Language (SDL) and scaffolds the matching resolver map: Query and Mutation resolvers, plus field resolvers for every non-scalar relationship. The thing most newcomers get wrong is the resolver signature. Every resolver receives four positional arguments in a fixed order: (parent, args, context, info). The first argument, parent (sometimes called root or source), is the value returned by the resolver one level up — for a User.posts field resolver, parent is the resolved User object. The second, args, is the typed object of field arguments declared in your SDL. The third, context, is a per-request object shared across every resolver in a single operation — it is where you put the authenticated user, your DataLoaders, and database clients. The fourth, info, carries the AST and execution metadata and is rarely touched outside of advanced field-selection optimization. The generator also emits a Context interface so your data sources and auth are dependency-injected rather than imported as globals, which keeps resolvers testable. Field resolvers are deliberately generated for object-typed fields because that is exactly where the N+1 query problem appears and where you will later wrap calls in a DataLoader.

How to Use

  1. Choose TypeScript output if you want a typed Resolvers map and Context interface; choose JavaScript only for quick prototypes
  2. Paste your full SDL including Query, Mutation, and your object types — the generator needs the object types to emit field resolvers for relationships
  3. Click "Generate Resolvers" and note the four-argument signature (parent, args, context, info) on every generated stub
  4. Wire your data sources into the Context interface (e.g. context.prisma, context.dataloaders, context.user) instead of importing them globally
  5. Replace each TODO with a real fetch, and for any field resolver that loads related rows in a list, batch it through a DataLoader to avoid N+1 queries

Why Use This Tool?

Emits the correct (parent, args, context, info) signature so you never guess argument order
Generates a Context interface for dependency injection, keeping resolvers pure and unit-testable
Creates field resolvers only for object-typed fields — the exact spots where N+1 problems and DataLoader batching belong
Types args from your SDL arguments so input validation errors surface at compile time
Matches the Apollo Server 4 resolver-map shape, droppable straight into new ApolloServer({ typeDefs, resolvers })

Tips & Best Practices

  • A field resolver only runs when the parent did not already supply that field. If your User row already includes posts, the User.posts resolver is skipped — return undefined from parent to force it to run
  • Put DataLoader instances on context, and create a fresh set per request. Sharing one DataLoader across requests caches stale data and leaks memory because its cache is unbounded by default
  • context is built once per operation by the context function, before any resolver runs — do authentication there (parse the token, load the user) so every resolver can trust context.user
  • Throwing inside a resolver is the idiomatic way to signal errors; use GraphQLError with extensions.code (e.g. UNAUTHENTICATED) rather than returning null and hoping the client notices
  • The info argument lets you read the requested sub-selection. Most teams never need it, but it is how libraries like @apollo/datasource-rest implement field-level projection to avoid over-fetching

Frequently Asked Questions

What are the four resolver arguments and when do I use each?

parent: the object returned by the parent resolver — for User.posts, this is the User. Use it to read the foreign key (parent.id) you need to fetch children. args: the field arguments from your SDL, e.g. { id } for user(id: ID!). context: the per-request shared object holding context.user, context.dataloaders, and DB clients. info: the GraphQL AST and execution state — used rarely, mainly for field-selection optimization. The order is fixed; you can use _ placeholders for ones you do not need.

What is the N+1 problem and how does DataLoader fix it?

If you query 10 users and each has a posts field resolver that runs SELECT * FROM posts WHERE author_id = ?, you fire 1 query for the users plus 10 for the posts — N+1 queries. DataLoader fixes this by collecting all the author_ids requested within a single event-loop tick, then issuing one batched query (SELECT ... WHERE author_id IN (...)). Each field resolver calls context.loaders.postsByAuthor.load(parent.id) and DataLoader coalesces them automatically.

Why should data sources go on context instead of being imported?

Context is request-scoped, so per-request state (the authenticated user, fresh DataLoaders) lives there naturally. Injecting db clients through context also makes resolvers testable — in a unit test you pass a mock context instead of stubbing module imports. Globally imported singletons cannot be swapped per request and cause cross-request cache bleed when used with DataLoader.

How are GraphQL types mapped to TypeScript in the resolver types?

ID and String map to string; Int and Float map to number; Boolean maps to boolean; a custom object type maps to that interface; List ([T]) maps to T[]; non-null (T!) drops the | null union, while a nullable T becomes T | null. Field arguments become a typed args interface per field.

How is this different from the GraphQL to TypeScript Resolver tool?

This tool generates runnable Apollo Server resolver stubs (a resolver map with function bodies and a Context). The TypeScript Resolver tool generates only the type definitions (Resolvers<Context> shapes) meant to be merged with hand-written logic, similar to what @graphql-codegen produces. Use this one to scaffold a new server; use that one to add type safety to existing resolvers.

Is my schema sent to a server?

No, all processing happens entirely in your browser. Your GraphQL schema never leaves your device.

Real-world Examples

Blog schema with a relationship field resolver

A typical posts-and-authors schema. The generator emits a Query resolver for posts plus a Post.author field resolver — the exact place you would later add a DataLoader so loading 50 posts does not fire 50 author queries.

Input
type Query {
  posts(first: Int): [Post!]!
}

type Post {
  id: ID!
  title: String!
  author: User!
}

type User {
  id: ID!
  name: String!
}
Output
interface Context {
  prisma: PrismaClient;
  loaders: { userById: DataLoader<string, User> };
  user?: { id: string };
}

export const resolvers = {
  Query: {
    posts: (_parent, args: { first?: number }, context: Context, _info) => {
      return context.prisma.post.findMany({ take: args.first ?? 20 });
    },
  },
  Post: {
    // Field resolver: runs per Post. Batch through DataLoader to avoid N+1.
    author: (parent: { authorId: string }, _args, context: Context) => {
      return context.loaders.userById.load(parent.authorId);
    },
  },
};

Authenticated mutation using context

A createPost mutation that reads the current user from context (populated by the context function during request setup) and throws a typed error when unauthenticated.

Input
type Mutation {
  createPost(title: String!, body: String!): Post!
}

type Post {
  id: ID!
  title: String!
}
Output
import { GraphQLError } from 'graphql';

export const resolvers = {
  Mutation: {
    createPost: (
      _parent,
      args: { title: string; body: string },
      context: Context,
    ) => {
      if (!context.user) {
        throw new GraphQLError('You must be logged in', {
          extensions: { code: 'UNAUTHENTICATED' },
        });
      }
      return context.prisma.post.create({
        data: { ...args, authorId: context.user.id },
      });
    },
  },
};

Related Tools