Skip to content
cccskills
FreePublished on GitHub

nest-advanced

GraphQL, WebSockets, and microservices patterns for NestJS. Apply only when the relevant package is in dependencies (`@nestjs/graphql`, `@nestjs/websockets`, `@nestjs/microservices`). Each section is orientation, not exhaustive — defer to NestJS docs for deep dives. Use this skill to: - Wire a GraphQL resolver (code-first) with class-validator inputs. - Build a WebSocket gateway with auth and room management. - Set up a microservice transport (TCP/RabbitMQ/NATS/Redis/Kafka) and message handlers. Do NOT use this skill for: - REST controllers (see nest-conventions + decorator-patterns). - ORM (see nest-data-layer). - Auth strategies (see security-analyst phase guidance).

The English summary is being prepared. Showing the original description.

Works withRedis

Run score Not tested

See how to install

Files included (1)

  • SKILL.md10.1 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

NestJS Advanced Surfaces

This skill covers the three advanced surfaces NestJS supports beyond REST. Each is opt-in via package presence — do not introduce these surfaces speculatively.

GraphQL (@nestjs/graphql)

Module setup (code-first, modern default)

import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';

@Module({
  imports: [
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      autoSchemaFile: 'schema.gql',         // or `true` for in-memory
      sortSchema: true,
      playground: process.env.NODE_ENV !== 'production',
      context: ({ req }) => ({ req }),
    }),
  ],
})
export class AppModule {}

ObjectType (return types)

@ObjectType()
export class User {
  @Field(() => ID)
  id!: string;

  @Field()
  email!: string;

  @Field({ nullable: true })
  name?: string;

  @Field(() => [Order])
  orders!: Order[];
}
  • @Field() — exposes the property; type inferred from TypeScript when possible.
  • For arrays/non-primitive types, pass the explicit type: @Field(() => [Order]).
  • nullable: true for optional fields; matches TS ?: modifier.

InputType (mutation inputs)

@InputType()
export class CreateUserInput {
  @Field()
  @IsEmail()
  email!: string;

  @Field()
  @MinLength(8)
  password!: string;
}

InputType + class-validator works alongside the global ValidationPipe for runtime checks.

Resolver

@Resolver(() => User)
export class UsersResolver {
  constructor(private readonly users: UsersService) {}

  @Query(() => User, { nullable: true })
  user(@Args('id', { type: () => ID }) id: string) {
    return this.users.findById(id);
  }

  @Query(() => [User])
  users() {
    return this.users.findAll();
  }

  @Mutation(() => User)
  createUser(@Args('input') input: CreateUserInput) {
    return this.users.create(input);
  }

  @ResolveField(() => [Order])
  orders(@Parent() user: User) {
    return this.users.ordersByUserId(user.id);
  }
}

Auth in GraphQL

@UseGuards() on resolvers and field resolvers. Use GqlExecutionContext in guards:

@Injectable()
export class GqlAuthGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const ctx = GqlExecutionContext.create(context);
    const req = ctx.getContext().req;
    return !!req.user;
  }
}

For custom param decorators in GraphQL, use:

export const CurrentUser = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) =>
    GqlExecutionContext.create(ctx).getContext().req.user,
);

N+1 — DataLoader

@Injectable({ scope: Scope.REQUEST })
export class OrdersLoader {
  constructor(private readonly orders: OrdersService) {}

  readonly byUserId = new DataLoader<string, Order[]>(async (userIds) => {
    const orders = await this.orders.findByUserIds([...userIds]);
    return userIds.map((id) => orders.filter((o) => o.userId === id));
  });
}

Inject in resolver, use in @ResolveField. Avoids N+1 on User.orders across a list query.

Federation pointer

@nestjs/apollo supports Apollo Federation v2 via ApolloFederationDriver + @apollo/subgraph. If the project uses federation (@key directives, multiple subgraphs), defer to NestJS Federation docs — patterns are stable but extensive.

Schema-first variant

If the project uses schema-first (legacy or specific tooling), .graphql files drive types and @Resolver classes implement them via @nestjs/graphql codegen. Match what exists; don't migrate without BA approval.

WebSockets (@nestjs/websockets)

Gateway

import { WebSocketGateway, WebSocketServer, SubscribeMessage, MessageBody, ConnectedSocket, OnGatewayConnection, OnGatewayDisconnect } from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';

@WebSocketGateway({
  cors: { origin: process.env.WS_ORIGIN ?? '*' },
  namespace: 'chat',
})
export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect {
  @WebSocketServer()
  server!: Server;

  handleConnection(client: Socket) {
    // auth on connection — read token from handshake
  }

  handleDisconnect(client: Socket) {
    // cleanup
  }

  @SubscribeMessage('message')
  async onMessage(
    @MessageBody() payload: SendMessageDto,
    @ConnectedSocket() client: Socket,
  ): Promise<{ ok: true }> {
    this.server.to(payload.roomId).emit('message', { from: client.id, text: payload.text });
    return { ok: true };
  }
}

