Node.js backend expert. PROACTIVELY use when working with Express, NestJS, Fastify, Node.js APIs. Triggers: nodejs, express, nestjs, fastify, api route, backend
/plugin marketplace add nguyenthienthanh/aura-frog/plugin install aura-frog@aurafrogThis skill is limited to using the following tools:
Expert-level Node.js backend patterns for Express, NestJS, Fastify, and API development.
This skill activates when:
express, @nestjs/core, or fastify in package.json*.controller.ts, *.service.ts filessrc/
├── config/ # Configuration
├── controllers/ # Route handlers
├── models/ # Database models
├── routes/ # Route definitions
├── middlewares/ # Custom middleware
├── services/ # Business logic
├── utils/ # Utilities
├── validators/ # Request validation
├── app.ts # Express setup
└── server.ts # Entry point
src/
├── modules/
│ └── users/
│ ├── users.controller.ts
│ ├── users.service.ts
│ ├── users.module.ts
│ ├── dto/
│ └── entities/
├── common/
│ ├── guards/
│ ├── interceptors/
│ └── filters/
├── app.module.ts
└── main.ts
// ✅ GOOD - Wrap async routes
const asyncHandler = (fn: RequestHandler): RequestHandler => {
return (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
};
router.get('/users/:id', asyncHandler(async (req, res) => {
const user = await userService.findById(req.params.id);
if (user == null) {
throw new NotFoundError('User');
}
res.json({ data: user });
}));
// ✅ GOOD - Typed error hierarchy
class AppError extends Error {
constructor(
message: string,
public statusCode: number = 500,
public code: string = 'INTERNAL_ERROR',
public isOperational: boolean = true
) {
super(message);
Error.captureStackTrace(this, this.constructor);
}
}
class NotFoundError extends AppError {
constructor(resource: string) {
super(`${resource} not found`, 404, 'NOT_FOUND');
}
}
class ValidationError extends AppError {
constructor(message: string, public details?: Record<string, string[]>) {
super(message, 400, 'VALIDATION_ERROR');
}
}
// ✅ GOOD - Centralized error handling
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
if (err instanceof AppError) {
return res.status(err.statusCode).json({
status: 'error',
code: err.code,
message: err.message,
});
}
logger.error('Unexpected error', { error: err, path: req.path });
res.status(500).json({
status: 'error',
code: 'INTERNAL_ERROR',
message: 'Something went wrong',
});
});
// ✅ GOOD - NestJS controller with DTOs
@Controller('users')
export class UsersController {
constructor(private usersService: UsersService) {}
@Post()
@HttpCode(201)
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
@Get(':id')
findOne(@Param('id', ParseUUIDPipe) id: string) {
return this.usersService.findById(id);
}
}
// ✅ GOOD - Validated DTO
import { IsEmail, IsString, MinLength } from 'class-validator';
export class CreateUserDto {
@IsEmail()
email: string;
@IsString()
@MinLength(2)
name: string;
@IsString()
@MinLength(8)
password: string;
}
// ✅ GOOD - Service pattern
@Injectable()
export class UsersService {
constructor(private prisma: PrismaService) {}
async create(data: CreateUserDto) {
const hashedPassword = await bcrypt.hash(data.password, 10);
return this.prisma.user.create({
data: { ...data, password: hashedPassword },
select: { id: true, email: true, name: true },
});
}
async findById(id: string) {
const user = await this.prisma.user.findUnique({ where: { id } });
if (user == null) {
throw new NotFoundException('User not found');
}
return user;
}
}
// ✅ GOOD - Prevent N+1 with include
const users = await prisma.user.findMany({
include: { posts: true, profile: true },
});
// ✅ GOOD - Select only needed fields
const users = await prisma.user.findMany({
select: { id: true, email: true, name: true },
});
// ✅ GOOD - Transactions
await prisma.$transaction(async (tx) => {
const sender = await tx.account.update({
where: { id: senderId },
data: { balance: { decrement: amount } },
});
await tx.account.update({
where: { id: receiverId },
data: { balance: { increment: amount } },
});
});
// ✅ GOOD - Cursor pagination
const users = await prisma.user.findMany({
take: 20,
skip: cursor ? 1 : 0,
cursor: cursor ? { id: cursor } : undefined,
orderBy: { createdAt: 'desc' },
});
// ✅ GOOD - Repository pattern
@EntityRepository(User)
export class UserRepository extends Repository<User> {
async findWithPosts(id: string): Promise<User | null> {
return this.findOne({
where: { id },
relations: ['posts'],
});
}
}
// ✅ GOOD - Parallel operations
async function getDashboard(userId: string) {
const [user, posts, notifications] = await Promise.all([
getUser(userId),
getUserPosts(userId),
getNotifications(userId),
]);
return { user, posts, notifications };
}
// ✅ GOOD - Handle partial failures
const results = await Promise.allSettled([
fetchFromAPI1(),
fetchFromAPI2(),
fetchFromAPI3(),
]);
const successful = results
.filter((r): r is PromiseFulfilledResult<Data> => r.status === 'fulfilled')
.map((r) => r.value);
// ✅ GOOD - AbortController for timeouts
async function fetchWithTimeout(url: string, timeout = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
return await fetch(url, { signal: controller.signal });
} finally {
clearTimeout(timeoutId);
}
}
// ❌ BAD - async in forEach (fire and forget!)
items.forEach(async (item) => await process(item));
// ✅ GOOD - Use Promise.all or for...of
await Promise.all(items.map((item) => process(item)));
// Or sequential:
for (const item of items) {
await process(item);
}
import { z } from 'zod';
// ✅ GOOD - Schema definition
const createUserSchema = z.object({
email: z.string().email(),
name: z.string().min(2).max(100),
password: z.string().min(8).regex(/[A-Z]/).regex(/[0-9]/),
});
type CreateUserInput = z.infer<typeof createUserSchema>;
// ✅ GOOD - Express middleware
const validate = (schema: z.ZodSchema) => {
return (req: Request, res: Response, next: NextFunction) => {
const result = schema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({
status: 'error',
code: 'VALIDATION_ERROR',
errors: result.error.flatten().fieldErrors,
});
}
req.body = result.data;
next();
};
};
router.post('/users', validate(createUserSchema), createUser);
// ✅ GOOD - Helmet for security headers
import helmet from 'helmet';
app.use(helmet());
// ✅ GOOD - Rate limiting
import rateLimit from 'express-rate-limit';
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100,
standardHeaders: true,
});
app.use('/api/', apiLimiter);
// ✅ GOOD - CORS configuration
import cors from 'cors';
app.use(cors({
origin: process.env.ALLOWED_ORIGINS?.split(','),
credentials: true,
}));
// ✅ GOOD - Input sanitization
import DOMPurify from 'isomorphic-dompurify';
const sanitized = DOMPurify.sanitize(userInput);
import pino from 'pino';
// ✅ GOOD - Structured logging
const logger = pino({
level: process.env.LOG_LEVEL ?? 'info',
redact: ['password', 'token', 'authorization'],
});
// ✅ GOOD - Request context
app.use((req, res, next) => {
req.log = logger.child({
requestId: req.headers['x-request-id'] ?? crypto.randomUUID(),
path: req.path,
method: req.method,
});
next();
});
// ✅ GOOD - Log levels
logger.debug('Detailed debug info');
logger.info('User created', { userId: user.id });
logger.warn('Deprecated endpoint', { endpoint: req.path });
logger.error('Operation failed', { error, userId });
import request from 'supertest';
// ✅ GOOD - Integration tests
describe('POST /api/users', () => {
it('creates a new user', async () => {
const response = await request(app)
.post('/api/users')
.send({ email: 'test@example.com', name: 'Test' })
.expect(201);
expect(response.body.data).toMatchObject({
email: 'test@example.com',
name: 'Test',
});
});
it('returns 400 for invalid email', async () => {
await request(app)
.post('/api/users')
.send({ email: 'invalid', name: 'Test' })
.expect(400);
});
});
// ✅ GOOD - Factory pattern
import { faker } from '@faker-js/faker';
const createUser = (overrides?: Partial<User>): User => ({
id: faker.string.uuid(),
email: faker.internet.email(),
name: faker.person.fullName(),
createdAt: new Date(),
...overrides,
});
checklist[12]{pattern,best_practice}:
Errors,Custom error classes + asyncHandler wrapper
Validation,Zod or class-validator DTOs
Database,Prisma/TypeORM with eager loading
Async,Promise.all for parallel Never async forEach
Security,Helmet + CORS + rate limiting
Logging,Pino structured logging
Testing,Supertest + factories
Auth,JWT with Passport or NestJS guards
Config,dotenv + typed config object
Routes,RESTful conventions /api/v1/
Middleware,Error handler last
Types,Strict TypeScript no any
Version: 1.3.0
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.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.