skills/nodejs-backend/SKILL.md
Expert Node.js backend development skill. ALWAYS trigger for ANY task involving Node.js, Express.js, NestJS, Fastify, REST APIs, GraphQL APIs, authentication (JWT/OAuth), databases (MySQL/PostgreSQL/MongoDB), ORMs (Prisma/TypeORM/Mongoose), middleware, validation (Zod/Joi), API architecture, microservices, WebSockets, background jobs (Bull/BullMQ), caching (Redis), rate limiting, backend security, server deployment, or server-side TypeScript. Also triggers for: "build an API", "create endpoint", "backend service", "backend architecture", "server-side", "database schema", "REST endpoint", "GraphQL schema/resolver", "authentication system".
npx skillsauth add thesaifalitai/claude-setup nodejs-backendInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are a senior backend engineer specializing in Node.js, NestJS, Express, TypeScript, REST, GraphQL, and production-grade API systems. You write clean, secure, scalable server-side code.
| Use Case | Recommended | |----------|------------| | Framework | NestJS (enterprise) or Express + TypeScript (lightweight) | | ORM | Prisma (PostgreSQL/MySQL) or Mongoose (MongoDB) | | Validation | Zod (type-safe) or class-validator (NestJS DTOs) | | Auth | Passport.js + JWT + refresh tokens | | Caching | Redis (ioredis) | | Queue | BullMQ (Redis-based) | | Testing | Jest + Supertest | | Logging | Winston + Morgan | | Env | dotenv + Zod schema validation |
src/
├── modules/
│ ├── auth/
│ │ ├── auth.controller.ts
│ │ ├── auth.service.ts
│ │ ├── auth.module.ts
│ │ ├── dto/
│ │ │ ├── login.dto.ts
│ │ │ └── register.dto.ts
│ │ └── guards/
│ │ ├── jwt.guard.ts
│ │ └── roles.guard.ts
│ ├── users/
│ └── products/
├── common/
│ ├── decorators/
│ ├── filters/ # Exception filters
│ ├── interceptors/ # Response transform
│ └── pipes/ # Validation pipes
├── config/ # Config module (ConfigService)
├── database/ # Prisma module
└── main.ts
// ✅ DTOs with class-validator
import { IsEmail, IsString, MinLength } from 'class-validator';
export class RegisterDto {
@IsEmail()
email: string;
@IsString()
@MinLength(8)
password: string;
}
// ✅ Controller with proper HTTP semantics
@Controller('users')
@UseGuards(JwtAuthGuard)
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get()
findAll(@Query() query: PaginationDto) {
return this.usersService.findAll(query);
}
@Get(':id')
findOne(@Param('id', ParseUUIDPipe) id: string) {
return this.usersService.findOne(id);
}
@Post()
@HttpCode(HttpStatus.CREATED)
create(@Body() dto: CreateUserDto) {
return this.usersService.create(dto);
}
@Patch(':id')
update(@Param('id', ParseUUIDPipe) id: string, @Body() dto: UpdateUserDto) {
return this.usersService.update(id, dto);
}
@Delete(':id')
@HttpCode(HttpStatus.NO_CONTENT)
remove(@Param('id', ParseUUIDPipe) id: string) {
return this.usersService.remove(id);
}
}
// auth.service.ts
@Injectable()
export class AuthService {
constructor(
private usersService: UsersService,
private jwtService: JwtService,
) {}
async login(email: string, password: string) {
const user = await this.usersService.findByEmail(email);
if (!user || !(await bcrypt.compare(password, user.passwordHash))) {
throw new UnauthorizedException('Invalid credentials');
}
const payload = { sub: user.id, email: user.email, roles: user.roles };
return {
access_token: this.jwtService.sign(payload, { expiresIn: '15m' }),
refresh_token: this.jwtService.sign(payload, { expiresIn: '7d' }),
};
}
}
// jwt.strategy.ts
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private configService: ConfigService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: configService.get('JWT_SECRET'),
});
}
async validate(payload: JwtPayload): Promise<UserEntity> {
return { id: payload.sub, email: payload.email, roles: payload.roles };
}
}
// schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(uuid())
email String @unique
passwordHash String @map("password_hash")
roles Role[] @default([USER])
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
posts Post[]
@@map("users")
}
model Post {
id String @id @default(uuid())
title String
content String?
published Boolean @default(false)
authorId String @map("author_id")
author User @relation(fields: [authorId], references: [id])
createdAt DateTime @default(now()) @map("created_at")
@@index([authorId])
@@map("posts")
}
enum Role {
USER
ADMIN
}
// schema.gql is auto-generated with code-first approach
@Resolver(() => User)
export class UsersResolver {
constructor(private usersService: UsersService) {}
@Query(() => [User])
@UseGuards(GqlAuthGuard)
users(): Promise<User[]> {
return this.usersService.findAll();
}
@Mutation(() => User)
createUser(@Args('input') input: CreateUserInput): Promise<User> {
return this.usersService.create(input);
}
@ResolveField(() => [Post])
posts(@Parent() user: User, @Context() ctx: GqlContext): Promise<Post[]> {
return ctx.loaders.postsLoader.load(user.id); // DataLoader to avoid N+1
}
}
// cache.service.ts
@Injectable()
export class CacheService {
constructor(@InjectRedis() private readonly redis: Redis) {}
async get<T>(key: string): Promise<T | null> {
const data = await this.redis.get(key);
return data ? JSON.parse(data) : null;
}
async set(key: string, value: unknown, ttlSeconds = 300): Promise<void> {
await this.redis.setex(key, ttlSeconds, JSON.stringify(value));
}
async invalidate(pattern: string): Promise<void> {
const keys = await this.redis.keys(pattern);
if (keys.length) await this.redis.del(...keys);
}
}
// filters/global-exception.filter.ts
@Catch()
export class GlobalExceptionFilter implements ExceptionFilter {
private readonly logger = new Logger(GlobalExceptionFilter.name);
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const status = exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
const message = exception instanceof HttpException
? exception.getResponse()
: 'Internal server error';
if (status === 500) this.logger.error(exception);
response.status(status).json({
statusCode: status,
message,
timestamp: new Date().toISOString(),
});
}
}
// queues/email.processor.ts
@Processor('email')
export class EmailProcessor {
@Process('send-welcome')
async handleWelcomeEmail(job: Job<{ email: string; name: string }>) {
await this.emailService.sendWelcome(job.data.email, job.data.name);
}
}
// Adding jobs
await this.emailQueue.add('send-welcome', { email, name }, {
attempts: 3,
backoff: { type: 'exponential', delay: 1000 },
});
# Install
npm i @nestjs/common @nestjs/core @nestjs/platform-express
npm i @nestjs/config @nestjs/jwt @nestjs/passport
npm i @nestjs/throttler @nestjs/cache-manager
npm i prisma @prisma/client
npm i class-validator class-transformer
npm i bcrypt ioredis bullmq
npm i -D @types/bcrypt
# Database
npx prisma generate
npx prisma migrate dev --name init
npx prisma migrate deploy # production
# Docker
docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=secret postgres:16
docker run -d -p 6379:6379 redis:7-alpine
development
Use when building Vue 3 applications with Composition API, Nuxt 3, or Quasar. Invoke for Pinia, TypeScript, PWA, Capacitor mobile apps, Vite configuration.
tools
Expert Upwork freelancer skill for writing winning proposals, client communication, and project management. ALWAYS trigger for ANY task involving Upwork job proposals, bid writing, client messages, project scoping, milestone planning, contract setup, client onboarding, status updates, project handoffs, portfolio descriptions, rate negotiation, handling difficult clients, raising rates, dispute resolution, or review requests. Also triggers for: "write a proposal", "reply to client", "Upwork bid", "freelance proposal", "client message", "project quote", "scope of work", "follow up with client", "client is ghosting", "request review", "upwork job".
development
UI component code implementation specialist. Trigger when WRITING or FIXING UI code — Tailwind CSS utilities, shadcn/ui components, Radix UI primitives, CVA component variants, Framer Motion animations, CSS variables for dark mode, WCAG accessibility code (ARIA labels, focus management, keyboard navigation), responsive layouts, skeleton loaders, empty states, Storybook stories, Figma-to-code. Also triggers for: fix this component, add dark mode, make this accessible, write a Button component, style this form, add animations, fix layout shift. For design PLANNING (choosing colors, styles, font pairings for a new project) use ui-ux-pro-max instead.
development
Design PLANNING and DISCOVERY specialist — use before writing any code. Invoke when CHOOSING a design direction: pick a visual style (glassmorphism, brutalism, minimalism, claymorphism, neumorphism, bento grid, skeuomorphism), generate a color palette, select font pairings, plan a design system, choose chart styles, or define the look-and-feel for a new project. Triggers: 'what style should I use', 'suggest a color palette', 'recommend fonts for my app', 'design system for a SaaS', 'what UI style fits a fintech app', 'generate a design brief', 'choose between dark/light theme', 'plan the visual identity'. Covers 50 styles, 97 palettes, 57 font pairings across React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind, shadcn/ui stacks. For WRITING or FIXING actual UI code (components, Tailwind classes, animations) use uiux-design instead.