NestJS architecture including modules, dependency injection, guards, interceptors, and microservices patterns.
Creates enterprise-grade NestJS applications with modular architecture, dependency injection, and microservices patterns.
npx claudepluginhub a5c-ai/babysitterThis skill is limited to using the following tools:
README.mdExpert assistance for building enterprise Node.js applications with NestJS.
Invoke this skill when you need to:
| Parameter | Type | Required | Description |
|---|---|---|---|
| moduleName | string | Yes | Module name |
| features | array | No | guards, pipes, interceptors |
| database | string | No | prisma, typeorm, mongoose |
| microservices | boolean | No | Enable microservices |
// src/users/users.module.ts
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { PrismaModule } from '../prisma/prisma.module';
@Module({
imports: [PrismaModule],
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}
// src/users/users.controller.ts
import {
Controller,
Get,
Post,
Put,
Delete,
Body,
Param,
Query,
UseGuards,
HttpCode,
HttpStatus,
} from '@nestjs/common';
import { ApiTags, ApiOperation, ApiBearerAuth, ApiResponse } from '@nestjs/swagger';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { UserResponseDto } from './dto/user-response.dto';
import { PaginationDto } from '../common/dto/pagination.dto';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { RolesGuard } from '../auth/guards/roles.guard';
import { Roles } from '../auth/decorators/roles.decorator';
import { CurrentUser } from '../auth/decorators/current-user.decorator';
@ApiTags('users')
@Controller('users')
@UseGuards(JwtAuthGuard, RolesGuard)
@ApiBearerAuth()
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get()
@ApiOperation({ summary: 'Get all users' })
@ApiResponse({ status: 200, type: [UserResponseDto] })
findAll(@Query() pagination: PaginationDto) {
return this.usersService.findAll(pagination);
}
@Get(':id')
@ApiOperation({ summary: 'Get user by ID' })
@ApiResponse({ status: 200, type: UserResponseDto })
findOne(@Param('id') id: string) {
return this.usersService.findById(id);
}
@Post()
@Roles('admin')
@ApiOperation({ summary: 'Create user' })
@ApiResponse({ status: 201, type: UserResponseDto })
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
@Put(':id')
@ApiOperation({ summary: 'Update user' })
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
return this.usersService.update(id, updateUserDto);
}
@Delete(':id')
@Roles('admin')
@HttpCode(HttpStatus.NO_CONTENT)
@ApiOperation({ summary: 'Delete user' })
remove(@Param('id') id: string) {
return this.usersService.delete(id);
}
}
// src/users/dto/create-user.dto.ts
import { IsEmail, IsString, MinLength, IsOptional, IsEnum } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
export class CreateUserDto {
@ApiProperty({ example: 'John Doe' })
@IsString()
name: string;
@ApiProperty({ example: 'john@example.com' })
@IsEmail()
email: string;
@ApiProperty({ minLength: 8 })
@IsString()
@MinLength(8)
password: string;
@ApiProperty({ required: false, enum: ['user', 'admin'] })
@IsOptional()
@IsEnum(['user', 'admin'])
role?: string;
}
// src/users/users.service.ts
import { Injectable, NotFoundException, ConflictException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { PaginationDto } from '../common/dto/pagination.dto';
import * as bcrypt from 'bcryptjs';
@Injectable()
export class UsersService {
constructor(private readonly prisma: PrismaService) {}
async findAll(pagination: PaginationDto) {
const { page = 1, limit = 10, search } = pagination;
const skip = (page - 1) * limit;
const [users, total] = await Promise.all([
this.prisma.user.findMany({
where: search ? { name: { contains: search, mode: 'insensitive' } } : undefined,
skip,
take: limit,
select: {
id: true,
name: true,
email: true,
role: true,
createdAt: true,
},
}),
this.prisma.user.count({
where: search ? { name: { contains: search, mode: 'insensitive' } } : undefined,
}),
]);
return {
data: users,
meta: {
total,
page,
limit,
totalPages: Math.ceil(total / limit),
},
};
}
async findById(id: string) {
const user = await this.prisma.user.findUnique({
where: { id },
select: {
id: true,
name: true,
email: true,
role: true,
createdAt: true,
},
});
if (!user) {
throw new NotFoundException(`User with ID ${id} not found`);
}
return user;
}
async create(dto: CreateUserDto) {
const existing = await this.prisma.user.findUnique({
where: { email: dto.email },
});
if (existing) {
throw new ConflictException('Email already in use');
}
const hashedPassword = await bcrypt.hash(dto.password, 10);
return this.prisma.user.create({
data: {
...dto,
password: hashedPassword,
},
select: {
id: true,
name: true,
email: true,
role: true,
createdAt: true,
},
});
}
async update(id: string, dto: UpdateUserDto) {
await this.findById(id);
if (dto.password) {
dto.password = await bcrypt.hash(dto.password, 10);
}
return this.prisma.user.update({
where: { id },
data: dto,
select: {
id: true,
name: true,
email: true,
role: true,
createdAt: true,
},
});
}
async delete(id: string) {
await this.findById(id);
await this.prisma.user.delete({ where: { id } });
}
}
// src/auth/guards/jwt-auth.guard.ts
import { Injectable, ExecutionContext } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Reflector } from '@nestjs/core';
import { IS_PUBLIC_KEY } from '../decorators/public.decorator';
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
constructor(private reflector: Reflector) {
super();
}
canActivate(context: ExecutionContext) {
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);
if (isPublic) {
return true;
}
return super.canActivate(context);
}
}
// src/auth/guards/roles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ROLES_KEY } from '../decorators/roles.decorator';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.getAllAndOverride<string[]>(ROLES_KEY, [
context.getHandler(),
context.getClass(),
]);
if (!requiredRoles) {
return true;
}
const { user } = context.switchToHttp().getRequest();
return requiredRoles.includes(user.role);
}
}
// src/auth/decorators/roles.decorator.ts
import { SetMetadata } from '@nestjs/common';
export const ROLES_KEY = 'roles';
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);
// src/auth/decorators/current-user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const CurrentUser = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
);
// src/common/interceptors/transform.interceptor.ts
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
export interface Response<T> {
data: T;
meta?: Record<string, any>;
}
@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> {
intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {
return next.handle().pipe(
map((data) => {
if (data?.data && data?.meta) {
return data;
}
return { data };
}),
);
}
}
Activates when the user asks about AI prompts, needs prompt templates, wants to search for prompts, or mentions prompts.chat. Use for discovering, retrieving, and improving prompts.
Search, retrieve, and install Agent Skills from the prompts.chat registry using MCP tools. Use when the user asks to find skills, browse skill catalogs, install a skill for Claude, or extend Claude's capabilities with reusable AI agent components.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.