From harness-claude
Builds GraphQL APIs in NestJS using GraphQLModule, code-first @ObjectType, @Resolver, @Query/@Mutation, and DataLoader for N+1 prevention in complex nested queries.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Build GraphQL APIs with GraphQLModule, @Resolver, @Query/@Mutation, @ObjectType, and DataLoader
Builds GraphQL APIs with schema design, resolvers, DataLoader for N+1 prevention, error handling, and optimizations using Apollo Server (JS) or Graphene (Python). Useful for flexible queries, REST migrations, or subscriptions.
Guides GraphQL API development: schema design with Relay connections and payloads, thin resolvers, N+1 detection/fixes via DataLoader, subscriptions over WebSocket/Redis, federation, query depth/complexity limits.
Develops type-safe GraphQL APIs with schema design, resolver optimization, Apollo Server implementation, query performance tuning, and federation architecture.
Share bugs, ideas, or general feedback.
Build GraphQL APIs with GraphQLModule, @Resolver, @Query/@Mutation, @ObjectType, and DataLoader
npm install @nestjs/graphql @nestjs/apollo @apollo/server graphql
@Module({
imports: [
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
sortSchema: true,
playground: true,
}),
],
})
export class AppModule {}
@ObjectType()
export class User {
@Field(() => ID) id: string;
@Field() email: string;
@Field({ nullable: true }) displayName?: string;
@Field(() => [Post]) posts: Post[];
}
@Resolver(() => User)
export class UsersResolver {
constructor(
private usersService: UsersService,
private postsService: PostsService
) {}
@Query(() => User, { nullable: true })
user(@Args('id', { type: () => ID }) id: string): Promise<User | null> {
return this.usersService.findOne(id);
}
@Query(() => [User])
users(): Promise<User[]> {
return this.usersService.findAll();
}
@Mutation(() => User)
createUser(@Args('input') input: CreateUserInput): Promise<User> {
return this.usersService.create(input);
}
@ResolveField(() => [Post])
posts(@Parent() user: User): Promise<Post[]> {
return this.postsService.findByUser(user.id);
}
}
@InputType()
export class CreateUserInput {
@Field() @IsEmail() email: string;
@Field() @MinLength(8) password: string;
}
@Injectable({ scope: Scope.REQUEST })
export class PostsLoader {
constructor(private postsService: PostsService) {}
readonly loader = new DataLoader<string, Post[]>(async (userIds) => {
const posts = await this.postsService.findByUserIds([...userIds]);
return userIds.map((id) => posts.filter((p) => p.userId === id));
});
}
Code-first vs schema-first: Code-first (above) generates the SDL from TypeScript decorators. Schema-first starts from a .graphql file and maps resolvers to it. Code-first is preferred for TypeScript teams because types are unified.
@Field(() => Type) explicit types: When TypeScript reflection cannot determine the type (arrays, enums, circular references), the explicit arrow function syntax is required. For nullable fields: @Field({ nullable: true }).
Auth on resolvers: Apply guards with @UseGuards(JwtAuthGuard) on the class or individual query/mutation. Access the current user via @Context() ctx: GqlContext where ctx.req.user is populated by the guard.
Subscriptions: Add installSubscriptionHandlers: true and use @Subscription(() => Post) with pubSub.asyncIterator('postCreated') for real-time updates via WebSocket.
Complexity and depth limiting: In production, configure validationRules: [depthLimit(5), createComplexityRule(...)] to prevent expensive queries from exhausting server resources.
https://docs.nestjs.com/graphql/quick-start