Claude Skill

nodejs-backend

Node.js backend patterns: framework selection, layered architecture, TypeScript, validation, error handling, security, production deployment. Use when building REST APIs, Express/Fastify servers, microservices, or server-side TypeScript.

LLM Mart · 0 points · 8 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download iliaal-whetstone-distillery_generated-skills_nodejs-backend-bccd699.zip · 5 KB
Part of iliaal/whetstone — 62 skills

Install

skills CLI npx skills add https://github.com/iliaal/whetstone/tree/master/distillery/generated-skills/nodejs-backend
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install iliaal-whetstone@llmmart
Git git clone https://github.com/iliaal/whetstone.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole iliaal/whetstone collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Node.js Backend

Framework Selection

Context Choose Why
Edge/Serverless Hono Zero-dep, fastest cold starts
Performance API Fastify 2-3x faster than Express, built-in schema validation
Enterprise/team NestJS DI, decorators, structured conventions
Legacy/ecosystem Express Most middleware, widest adoption

Ask user: deployment target, cold start needs, team experience, existing codebase.

Architecture

src/
├── routes/          # HTTP: parse request, call service, format response
├── middleware/       # Auth, validation, rate limiting, logging
├── services/        # Business logic (no HTTP types)
├── repositories/    # Data access only (queries, ORM)
├── config/          # Env, DB pool, constants
└── types/           # Shared TypeScript interfaces
  • Routes never contain business logic
  • Services never import Request/Response
  • Repositories never throw HTTP errors
  • For scripts/prototypes: single file is fine — ask "will this grow?"

TypeScript Rules

  • Use import type { } for type-only imports — eliminates runtime overhead
  • Prefer interface for object shapes (2-5x faster type resolution than intersections)
  • Prefer unknown over any — forces explicit narrowing
  • Use z.infer<typeof Schema> as single source of truth — never duplicate types and schemas
  • Minimize as assertions — use type guards instead
  • Add explicit return types to exported functions (faster declaration emit)
  • Untyped package? declare module 'pkg' { const v: unknown; export default v; } in types/ambient.d.ts

Validation

Zod (TypeScript inference) or TypeBox (Fastify native). Validate at boundaries only: request entry, before DB ops, env vars at startup. Use .extend(), .pick(), .omit(), .partial(), .merge() for DRY schemas.

Error Handling

Custom error hierarchy: AppError(message, statusCode, isOperational) → ValidationError(400), NotFoundError(404), UnauthorizedError(401), ForbiddenError(403), ConflictError(409)

Centralized handler middleware:

  • AppError → return { error: message } with statusCode
  • Unknown → log full stack, return 500 + generic message in production
  • Async wrapper: const asyncHandler = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);

Codes: 400 bad input | 401 no auth | 403 no permission | 404 missing | 409 conflict | 422 business rule | 429 rate limited | 500 server fault

API Design

  • Resources: plural nouns (/users), max 2 nesting levels (/users/:id/orders)
  • Methods: GET read | POST create | PUT replace | PATCH partial | DELETE remove
  • Versioning: URL path /api/v1/
  • Response: { data, pagination?: { page, limit, total, totalPages } }
  • Errors: { error: { code, message, details? } }
  • Queries: ?page=1&limit=20&status=active&sort=createdAt,desc
  • Return Location header on 201. Use 204 for successful DELETE with no body.

Async Patterns

Pattern Use When
async/await Sequential operations
Promise.all Parallel independent ops
Promise.allSettled Parallel, some may fail
Promise.race Timeout or first-wins

Never readFileSync / sync methods in production. Offload CPU work to worker threads. Stream large payloads.

Discipline

  • For non-trivial changes, pause and ask: "is there a more elegant way?" Skip for obvious fixes.
  • Simplicity first — every change as simple as possible, impact minimal code
  • Only touch what's necessary — avoid introducing unrelated changes
  • No hacky workarounds — if a fix feels wrong, step back and implement the clean solution

References

