Build type-safe GraphQL APIs with NestJS, TypeORM, and automated schema generation.
# NestJS GraphQL API Development for Google Antigravity
Build powerful GraphQL APIs with NestJS in your Google Antigravity projects. This guide covers schema design, resolvers, authentication, and performance optimization.
## GraphQL Module Setup
Configure NestJS GraphQL module:
```typescript
// src/app.module.ts
import { Module } from "@nestjs/common";
import { GraphQLModule } from "@nestjs/graphql";
import { ApolloDriver, ApolloDriverConfig } from "@nestjs/apollo";
import { join } from "path";
import { ApolloServerPluginLandingPageLocalDefault } from "@apollo/server/plugin/landingPage/default";
@Module({
imports: [
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
// Schema generation
autoSchemaFile: join(process.cwd(), "src/schema.gql"),
sortSchema: true,
// Development features
playground: false,
plugins: [ApolloServerPluginLandingPageLocalDefault()],
// Context setup
context: ({ req, res }) => ({ req, res }),
// Error formatting
formatError: (error) => {
const originalError = error.extensions?.originalError as any;
if (!originalError) {
return {
message: error.message,
locations: error.locations,
path: error.path,
};
}
return {
message: originalError.message || error.message,
code: error.extensions?.code,
statusCode: originalError.statusCode,
};
},
// Subscriptions
subscriptions: {
"graphql-ws": true,
"subscriptions-transport-ws": true,
},
// Performance
cache: "bounded",
persistedQueries: false,
}),
UsersModule,
PostsModule,
AuthModule,
],
})
export class AppModule {}
```
## GraphQL Types and Models
Define GraphQL object types:
```typescript
// src/users/models/user.model.ts
import { Field, ID, ObjectType, registerEnumType } from "@nestjs/graphql";
import { Post } from "../../posts/models/post.model";
export enum UserRole {
USER = "USER",
ADMIN = "ADMIN",
MODERATOR = "MODERATOR",
}
registerEnumType(UserRole, {
name: "UserRole",
description: "User role in the system",
});
@ObjectType({ description: "User account" })
export class User {
@Field(() => ID)
id: string;
@Field({ description: "User email address" })
email: string;
@Field({ description: "User display name" })
name: string;
@Field({ nullable: true, description: "User avatar URL" })
avatar?: string;
@Field(() => UserRole, { defaultValue: UserRole.USER })
role: UserRole;
@Field(() => [Post], { nullable: "items" })
posts?: Post[];
@Field()
createdAt: Date;
@Field()
updatedAt: Date;
}
// Pagination types
@ObjectType()
export class PageInfo {
@Field()
hasNextPage: boolean;
@Field()
hasPreviousPage: boolean;
@Field({ nullable: true })
startCursor?: string;
@Field({ nullable: true })
endCursor?: string;
}
@ObjectType()
export class UserEdge {
@Field(() => User)
node: User;
@Field()
cursor: string;
}
@ObjectType()
export class UserConnection {
@Field(() => [UserEdge])
edges: UserEdge[];
@Field(() => PageInfo)
pageInfo: PageInfo;
@Field()
totalCount: number;
}
```
## Resolvers Implementation
Create comprehensive resolvers:
```typescript
// src/users/users.resolver.ts
import {
Resolver,
Query,
Mutation,
Args,
ID,
ResolveField,
Parent,
Context,
Subscription,
} from "@nestjs/graphql";
import { UseGuards } from "@nestjs/common";
import { PubSub } from "graphql-subscriptions";
import { User, UserConnection } from "./models/user.model";
import { UsersService } from "./users.service";
import { PostsService } from "../posts/posts.service";
import { CreateUserInput } from "./dto/create-user.input";
import { UpdateUserInput } from "./dto/update-user.input";
import { PaginationArgs } from "../common/dto/pagination.args";
import { GqlAuthGuard } from "../auth/guards/gql-auth.guard";
import { CurrentUser } from "../auth/decorators/current-user.decorator";
const pubSub = new PubSub();
@Resolver(() => User)
export class UsersResolver {
constructor(
private readonly usersService: UsersService,
private readonly postsService: PostsService,
) {}
@Query(() => User, { name: "user", nullable: true })
async getUser(
@Args("id", { type: () => ID }) id: string,
): Promise<User | null> {
return this.usersService.findById(id);
}
@Query(() => UserConnection, { name: "users" })
async getUsers(
@Args() paginationArgs: PaginationArgs,
@Args("search", { nullable: true }) search?: string,
): Promise<UserConnection> {
return this.usersService.findAllPaginated(paginationArgs, search);
}
@Query(() => User, { name: "me" })
@UseGuards(GqlAuthGuard)
async getCurrentUser(@CurrentUser() user: User): Promise<User> {
return user;
}
@Mutation(() => User)
async createUser(
@Args("input") createUserInput: CreateUserInput,
): Promise<User> {
const user = await this.usersService.create(createUserInput);
// Publish subscription event
pubSub.publish("userCreated", { userCreated: user });
return user;
}
@Mutation(() => User)
@UseGuards(GqlAuthGuard)
async updateUser(
@Args("id", { type: () => ID }) id: string,
@Args("input") updateUserInput: UpdateUserInput,
@CurrentUser() currentUser: User,
): Promise<User> {
// Authorization check
if (currentUser.id !== id && currentUser.role !== "ADMIN") {
throw new ForbiddenException("Cannot update other users");
}
return this.usersService.update(id, updateUserInput);
}
@Mutation(() => Boolean)
@UseGuards(GqlAuthGuard)
async deleteUser(
@Args("id", { type: () => ID }) id: string,
@CurrentUser() currentUser: User,
): Promise<boolean> {
if (currentUser.role !== "ADMIN") {
throw new ForbiddenException("Only admins can delete users");
}
await this.usersService.delete(id);
return true;
}
// Field resolver for posts
@ResolveField()
async posts(@Parent() user: User): Promise<Post[]> {
return this.postsService.findByAuthorId(user.id);
}
// Subscription
@Subscription(() => User, {
filter: (payload, variables) => {
// Optional filtering logic
return true;
},
})
userCreated() {
return pubSub.asyncIterator("userCreated");
}
}
```
## Input Types and Validation
Create validated input types:
```typescript
// src/users/dto/create-user.input.ts
import { InputType, Field } from "@nestjs/graphql";
import {
IsEmail,
IsNotEmpty,
MinLength,
MaxLength,
Matches,
} from "class-validator";
@InputType()
export class CreateUserInput {
@Field()
@IsEmail({}, { message: "Invalid email format" })
email: string;
@Field()
@IsNotEmpty()
@MinLength(2)
@MaxLength(100)
name: string;
@Field()
@MinLength(8)
@Matches(/((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/, {
message: "Password must contain uppercase, lowercase, and number/special character",
})
password: string;
}
// src/common/dto/pagination.args.ts
import { ArgsType, Field, Int } from "@nestjs/graphql";
import { IsOptional, Min, Max } from "class-validator";
@ArgsType()
export class PaginationArgs {
@Field(() => Int, { defaultValue: 20 })
@Min(1)
@Max(100)
first: number = 20;
@Field({ nullable: true })
@IsOptional()
after?: string;
@Field({ nullable: true })
@IsOptional()
before?: string;
}
```
## DataLoader for N+1 Prevention
Implement efficient data loading:
```typescript
// src/users/users.loader.ts
import * as DataLoader from "dataloader";
import { Injectable, Scope } from "@nestjs/common";
import { UsersService } from "./users.service";
import { User } from "./models/user.model";
@Injectable({ scope: Scope.REQUEST })
export class UsersLoader {
constructor(private readonly usersService: UsersService) {}
public readonly batchUsers = new DataLoader<string, User>(
async (userIds: readonly string[]) => {
const users = await this.usersService.findByIds([...userIds]);
const usersMap = new Map(users.map((user) => [user.id, user]));
return userIds.map((id) => usersMap.get(id) || null);
},
);
}
// Usage in resolver
@ResolveField()
async author(
@Parent() post: Post,
@Context("usersLoader") usersLoader: UsersLoader,
): Promise<User> {
return usersLoader.batchUsers.load(post.authorId);
}
```
Google Antigravity generates sophisticated GraphQL APIs with proper type definitions, resolvers, and performance optimizations for scalable NestJS applications.This NestJS prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their nestjs implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For NestJS projects, consider mentioning your framework version, coding style, and any specific libraries you're using.