Auth on connection

WebSocket auth is NOT covered by HTTP guards by default. Pattern:

  1. Client sends token in handshake.auth.token (Socket.io) or Sec-WebSocket-Protocol (raw WS).
  2. Gateway's handleConnection(client) validates synchronously; on failure, client.disconnect(true).
  3. Subsequent messages can rely on client.data.user populated at connect.

Or use @UseGuards(WsJwtGuard) per @SubscribeMessage — guard implements canActivate reading from socket handshake.

Room management

client.join(`room:${roomId}`);
client.leave(`room:${roomId}`);
this.server.to(`room:${roomId}`).emit('event', payload);

Rooms are cheap; cleanup happens automatically on disconnect.

Validation

DTOs work the same — global ValidationPipe applies if enabled at app level. For Socket.io, payload arrives as parsed JSON; class-transformer + class-validator validate normally.

Adapters

@nestjs/platform-socket.io is the default. For raw ws, install @nestjs/platform-ws and app.useWebSocketAdapter(new WsAdapter(app)). For Redis-backed pub/sub across instances, install @socket.io/redis-adapter and configure in IoAdapter subclass.

Microservices (@nestjs/microservices)

Transports

TransportUse case
Transport.TCPSimple node-to-node, no broker
Transport.RMQ (RabbitMQ)Reliable queues, work distribution
Transport.NATSHigh-throughput, lightweight messaging
Transport.REDISPub/sub, simple event broadcasting
Transport.KAFKAEvent sourcing, log-based
Transport.MQTTIoT
Transport.GRPCStrongly-typed RPC, polyglot services

Hybrid app (HTTP + microservice)

// src/main.ts
async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.connectMicroservice<MicroserviceOptions>({
    transport: Transport.RMQ,
    options: {
      urls: [process.env.RABBITMQ_URL!],
      queue: 'orders_queue',
      queueOptions: { durable: true },
      prefetchCount: 1,
    },
  });

  await app.startAllMicroservices();
  await app.listen(3000);
}

Standalone microservice

async function bootstrap() {
  const app = await NestFactory.createMicroservice<MicroserviceOptions>(AppModule, {
    transport: Transport.NATS,
    options: { servers: ['nats://localhost:4222'] },
  });
  await app.listen();
}

Patterns

// request-response (returns)
@MessagePattern({ cmd: 'find_user' })
findUser(@Payload() dto: FindUserDto, @Ctx() context: RmqContext) {
  return this.users.findById(dto.id);
}

// fire-and-forget event
@EventPattern('order.placed')
async handleOrderPlaced(@Payload() event: OrderPlacedEvent) {
  await this.email.sendOrderConfirmation(event);
}

Calling microservices from a service

@Injectable()
export class OrdersService {
  constructor(@Inject('USERS_SERVICE') private readonly usersClient: ClientProxy) {}

  async createOrder(userId: string, items: OrderItem[]) {
    const user = await firstValueFrom(this.usersClient.send({ cmd: 'find_user' }, { id: userId }));
    // ... use user
    this.usersClient.emit('order.placed', { userId, items });
  }
}

Register the client in module providers:

@Module({
  imports: [
    ClientsModule.register([
      {
        name: 'USERS_SERVICE',
        transport: Transport.RMQ,
        options: { urls: [process.env.RABBITMQ_URL!], queue: 'users_queue' },
      },
    ]),
  ],
})

Acknowledgement and retries (RabbitMQ)

By default RabbitMQ auto-acks. For retry-safe handling, set noAck: false and ack manually:

@EventPattern('order.placed')
async handle(@Payload() data: any, @Ctx() ctx: RmqContext) {
  const channel = ctx.getChannelRef();
  const msg = ctx.getMessage();
  try {
    await this.process(data);
    channel.ack(msg);
  } catch {
    channel.nack(msg, false, true);     // requeue
  }
}

Common pitfalls

  • Forgetting to start microservices in hybrid app — await app.startAllMicroservices() BEFORE app.listen().
  • Returning vs emitting — MessagePattern for request-response; EventPattern for fire-and-forget. Mixing them causes hangs.
  • Serialization — payloads must be JSON-serializable for most transports. Dates serialize as strings; class instances lose prototype.
  • Backpressure in Kafka/RabbitMQ — set prefetch / max in-flight to avoid overwhelming consumers.
  • No global pipes by default in microservices — apply @UsePipes(new ValidationPipe()) per handler or via app.useGlobalPipes() on the microservice instance.

