nodejs-expert

Expert-level Node.js backend patterns for Express, NestJS, Fastify, and API development.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "nodejs-expert" with this command: npx skills add nguyenthienthanh/aura-frog/nguyenthienthanh-aura-frog-nodejs-expert

Node.js Expert Skill

Expert-level Node.js backend patterns for Express, NestJS, Fastify, and API development.

Auto-Detection

This skill activates when:

  • Working with Node.js backend projects

  • Detected express , @nestjs/core , or fastify in package.json

  • Building REST/GraphQL APIs

  • Working with *.controller.ts , *.service.ts files

  1. Project Structure

Express (MVC Pattern)

src/ ├── 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

NestJS (Modular Pattern)

src/ ├── modules/ │ └── users/ │ ├── users.controller.ts │ ├── users.service.ts │ ├── users.module.ts │ ├── dto/ │ └── entities/ ├── common/ │ ├── guards/ │ ├── interceptors/ │ └── filters/ ├── app.module.ts └── main.ts

  1. Express Patterns

Async Error Handler

// ✅ 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 }); }));

Custom Error Classes

// ✅ 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'); } }

Global Error Handler

// ✅ 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', }); });

  1. NestJS Patterns

Controller with Validation

// ✅ 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); } }

DTO with class-validator

// ✅ 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; }

Service with Repository

// ✅ 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; } }

  1. Database Patterns

Prisma Best Practices

// ✅ 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' }, });

TypeORM Patterns

// ✅ 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'], }); } }

  1. Async Best Practices

// ✅ 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); }

  1. Validation with Zod

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

  1. Security Patterns

// ✅ 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);

  1. Logging Best Practices

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 });

  1. Testing Patterns

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, });

Quick Reference

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

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

python-expert

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

code-simplifier

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

code-reviewer

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

dev-expert

No summary provided by upstream source.

Repository SourceNeeds Review