agentleFS
thesaifalitaithesaifalitai/claude-setup/skills/nodejs-backend/SKILL.md

nodejs-backend

Skill9 starsChanged 9 months agoMITView on GitHub

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

  • Installs packages

5 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use security and code-review.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • 0xSeroSkill

    security

    Also in 3 projects that use this

  • GoogleSkill

    code-review

    Adversarial systems, concurrency, memory-model, and architecture code review protocol. Use this skill whenever asked to review code, PRs, diffs, or branches in Filament.

    Also in 3 projects that use this

The file

Files
SKILL.md

Node.js Backend Expert

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.

Tech Stack Decisions

Use CaseRecommended
FrameworkNestJS (enterprise) or Express + TypeScript (lightweight)
ORMPrisma (PostgreSQL/MySQL) or Mongoose (MongoDB)
ValidationZod (type-safe) or class-validator (NestJS DTOs)
AuthPassport.js + JWT + refresh tokens
CachingRedis (ioredis)
QueueBullMQ (Redis-based)
TestingJest + Supertest
LoggingWinston + Morgan
Envdotenv + Zod schema validation

NestJS Project Structure

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

REST API Patterns

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

JWT Authentication

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

Prisma Schema Patterns

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

GraphQL with NestJS

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

Redis Caching

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

Error Handling

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

BullMQ Job Queue

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

Security Checklist

  • Helmet.js (HTTP headers)
  • Rate limiting (throttler: 100 req/min per IP)
  • Input validation on all endpoints
  • Parameterized queries (no raw SQL)
  • bcrypt for passwords (rounds ≥ 12)
  • JWT secrets in env, rotated regularly
  • CORS configured for known origins only
  • SQL injection prevention via Prisma/TypeORM
  • XSS prevention via sanitization
  • Audit logs for sensitive actions

Production Setup

# 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

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.