本文へ移動
cccskills
無料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).

インストール方法を見る

含まれるファイル(1)

  • SKILL.md10.1 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

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.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

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).

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

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).

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

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).

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

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).

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

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).

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

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.

日本語の概要は準備中です。原文の説明を表示しています。

AratKruglik/claude-sdlc362026年9月21日 更新

AratKruglik のスキルをすべて見る

このスキルの問題を報告する