Anti-patterns (all surfaces)

  • ❌ Mixing GraphQL and REST for the same resource without a clear boundary.
  • ❌ WebSocket auth via cookies only (no fallback for native clients).
  • ❌ Microservice handlers blocking on long DB calls without backpressure.
  • ❌ Treating GraphQL field resolvers as cheap when they trigger DB roundtrips (use DataLoader).
  • ❌ Putting business logic in resolvers/gateways/handlers — always delegate to services.

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Angular 18-21 project structure, standalone components vs NgModule, control flow (@if/@for/@switch + *ngIf/*ngFor legacy), decorators, dependency injection (inject() function), lifecycle hooks, pipes, Angular Universal SSR pointer. Use this skill to: - Detect project style (standalone vs NgModule) and apply matching patterns. - Pick correct decorators and DI approach. - Use modern control flow (@if/@for/@switch) in Angular 17+ projects. - Apply `inject()` function over constructor injection where appropriate. - Wire bootstrap correctly (bootstrapApplication for standalone, AppModule for legacy). Do NOT use this skill for: - State management (see angular-state-and-rx). - Routing (see angular-routing). - Forms (see angular-forms). - Testing (see angular-testing).

The English summary is being prepared. Showing the original description.

AratKruglik/claude-sdlc48Updated Oct 17, 2026

Angular forms: Reactive Forms (preferred — typed FormGroup/FormControl since Angular 14, FormBuilder, custom + async validators, FormArray, multi-step) and Template-driven (`[(ngModel)]` + FormsModule). Validation strategies, server error mapping, accessibility. Use this skill to: - Build Reactive Forms with typed FormGroup/FormControl. - Use FormBuilder для concise syntax. - Implement custom synchronous and async validators. - Wire FormArray for dynamic field lists. - Map server errors back to form fields. - Pick Reactive vs Template-driven (prefer Reactive). Do NOT use this skill for: - General conventions (see angular-conventions). - State management beyond forms (see angular-state-and-rx). - Routing (see angular-routing). - Testing forms (see angular-testing).

The English summary is being prepared. Showing the original description.

AratKruglik/claude-sdlc48Updated Oct 17, 2026

Angular Router (built-in `@angular/router`) — route configuration for standalone and NgModule projects, functional guards (Angular 14.1+), lazy loading, route resolvers, typed params via signals/observables, programmatic navigation, route data and meta. Use this skill to: - Configure routes (standalone-style or NgModule-style). - Use functional guards (canActivate as function, preferred over class-based in 17+). - Lazy-load components or feature modules. - Implement auth guards via route meta + functional guards. - Read params/queries via `inject(ActivatedRoute)` + signals or RxJS. Do NOT use this skill for: - General conventions (see angular-conventions). - State management (see angular-state-and-rx). - Forms (see angular-forms). - Testing routes (see angular-testing).

The English summary is being prepared. Showing the original description.

AratKruglik/claude-sdlc48Updated Oct 17, 2026

State management for Angular 18-21: signals (signal/computed/effect), services-as-state, NgRx Store + Effects + Selectors, NgRx Component Store, NgRx Signals (newer signal-based store). RxJS essentials — operators, async pipe, takeUntilDestroyed, signal/observable interop. Use this skill to: - Pick the right state tool (signals / services / NgRx variant / vue-query equivalent). - Use signals correctly (signal/computed/effect — when each). - Build a Pinia-style service-as-state singleton. - Set up NgRx Store + Effects + Selectors. - Use RxJS without leaking subscriptions (async pipe, takeUntilDestroyed, Subject patterns). - Bridge signals ↔ observables via toSignal / toObservable. Do NOT use this skill for: - General Angular conventions (see angular-conventions). - Routing state (see angular-routing). - Form state (see angular-forms). - Testing state (see angular-testing).

The English summary is being prepared. Showing the original description.

AratKruglik/claude-sdlc48Updated Oct 17, 2026

Testing Angular 18-21: TestBed, component harnesses (@angular/cdk/testing), Karma+Jasmine (default historical) vs Jest (jest-preset-angular, modern), Angular Testing Library (RTL-style). HttpClient mocking via HttpTestingController. NgRx Effects testing. Cypress / Playwright e2e. Use this skill to: - Detect runner (Karma+Jasmine vs Jest) and configure correctly. - Write component tests with TestBed. - Use component harnesses for Material / custom UI components. - Mock HttpClient via provideHttpClientTesting + HttpTestingController. - Test signal-based inputs with componentRef.setInput(). - Test NgRx Effects with provideMockActions. Do NOT use this skill for: - General Angular conventions (see angular-conventions). - Routing patterns broadly (see angular-routing — covers testing routes briefly). - Form patterns broadly (see angular-forms).

The English summary is being prepared. Showing the original description.

AratKruglik/claude-sdlc48Updated Oct 17, 2026

Shared conventions for every SDLC development-phase architect agent: hard rules, code quality bar, workflow steps (superpowers invocation, spec reading, codebase exploration, verification), and the report/compact-summary contract. Architects load this skill first, then apply their stack-specific instructions on top.

The English summary is being prepared. Showing the original description.

AratKruglik/claude-sdlc48Updated Oct 17, 2026

See all skills by AratKruglik

Report a problem with this skill