# Abhayla Claude Best Practices Node Backend Dev

> Node.js Backend Development

- Skill: `tomevault-io/abhayla-claude-best-practices-node-backend-dev` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/abhayla-claude-best-practices-node-backend-dev`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/abhayla-claude-best-practices-node-backend-dev/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/abhayla-claude-best-practices-node-backend-dev

---


# Node.js Backend Development

Build production-ready Node.js backend APIs with Express, Hono, or ElysiaJS. Includes routing,
middleware, validation, database access, error handling, auth, WebSocket, and testing patterns.

**Request:** $ARGUMENTS

---

## STEP 1: Project Setup

### Framework Decision Table

| Framework  | Best For                           | Runtime              | Validation               |
|------------|------------------------------------|----------------------|--------------------------|
| Express    | Mature ecosystem, middleware depth | Node.js              | Manual (Zod)             |
| Hono       | Edge/lightweight, multi-runtime   | Node/Bun/Deno/Edge   | @hono/zod-validator      |
| ElysiaJS   | Bun-native, type-safe DI          | Bun                  | TypeBox (built-in)       |
| Fastify    | Plugin ecosystem, schema-first    | Node.js              | JSON Schema (built-in)   |

### Initialize Project

```bash
# Node.js (Express or Hono)
mkdir my-api && cd my-api
npm init -y
npm install typescript tsx @types/node --save-dev
npx tsc --init --strict --module nodenext --moduleResolution nodenext --outDir dist

# Bun (ElysiaJS)
bun init
bun add elysia
```

### Project Structure

```
src/
  index.ts            # Entry point
  routes/
    v1/               # Versioned routes
      users.ts
      health.ts
  middleware/
    auth.ts
    error-handler.ts
    cors.ts
    rate-limit.ts
  services/           # Business logic layer
    user.service.ts
  db/
    index.ts          # DB client singleton
    schema/           # ORM schema definitions
    migrations/       # Migration files
  config/
    env.ts            # Environment validation at startup
  types/
    index.ts          # Shared type definitions
```


**Read:** `references/framework-setup.md` for entry point examples per framework and environment validation with Zod.

---

## STEP 2: Routing

**Read:** `references/routing-examples.md` for complete route definitions per framework (Express, Hono, ElysiaJS).

### Route Conventions

- One file per domain (users, orders, products) inside `routes/v1/`.
- API versioning: all routes under `/api/v1/`. When v2 is needed, create `routes/v2/` and mount separately.
- Health check at `GET /api/health` (outside versioned prefix).

---

## STEP 3: Middleware

**Read:** `references/middleware-examples.md` for auth, CORS, security headers, logging, and rate limiting examples per framework.

---

## STEP 4: Validation

**Read:** `references/validation-examples.md` for Zod schema definitions and validation patterns per framework (Express, Hono, ElysiaJS).

---

## STEP 5: Database Access

**Read:** `references/database-setup.md` for ORM setup (Prisma, Drizzle, raw pg), connection pooling, and service layer pattern.

---

## STEP 6: Error Handling

**Read:** `references/error-handling-examples.md` for AppError class, PostgreSQL error mapping, and global error handlers per framework.

---

## STEP 7: Auth Patterns

**Read:** `references/auth-patterns.md` for session-based (Better Auth), token-based (JWT), dev bypass, and optional auth middleware.

---

## STEP 8: Real-time (WebSocket)

> Extended examples with all three frameworks: `references/websocket.md`

### Core Patterns

- **ElysiaJS**: Use `.ws()` with built-in Bun WebSocket. Supports `subscribe`/`publish` for topic-based pub/sub.
- **Express/Hono on Node.js**: Use `ws` library with `WebSocketServer` attached to the HTTP server.
- **Hono**: Use `@hono/node-ws` with `createNodeWebSocket()` and `upgradeWebSocket()` helper.
- Auth on WS connect: validate token from query param or first message. Close with 4001/4003 codes on failure.

### Typed Message Protocol (Discriminated Union)

```typescript
type WsMessage =
  | { type: 'join'; payload: { roomId: string } }
  | { type: 'leave'; payload: { roomId: string } }
  | { type: 'message'; payload: { roomId: string; content: string } }
  | { type: 'typing'; payload: { roomId: string; isTyping: boolean } };