Files (whetstone)
  • references
    • database-production.md 1.2 KB
      # Database & Production
      
      ## Database
      
      ORM: **Drizzle** (SQL-like, lightweight) or **Prisma** (schema-first, migrations built-in)
      
      Connection pooling: `new Pool({ max: 20, idleTimeoutMillis: 30000, connectionTimeoutMillis: 2000 })`
      
      Transactions: `BEGIN` → ops → `COMMIT` / catch → `ROLLBACK` / finally → `client.release()`
      
      Index strategies:
      ```sql
      CREATE INDEX idx_col ON t(col);                            -- equality
      CREATE INDEX idx_multi ON t(col1, col2);                   -- composite
      CREATE INDEX idx_partial ON t(col) WHERE status = 'active'; -- filtered
      CREATE INDEX idx_cover ON t(col) INCLUDE (name);            -- covering
      ```
      
      Always `EXPLAIN ANALYZE` slow queries. Watch for sequential scans on large tables.
      
      ## Production
      
      - **Docker**: multi-stage build — `node:20-alpine` builder + prod image with `npm ci --omit=dev`
      - **Process**: PM2 cluster mode (`instances: 'max'`) or container orchestration
      - **Shutdown**: SIGTERM → stop accepting connections → drain in-flight → close DB pool
      - **Logging**: Pino (structured JSON), not console.log
      - **Health**: `GET /health` returning `{ status: 'ok' }`
      - **Compression**: gzip/brotli via middleware
      
    • security.md 1.3 KB
      # Authentication & Security
      
      ## Authentication
      
      - **Access token**: JWT, 15min expiry, payload: `{ userId, email }`
      - **Refresh token**: JWT, 7d expiry, stored in DB (revocable)
      - **Passwords**: bcrypt (10+ rounds) or argon2
      - **Middleware**: extract `Bearer` token → `jwt.verify` → attach `req.user` → `next()`
      - **Authorization**: after auth, check role or resource ownership per request
      - Always return generic "Invalid credentials" — never reveal if user exists
      
      ## Security Checklist
      
      - [ ] All inputs validated (Zod/TypeBox at route boundary)
      - [ ] Parameterized queries only (no string concatenation)
      - [ ] Passwords hashed (bcrypt/argon2, never plaintext)
      - [ ] JWT: verify signature + expiry, short-lived access tokens
      - [ ] Rate limiting (express-rate-limit + Redis store, stricter on auth endpoints)
      - [ ] Security headers (Helmet)
      - [ ] HTTPS everywhere in production
      - [ ] CORS restricted to specific origins
      - [ ] Secrets from env vars only, validated at startup
      - [ ] `npm audit` regularly
      - [ ] No stack traces in production error responses
      - [ ] Authorization per request, not just authentication
      
      OWASP API Top 10: Broken Object-Level Auth | Broken Auth | Broken Property-Level Auth | Unrestricted Resource Consumption | Broken Function-Level Auth | Sensitive Business Flow | SSRF | Security Misconfiguration | Improper Inventory | Unsafe API Consumption
      
    • typescript-config.md 2.2 KB
      # TypeScript Configuration & Patterns
      
      ## Configuration
      
      tsconfig essentials:
      ```json
      {
        "compilerOptions": {
          "target": "ES2022",
          "module": "NodeNext",
          "moduleResolution": "NodeNext",
          "strict": true,
          "noUncheckedIndexedAccess": true,
          "exactOptionalPropertyTypes": true,
          "isolatedModules": true,
          "skipLibCheck": true,
          "outDir": "./dist",
          "rootDir": "./src"
        }
      }
      ```
      
      ESM-first: set `"type": "module"` in package.json.
      
      Dev: `tsx watch src/server.ts` | Build: `tsc` | Node 22+: `--experimental-strip-types` for scripts
      
      Type-safe env at startup — Zod schema as source of truth:
      ```typescript
      import { z } from 'zod';
      const EnvSchema = z.object({
        PORT: z.coerce.number().default(3000),
        DATABASE_URL: z.string().url(),
        JWT_SECRET: z.string().min(32),
      });
      export type Env = z.infer<typeof EnvSchema>;
      export const env = EnvSchema.parse(process.env);
      ```
      
      ## Type Patterns
      
      **Branded types** — prevent mixing domain primitives:
      ```typescript
      type Brand<K, T> = K & { __brand: T };
      type UserId = Brand<string, 'UserId'>;
      type OrderId = Brand<string, 'OrderId'>;
      // Compiler prevents passing OrderId where UserId expected
      ```
      
      **Discriminated unions** — make illegal states unrepresentable:
      ```typescript
      type Result<T> = { ok: true; data: T } | { ok: false; error: string };
      ```
      
      **Exhaustive switch** — catch missing cases at compile time:
      ```typescript
      default: { const _: never = status; throw new Error(`Unhandled: ${_}`); }
      ```
      
      **Type guards** for runtime narrowing:
      ```typescript
      function isAppError(err: unknown): err is AppError { return err instanceof AppError; }
      ```
      
      **`satisfies`** — validate constraints, preserve literal types:
      ```typescript
      const config = { port: 3000, host: 'localhost' } satisfies Record<string, string | number>;
      ```
      
      **`as const`** — literal unions from arrays:
      ```typescript
      const ROLES = ['admin', 'user', 'guest'] as const;
      type Role = typeof ROLES[number]; // 'admin' | 'user' | 'guest'
      ```
      
      ## Compiler Performance
      
      - `incremental: true` — 50-90% faster rebuilds
      - `skipLibCheck: true` — skip .d.ts checking
      - `isolatedModules: true` — enables fast single-file transpilation
      - Avoid deeply nested generics and large unions (>100 members)
      - Diagnose: `npx tsc --extendedDiagnostics`
      
  • manifest.json 1.2 KB
    {
      "query": "nodejs-backend",
      "search_queries": [
        "nodejs",
        "javascript backend",
        "typescript"
      ],
      "instructions": "Exclude front-end related patterns & concepts. Include TypeScript.",
      "generated": "2026-02-13",
      "token_count": 1161,
      "sources": [
        {
          "id": "wshobson/agents/typescript-advanced-types",
          "installs": 4473,
          "sha1": "dc08d7b95f19533cf758739162f0fd82a5217f39"
        },
        {
          "id": "wshobson/agents/nodejs-backend-patterns",
          "installs": 3541,
          "sha1": "b9aed50c84f7253e3805c33ab8556904c3e45a48"
        },
        {
          "id": "sickn33/antigravity-awesome-skills/typescript-expert",
          "installs": 1050,
          "sha1": "a85100f5e5fb7b03bfdbbb06095b29602da45cd5"
        },
        {
          "id": "sickn33/antigravity-awesome-skills/api-security-best-practices",
          "installs": 815,
          "sha1": "68e4f51e2702a060146107ee64e51059fe2b2fda"
        },
        {
          "id": "sickn33/antigravity-awesome-skills/nodejs-best-practices",
          "installs": 521,
          "sha1": "26900cb9792a83bd4c846c96b9083b93fc23c60d"
        },
        {
          "id": "pproenca/dot-skills/typescript",
          "installs": 480,
          "sha1": "5ce69fa74cdae95a345d61b28eec32e68b9cc341"
        }
      ]
    }
    
  • SKILL.md 4.3 KB
    ---
    name: nodejs-backend
    description: >-
      Node.js backend patterns: framework selection, layered architecture, TypeScript,
      validation, error handling, security, production deployment. Use when building
      REST APIs, Express/Fastify servers, microservices, or server-side TypeScript.
    ---
    
    # Node.js Backend
    
    ## Framework Selection
    
    | Context | Choose | Why |
    |---------|--------|-----|
    | Edge/Serverless | Hono | Zero-dep, fastest cold starts |
    | Performance API | Fastify | 2-3x faster than Express, built-in schema validation |
    | Enterprise/team | NestJS | DI, decorators, structured conventions |
    | Legacy/ecosystem | Express | Most middleware, widest adoption |
    
    Ask user: deployment target, cold start needs, team experience, existing codebase.
    
    ## Architecture
    
    ```
    src/
    ├── routes/          # HTTP: parse request, call service, format response
    ├── middleware/       # Auth, validation, rate limiting, logging
    ├── services/        # Business logic (no HTTP types)
    ├── repositories/    # Data access only (queries, ORM)
    ├── config/          # Env, DB pool, constants
    └── types/           # Shared TypeScript interfaces
    ```
    
    - Routes never contain business logic
    - Services never import Request/Response
    - Repositories never throw HTTP errors
    - For scripts/prototypes: single file is fine — ask "will this grow?"
    
    ## TypeScript Rules
    
    - Use `import type { }` for type-only imports — eliminates runtime overhead
    - Prefer `interface` for object shapes (2-5x faster type resolution than intersections)
    - Prefer `unknown` over `any` — forces explicit narrowing
    - Use `z.infer<typeof Schema>` as single source of truth — never duplicate types and schemas
    - Minimize `as` assertions — use type guards instead
    - Add explicit return types to exported functions (faster declaration emit)
    - Untyped package? `declare module 'pkg' { const v: unknown; export default v; }` in `types/ambient.d.ts`
    
    ## Validation
    
    **Zod** (TypeScript inference) or **TypeBox** (Fastify native). Validate at boundaries only: request entry, before DB ops, env vars at startup. Use `.extend()`, `.pick()`, `.omit()`, `.partial()`, `.merge()` for DRY schemas.
    
    ## Error Handling
    
    Custom error hierarchy: `AppError(message, statusCode, isOperational)` → `ValidationError(400)`, `NotFoundError(404)`, `UnauthorizedError(401)`, `ForbiddenError(403)`, `ConflictError(409)`
    
    Centralized handler middleware:
    - `AppError` → return `{ error: message }` with statusCode
    - Unknown → log full stack, return 500 + generic message in production
    - Async wrapper: `const asyncHandler = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);`
    
    Codes: 400 bad input | 401 no auth | 403 no permission | 404 missing | 409 conflict | 422 business rule | 429 rate limited | 500 server fault
    
    ## API Design
    
    - **Resources**: plural nouns (`/users`), max 2 nesting levels (`/users/:id/orders`)
    - **Methods**: GET read | POST create | PUT replace | PATCH partial | DELETE remove
    - **Versioning**: URL path `/api/v1/`
    - **Response**: `{ data, pagination?: { page, limit, total, totalPages } }`
    - **Errors**: `{ error: { code, message, details? } }`
    - **Queries**: `?page=1&limit=20&status=active&sort=createdAt,desc`
    - Return `Location` header on 201. Use 204 for successful DELETE with no body.
    
    ## Async Patterns
    
    | Pattern | Use When |
    |---------|----------|
    | `async/await` | Sequential operations |
    | `Promise.all` | Parallel independent ops |
    | `Promise.allSettled` | Parallel, some may fail |
    | `Promise.race` | Timeout or first-wins |
    
    Never `readFileSync` / sync methods in production. Offload CPU work to worker threads. Stream large payloads.
    
    ## Discipline
    
    - For non-trivial changes, pause and ask: "is there a more elegant way?" Skip for obvious fixes.
    - Simplicity first — every change as simple as possible, impact minimal code
    - Only touch what's necessary — avoid introducing unrelated changes
    - No hacky workarounds — if a fix feels wrong, step back and implement the clean solution
    
    ## References
    
    - [TypeScript config](references/typescript-config.md) — tsconfig, ESM, branded types, compiler performance
    - [Security](references/security.md) — JWT, password hashing, rate limiting, OWASP
    - [Database & production](references/database-production.md) — connection pooling, transactions, Docker, logging
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related