```

### Broadcaster Pattern

Hold a server reference so services can publish events without importing WebSocket internals.

```typescript
class Broadcaster {
  private wss: WebSocketServer | null = null;
  attach(wss: WebSocketServer) { this.wss = wss; }

  publish(topic: string, data: unknown) {
    if (!this.wss) return;
    const message = JSON.stringify({ topic, data });
    this.wss.clients.forEach((client) => {
      if (client.readyState === WebSocket.OPEN) client.send(message);
    });
  }
}
export const broadcaster = new Broadcaster();
```

---

## STEP 9: Testing

**Read:** `references/testing-examples.md` for framework-specific test invocation, validation tests, service mocking, and DB cleanup.

---

## Troubleshooting

| Problem | Cause | Fix |
|---------|-------|-----|
| CORS errors in browser | Missing or misconfigured CORS middleware | Mount CORS middleware before routes. Verify allowed origins include the frontend URL with protocol. |
| Connection pool exhaustion | Too many PrismaClient instances or unclosed connections | Use singleton pattern (Step 5). Set `max` pool size to 10-20. Close pools on SIGTERM. |
| Middleware not executing | Middleware mounted after the route it should protect | Mount middleware before routes. Express processes middleware in registration order. |
| Async error crashes process | Unhandled promise rejection in route handler | Express: wrap handlers with `try/catch` and call `next(err)`, or use `express-async-errors`. Hono/Elysia: errors in async handlers are caught automatically. |
| Port already in use (EADDRINUSE) | Previous process still bound to the port | Kill the old process: `lsof -ti :3000 \| xargs kill` (Unix) or `npx kill-port 3000`. |
| ECONNREFUSED to database | Database not running or wrong connection string | Verify DATABASE_URL, confirm the database is accepting connections, check firewall rules. |
| Request body undefined | Missing JSON body parser middleware | Express: add `app.use(express.json())` before routes. Hono/Elysia: built-in, no extra step needed. |
| TypeBox validation errors cryptic | Default ElysiaJS error format lacks field-level detail | Use `.onError()` hook to reformat validation errors into the standard `{ error: { code, message, details } }` shape. |
| Drizzle migrations out of sync | Schema changed without generating migration | Run `npx drizzle-kit generate` after schema changes, then `npx drizzle-kit migrate`. |
| JWT token rejected after deploy | Different JWT_SECRET between environments | Validate JWT_SECRET in env.ts at startup. Use the same secret across all instances in an environment. |

---

## CRITICAL RULES

### MUST DO

- Always parameterize SQL queries -- never use string interpolation for user data
- Implement graceful shutdown: close DB pools and HTTP server on SIGTERM/SIGINT
- Use a consistent JSON error response format (`{ error: { code, message, details? } }`) across all endpoints
- Provide a health check endpoint at `GET /api/health` that verifies DB connectivity
- Validate all user input at the route boundary -- services trust validated data
- Use connection pooling for PostgreSQL (max 10-20 connections per instance)
- Validate environment variables at startup with a schema -- fail fast on missing config
- Use `createMiddleware()` from `hono/factory` when extracting Hono middleware to separate files (preserves type safety)
- Register CORS middleware before routes -- especially before WebSocket upgrade handlers in Hono
- Use the singleton pattern for Prisma in development to prevent hot-reload connection leaks

### MUST NOT DO

- Expose stack traces in production error responses -- log them server-side, return a generic message to the client
- Use synchronous file I/O or heavy computation in request handlers -- offload to worker threads or a job queue
- Trust client-submitted user IDs for authorization -- always derive the user identity from the auth token
- Skip error handling middleware -- unhandled rejections crash the Node.js process
- Store secrets in code -- use environment variables validated at startup
- Mount auth middleware after routes -- middleware registration order determines execution order
- Create a new PrismaClient per request -- reuse a single instance via the singleton pattern
- Use `express.json()` after route definitions -- body parsing must happen before handlers execute

---
> Source: [abhayla/claude-best-practices](https://github.com/abhayla/claude-best-practices) — distributed by [TomeVault](https://tomevault.io).
<!-- tomevault:4.0:skill_md:2026-06-15 -->

