{"id":"7b6228e5-ac26-4306-84c8-519b56a0bd83","entityType":"agent","slug":"clawhub-skills-1kalin-afrexai-nodejs-production","name":"afrexai-nodejs-production","canonicalUrl":"https://www.xpersona.co/agent/clawhub-skills-1kalin-afrexai-nodejs-production","canonicalPath":"/agent/clawhub-skills-1kalin-afrexai-nodejs-production","generatedAt":"2026-10-09T18:14:22.932Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-04-15T00:45:39.800Z","emptyReason":null},"description":"Node.js & TypeScript Production Engineering Node.js & TypeScript Production Engineering Complete methodology for building production-grade Node.js backends with TypeScript. Covers architecture, frameworks, error handling, database patterns, security, testing, observability, and deployment. --- Quick Health Check (/16) Run through these 8 signals — score 0 (missing) or 2 (present): | # | Signal | Check | |---|--------|-------| | 1 | Strict TypeScript | \"strict\"","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. Last updated 4/15/2026.","installCommand":"clawhub skill install skills:1kalin:afrexai-nodejs-production","sourceUrl":"https://github.com/openclaw/skills/tree/main/skills/1kalin/afrexai-nodejs-production","homepage":null,"primaryLinks":[{"label":"View on ClawHub","url":"https://github.com/openclaw/skills/tree/main/skills/1kalin/afrexai-nodejs-production","kind":"source"}],"safetyScore":84,"overallRank":62,"popularityScore":50,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Node.js & TypeScript Production Engineering Node.js & TypeScript Production Engineering Complete methodology for building production-grade Node.js backends with"},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-04-15T00:45:39.800Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[{"label":"both","status":"self-declared"}],"verifiedCount":0,"selfDeclaredCount":2,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"},{"key":"both","type":"capability","support":"supported","confidenceSource":"profile","notes":"Declared in agent profile metadata"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile capability:both|supported|profile"}},"adoption":{"evidence":{"source":"no-adoption-signals","verified":false,"confidence":"low","updatedAt":"2026-04-15T00:45:39.800Z","emptyReason":"No source adoption metrics were available."},"stars":null,"forks":null,"downloads":null,"packageName":null,"latestVersion":null,"tractionLabel":null},"release":{"evidence":{"source":"agent-index","verified":false,"confidence":"medium","updatedAt":"2026-02-25T06:17:45.729Z","emptyReason":null},"lastUpdatedAt":"2026-04-15T00:45:39.800Z","lastCrawledAt":"2026-02-25T06:17:45.729Z","lastIndexedAt":null,"nextCrawlAt":"2026-02-26T06:17:45.729Z","lastVerifiedAt":null,"highlights":[]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install skills:1kalin:afrexai-nodejs-production","setupComplexity":"low","setupSteps":["Setup complexity is LOW. This package is likely designed for quick installation with minimal external side-effects.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-skills-1kalin-afrexai-nodejs-production/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-skills-1kalin-afrexai-nodejs-production/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-skills-1kalin-afrexai-nodejs-production/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-skills-1kalin-afrexai-nodejs-production/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-skills-1kalin-afrexai-nodejs-production/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-skills-1kalin-afrexai-nodejs-production/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-09T18:14:22.932Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-skills-1kalin-afrexai-nodejs-production/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-skills-1kalin-afrexai-nodejs-production/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-skills-1kalin-afrexai-nodejs-production/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-skills-1kalin-afrexai-nodejs-production/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-04-15T00:45:39.800Z","emptyReason":null},"readme":"# Node.js & TypeScript Production Engineering\n\nComplete methodology for building production-grade Node.js backends with TypeScript. Covers architecture, frameworks, error handling, database patterns, security, testing, observability, and deployment.\n\n---\n\n## Quick Health Check (/16)\n\nRun through these 8 signals — score 0 (missing) or 2 (present):\n\n| # | Signal | Check |\n|---|--------|-------|\n| 1 | Strict TypeScript | `\"strict\": true` in tsconfig, no `any` escape hatches |\n| 2 | Structured errors | Custom error classes with codes, not string throws |\n| 3 | Input validation | Zod/Valibot on every external boundary |\n| 4 | Database migrations | Version-controlled, reversible, CI-enforced |\n| 5 | Health endpoints | `/health` (liveness) + `/ready` (readiness) with dependency checks |\n| 6 | Structured logging | JSON logs with request IDs, no `console.log` in prod |\n| 7 | Test coverage | >80% unit, integration tests for critical paths |\n| 8 | Graceful shutdown | SIGTERM handler drains connections before exit |\n\n**Score interpretation:** 0-6 = critical gaps, 8-10 = needs work, 12-14 = solid, 16 = production-grade.\n\n---\n\n## Phase 1: Architecture & Project Structure\n\n### Framework Selection Matrix\n\n| Framework | Best For | Throughput | Ecosystem | Learning Curve | TypeScript |\n|-----------|----------|-----------|-----------|----------------|------------|\n| **Hono** | Edge/serverless, lightweight APIs | ⭐⭐⭐⭐⭐ | Growing fast | Low | Native |\n| **Fastify** | High-perf monoliths, JSON APIs | ⭐⭐⭐⭐⭐ | Mature | Medium | Excellent |\n| **Express** | Legacy, max middleware ecosystem | ⭐⭐⭐ | Massive | Low | Via @types |\n| **NestJS** | Enterprise, large teams, DI-heavy | ⭐⭐⭐⭐ | Large | High | Native |\n| **Elysia** | Bun-first, type-safe APIs | ⭐⭐⭐⭐⭐ | Small | Low | Native |\n| **tRPC** | Full-stack TS, type-safe RPC | N/A (layer) | Growing | Medium | Native |\n\n**Decision rules:**\n- New project, small team → **Hono** (portable, fast, minimal)\n- High throughput JSON API → **Fastify** (proven, benchmarked)\n- Enterprise, 10+ developers → **NestJS** (structure enforced)\n- Full-stack TypeScript monorepo → **tRPC** (end-to-end types)\n- Existing Express codebase → Stay on Express, migrate incrementally\n- Bun runtime → **Elysia** or **Hono**\n\n### Recommended Project Structure\n\n```\nsrc/\n├── index.ts              # Entry point — bootstrap only\n├── app.ts                # App factory (createApp)\n├── config/\n│   ├── env.ts            # Environment validation (Zod)\n│   └── database.ts       # DB connection config\n├── routes/\n│   ├── index.ts          # Route registry\n│   ├── users.ts          # /users routes\n│   └── orders.ts         # /orders routes\n├── services/\n│   ├── user.service.ts   # Business logic\n│   └── order.service.ts\n├── repositories/\n│   ├── user.repo.ts      # Data access (Drizzle/Prisma)\n│   └── order.repo.ts\n├── middleware/\n│   ├── auth.ts           # Authentication\n│   ├── validate.ts       # Request validation\n│   ├── error-handler.ts  # Global error handler\n│   └── request-id.ts     # Correlation ID\n├── errors/\n│   └── index.ts          # Custom error classes\n├── types/\n│   └── index.ts          # Shared types\n├── utils/\n│   └── index.ts          # Pure utility functions\n├── jobs/                 # Background jobs/queues\n│   └── email.job.ts\n└── __tests__/            # Tests mirror src/ structure\n    ├── services/\n    └── routes/\ndrizzle/                  # Database migrations\n├── 0001_create_users.sql\n└── meta/\ntsconfig.json\npackage.json\nDockerfile\ndocker-compose.yml\n.env.example\n```\n\n### 7 Architecture Rules\n\n1. **Routes → Services → Repositories** — never skip layers\n2. **Services contain business logic** — routes are thin (validate + call service + respond)\n3. **Repositories own data access** — services never import DB client directly\n4. **No circular dependencies** — dependency flow is strictly downward\n5. **One export per file** — makes imports predictable, testing easy\n6. **Config validated at startup** — fail fast, not at runtime\n7. **≤50 lines per function, ≤300 lines per file** — split when exceeded\n\n---\n\n## Phase 2: TypeScript Configuration\n\n### Production tsconfig.json\n\n```json\n{\n  \"compilerOptions\": {\n    \"target\": \"ES2022\",\n    \"module\": \"NodeNext\",\n    \"moduleResolution\": \"NodeNext\",\n    \"lib\": [\"ES2022\"],\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"strict\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"noImplicitOverride\": true,\n    \"noPropertyAccessFromIndexSignature\": true,\n    \"exactOptionalPropertyTypes\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"isolatedModules\": true,\n    \"declaration\": true,\n    \"declarationMap\": true,\n    \"sourceMap\": true,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": true\n  },\n  \"include\": [\"src\"],\n  \"exclude\": [\"node_modules\", \"dist\", \"**/*.test.ts\"]\n}\n```\n\n### 8 TypeScript Rules\n\n1. **Never use `any`** — use `unknown` and narrow, or define proper types\n2. **`noUncheckedIndexedAccess: true`** — forces null checks on array/object access\n3. **Branded types for IDs** — `type UserId = string & { __brand: 'UserId' }`\n4. **Zod schemas derive types** — `type User = z.infer<typeof UserSchema>`\n5. **Discriminated unions for states** — `{ status: 'active'; data: T } | { status: 'error'; error: E }`\n6. **`satisfies` over `as`** — preserves narrowed types: `config satisfies Config`\n7. **Enums → const objects** — `const Status = { ACTIVE: 'active', INACTIVE: 'inactive' } as const`\n8. **Return types on public APIs** — explicit return types on exported functions\n\n### Branded Types Pattern\n\n```typescript\n// types/branded.ts\ndeclare const __brand: unique symbol;\ntype Brand<T, B extends string> = T & { [__brand]: B };\n\nexport type UserId = Brand<string, 'UserId'>;\nexport type OrderId = Brand<string, 'OrderId'>;\nexport type Email = Brand<string, 'Email'>;\n\nexport function UserId(id: string): UserId { return id as UserId; }\nexport function Email(email: string): Email {\n  if (!email.includes('@')) throw new ValidationError('Invalid email');\n  return email as Email;\n}\n```\n\n---\n\n## Phase 3: Environment & Configuration\n\n### Validated Configuration with Zod\n\n```typescript\n// config/env.ts\nimport { z } from 'zod';\n\nconst EnvSchema = z.object({\n  NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),\n  PORT: z.coerce.number().int().min(1).max(65535).default(3000),\n  DATABASE_URL: z.string().url(),\n  REDIS_URL: z.string().url().optional(),\n  JWT_SECRET: z.string().min(32),\n  JWT_EXPIRES_IN: z.string().default('15m'),\n  LOG_LEVEL: z.enum(['fatal', 'error', 'warn', 'info', 'debug', 'trace']).default('info'),\n  CORS_ORIGINS: z.string().transform(s => s.split(',')).default('*'),\n  RATE_LIMIT_MAX: z.coerce.number().default(100),\n  RATE_LIMIT_WINDOW_MS: z.coerce.number().default(60_000),\n});\n\nexport type Env = z.infer<typeof EnvSchema>;\n\n// Validate at import time — fail fast\nconst parsed = EnvSchema.safeParse(process.env);\nif (!parsed.success) {\n  console.error('❌ Invalid environment variables:', parsed.error.flatten().fieldErrors);\n  process.exit(1);\n}\n\nexport const env = parsed.data;\n```\n\n### 5 Configuration Rules\n\n1. **Validate ALL env vars at startup** — never read `process.env` directly elsewhere\n2. **Type-safe access only** — import `env` object, not `process.env`\n3. **Secrets via vault/env** — never hardcoded, never committed\n4. **`.env.example` always current** — document every var with description + default\n5. **Different configs per environment** — use `NODE_ENV` for branching, never feature flags in env\n\n---\n\n## Phase 4: Error Handling Architecture\n\n### Custom Error Hierarchy\n\n```typescript\n// errors/index.ts\nexport class AppError extends Error {\n  constructor(\n    message: string,\n    public readonly code: string,\n    public readonly statusCode: number,\n    public readonly details?: Record<string, unknown>,\n  ) {\n    super(message);\n    this.name = this.constructor.name;\n    Error.captureStackTrace(this, this.constructor);\n  }\n}\n\nexport class ValidationError extends AppError {\n  constructor(message: string, details?: Record<string, unknown>) {\n    super(message, 'VALIDATION_ERROR', 400, details);\n  }\n}\n\nexport class NotFoundError extends AppError {\n  constructor(resource: string, id: string) {\n    super(`${resource} not found: ${id}`, 'NOT_FOUND', 404, { resource, id });\n  }\n}\n\nexport class UnauthorizedError extends AppError {\n  constructor(message = 'Authentication required') {\n    super(message, 'UNAUTHORIZED', 401);\n  }\n}\n\nexport class ForbiddenError extends AppError {\n  constructor(message = 'Insufficient permissions') {\n    super(message, 'FORBIDDEN', 403);\n  }\n}\n\nexport class ConflictError extends AppError {\n  constructor(message: string, details?: Record<string, unknown>) {\n    super(message, 'CONFLICT', 409, details);\n  }\n}\n\nexport class RateLimitError extends AppError {\n  constructor(retryAfterMs: number) {\n    super('Too many requests', 'RATE_LIMITED', 429, { retryAfterMs });\n  }\n}\n```\n\n### Global Error Handler\n\n```typescript\n// middleware/error-handler.ts\nimport type { ErrorHandler } from 'hono';\nimport { AppError } from '../errors/index.js';\nimport { logger } from '../utils/logger.js';\n\nexport const errorHandler: ErrorHandler = (err, c) => {\n  const requestId = c.get('requestId');\n\n  if (err instanceof AppError) {\n    if (err.statusCode >= 500) {\n      logger.error({ err, requestId }, 'Server error');\n    } else {\n      logger.warn({ err, requestId }, 'Client error');\n    }\n\n    return c.json({\n      error: { code: err.code, message: err.message, details: err.details },\n    }, err.statusCode as any);\n  }\n\n  // Unexpected errors — don't leak internals\n  logger.error({ err, requestId }, 'Unhandled error');\n  return c.json({\n    error: { code: 'INTERNAL_ERROR', message: 'An unexpected error occurred' },\n  }, 500);\n};\n```\n\n### 6 Error Handling Rules\n\n1. **Throw custom errors, catch at boundary** — services throw, routes/middleware catch\n2. **Never throw strings** — always `throw new SomeError(message)`\n3. **Don't leak internals** — 5xx returns generic message, log the real error\n4. **Include error codes** — machine-readable codes for client handling\n5. **Validate early, fail fast** — Zod at route level before business logic\n6. **Async errors auto-caught** — use frameworks that handle rejected promises (Hono/Fastify do this natively)\n\n---\n\n## Phase 5: Input Validation\n\n### Zod Schema Patterns\n\n```typescript\n// routes/users.ts\nimport { z } from 'zod';\nimport { zValidator } from '@hono/zod-validator';\n\nconst CreateUserSchema = z.object({\n  email: z.string().email().max(255).toLowerCase(),\n  name: z.string().min(1).max(100).trim(),\n  role: z.enum(['user', 'admin']).default('user'),\n});\n\nconst PaginationSchema = z.object({\n  cursor: z.string().optional(),\n  limit: z.coerce.number().int().min(1).max(100).default(20),\n});\n\nconst UserIdParamSchema = z.object({\n  id: z.string().uuid(),\n});\n\n// Usage with Hono\napp.post('/users', zValidator('json', CreateUserSchema), async (c) => {\n  const body = c.req.valid('json'); // Fully typed!\n  const user = await userService.create(body);\n  return c.json({ data: user }, 201);\n});\n\napp.get('/users', zValidator('query', PaginationSchema), async (c) => {\n  const { cursor, limit } = c.req.valid('query');\n  const result = await userService.list({ cursor, limit });\n  return c.json({ data: result.items, nextCursor: result.nextCursor });\n});\n```\n\n### Validation Rules\n\n1. **Validate at the edge** — every route handler validates input before calling services\n2. **Schemas define the contract** — derive TypeScript types from Zod, not the other way around\n3. **Transform in schemas** — `.trim()`, `.toLowerCase()`, `.default()` in Zod, not in services\n4. **Reuse common schemas** — `PaginationSchema`, `DateRangeSchema`, `SortSchema`\n5. **Validate path params too** — UUIDs, slugs, numeric IDs\n\n---\n\n## Phase 6: Database Patterns\n\n### ORM Selection Guide\n\n| ORM | Best For | Type Safety | Migration | Query Builder | Learning Curve |\n|-----|----------|-------------|-----------|---------------|----------------|\n| **Drizzle** | SQL-first, edge, performance | ⭐⭐⭐⭐⭐ | Built-in | SQL-like | Low |\n| **Prisma** | Rapid dev, schema-first | ⭐⭐⭐⭐ | Built-in | Custom DSL | Low |\n| **Kysely** | SQL purists, complex queries | ⭐⭐⭐⭐⭐ | External | Type-safe SQL | Medium |\n| **TypeORM** | Legacy, decorator-style | ⭐⭐⭐ | Built-in | ORM-style | Medium |\n\n**Decision:** Drizzle for new projects (matches SQL mental model, best TypeScript inference, edge-compatible).\n\n### Drizzle Schema Example\n\n```typescript\n// drizzle/schema.ts\nimport { pgTable, text, timestamp, uuid, varchar, boolean, index } from 'drizzle-orm/pg-core';\n\nexport const users = pgTable('users', {\n  id: uuid('id').primaryKey().defaultRandom(),\n  email: varchar('email', { length: 255 }).notNull().unique(),\n  name: varchar('name', { length: 100 }).notNull(),\n  role: text('role', { enum: ['user', 'admin'] }).notNull().default('user'),\n  isActive: boolean('is_active').notNull().default(true),\n  createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),\n  updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),\n}, (table) => [\n  index('idx_users_email').on(table.email),\n  index('idx_users_created').on(table.createdAt),\n]);\n```\n\n### Repository Pattern\n\n```typescript\n// repositories/user.repo.ts\nimport { eq, desc, gt } from 'drizzle-orm';\nimport { db } from '../config/database.js';\nimport { users } from '../../drizzle/schema.js';\nimport type { UserId } from '../types/branded.js';\nimport { NotFoundError } from '../errors/index.js';\n\nexport class UserRepository {\n  async findById(id: UserId) {\n    const [user] = await db.select().from(users).where(eq(users.id, id)).limit(1);\n    if (!user) throw new NotFoundError('User', id);\n    return user;\n  }\n\n  async list(opts: { cursor?: string; limit: number }) {\n    const query = db.select().from(users).orderBy(desc(users.createdAt)).limit(opts.limit + 1);\n    if (opts.cursor) query.where(gt(users.id, opts.cursor));\n    const rows = await query;\n    const hasMore = rows.length > opts.limit;\n    return { items: rows.slice(0, opts.limit), nextCursor: hasMore ? rows[opts.limit - 1].id : undefined };\n  }\n\n  async create(data: { email: string; name: string; role?: string }) {\n    const [user] = await db.insert(users).values(data).returning();\n    return user;\n  }\n\n  async update(id: UserId, data: Partial<{ name: string; role: string; isActive: boolean }>) {\n    const [user] = await db.update(users).set({ ...data, updatedAt: new Date() }).where(eq(users.id, id)).returning();\n    if (!user) throw new NotFoundError('User', id);\n    return user;\n  }\n}\n```\n\n### Database Rules\n\n1. **Cursor-based pagination** — never OFFSET for user-facing lists\n2. **Migrations in version control** — `drizzle-kit generate` → commit → `drizzle-kit migrate` in CI\n3. **Connection pooling** — use `pg` pool (min: 2, max: 10 per process) or serverless driver for edge\n4. **Transactions for multi-write** — `db.transaction(async (tx) => { ... })`\n5. **Index query patterns** — add indexes for WHERE + ORDER BY columns, monitor slow queries\n6. **SQLite for dev, Postgres for prod** — Drizzle supports both with same schema (use `better-sqlite3` locally)\n\n---\n\n## Phase 7: Authentication & Authorization\n\n### JWT Auth Middleware (Hono)\n\n```typescript\n// middleware/auth.ts\nimport { jwt } from 'hono/jwt';\nimport { env } from '../config/env.js';\nimport type { UserId } from '../types/branded.js';\n\ntype JwtPayload = { sub: UserId; role: 'user' | 'admin'; iat: number; exp: number };\n\nexport const authenticate = jwt({ secret: env.JWT_SECRET });\n\nexport const requireRole = (...roles: string[]) => {\n  return async (c: any, next: any) => {\n    const payload = c.get('jwtPayload') as JwtPayload;\n    if (!roles.includes(payload.role)) {\n      throw new ForbiddenError(`Required role: ${roles.join(' or ')}`);\n    }\n    await next();\n  };\n};\n```\n\n### Security Checklist\n\n| # | Item | Priority |\n|---|------|----------|\n| 1 | Helmet/security headers (CSP, HSTS, X-Frame) | P0 |\n| 2 | Rate limiting per IP + per user | P0 |\n| 3 | Input validation on every endpoint | P0 |\n| 4 | CORS configured (not `*` in prod) | P0 |\n| 5 | JWT short-lived (15m) + refresh token rotation | P0 |\n| 6 | Password hashing (argon2id, cost ≥ 3) | P0 |\n| 7 | SQL injection prevention (parameterized queries) | P0 |\n| 8 | Request size limits (1MB default) | P1 |\n| 9 | Dependency audit (`npm audit`, Snyk) | P1 |\n| 10 | API key scoping (read-only, write, admin) | P1 |\n\n---\n\n## Phase 8: Structured Logging & Observability\n\n### Pino Logger Setup\n\n```typescript\n// utils/logger.ts\nimport pino from 'pino';\nimport { env } from '../config/env.js';\n\nexport const logger = pino({\n  level: env.LOG_LEVEL,\n  ...(env.NODE_ENV === 'development' && { transport: { target: 'pino-pretty' } }),\n  serializers: { err: pino.stdSerializers.err },\n  redact: ['req.headers.authorization', '*.password', '*.token', '*.secret'],\n  formatters: {\n    level: (label) => ({ level: label }),\n  },\n});\n```\n\n### Request ID Middleware\n\n```typescript\n// middleware/request-id.ts\nimport { randomUUID } from 'node:crypto';\n\nexport const requestId = () => {\n  return async (c: any, next: any) => {\n    const id = c.req.header('x-request-id') || randomUUID();\n    c.set('requestId', id);\n    c.header('x-request-id', id);\n    await next();\n  };\n};\n```\n\n### Request Logging Middleware\n\n```typescript\n// middleware/request-logger.ts\nimport { logger } from '../utils/logger.js';\n\nexport const requestLogger = () => {\n  return async (c: any, next: any) => {\n    const start = performance.now();\n    const requestId = c.get('requestId');\n\n    await next();\n\n    const duration = Math.round(performance.now() - start);\n    const level = c.res.status >= 500 ? 'error' : c.res.status >= 400 ? 'warn' : 'info';\n\n    logger[level]({\n      requestId,\n      method: c.req.method,\n      path: c.req.path,\n      status: c.res.status,\n      durationMs: duration,\n      userAgent: c.req.header('user-agent'),\n    }, `${c.req.method} ${c.req.path} ${c.res.status} ${duration}ms`);\n  };\n};\n```\n\n### Health Endpoints\n\n```typescript\n// routes/health.ts\napp.get('/health', (c) => c.json({ status: 'ok', timestamp: new Date().toISOString() }));\n\napp.get('/ready', async (c) => {\n  const checks = {\n    database: false,\n    redis: false,\n  };\n\n  try {\n    await db.execute(sql`SELECT 1`);\n    checks.database = true;\n  } catch {}\n\n  try {\n    await redis.ping();\n    checks.redis = true;\n  } catch {}\n\n  const ready = Object.values(checks).every(Boolean);\n  return c.json({ status: ready ? 'ready' : 'degraded', checks }, ready ? 200 : 503);\n});\n```\n\n---\n\n## Phase 9: Testing Strategy\n\n### Test Pyramid\n\n| Level | Coverage Target | Tools | What to Test |\n|-------|----------------|-------|-------------|\n| **Unit** | >80% | Vitest | Services, utils, pure logic |\n| **Integration** | Critical paths | Vitest + testcontainers | Routes → DB round-trip |\n| **E2E** | Happy paths | Vitest + supertest | Full HTTP request cycle |\n| **Contract** | API boundaries | Vitest | Request/response shapes |\n\n### Vitest Configuration\n\n```typescript\n// vitest.config.ts\nimport { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n  test: {\n    globals: true,\n    environment: 'node',\n    include: ['src/**/*.test.ts'],\n    coverage: { provider: 'v8', reporter: ['text', 'lcov'], thresholds: { lines: 80, branches: 75 } },\n    setupFiles: ['./src/__tests__/setup.ts'],\n    pool: 'forks', // Isolation for DB tests\n  },\n});\n```\n\n### Service Unit Test Pattern\n\n```typescript\n// __tests__/services/user.service.test.ts\nimport { describe, it, expect, vi, beforeEach } from 'vitest';\nimport { UserService } from '../../services/user.service.js';\nimport type { UserRepository } from '../../repositories/user.repo.js';\n\ndescribe('UserService', () => {\n  let service: UserService;\n  let repo: jest.Mocked<UserRepository>;\n\n  beforeEach(() => {\n    repo = { findById: vi.fn(), create: vi.fn(), update: vi.fn(), list: vi.fn() } as any;\n    service = new UserService(repo);\n  });\n\n  it('creates user with normalized email', async () => {\n    repo.create.mockResolvedValue({ id: '1', email: 'test@example.com', name: 'Test' } as any);\n    const result = await service.create({ email: 'Test@Example.COM', name: 'Test' });\n    expect(repo.create).toHaveBeenCalledWith(expect.objectContaining({ email: 'test@example.com' }));\n    expect(result.email).toBe('test@example.com');\n  });\n});\n```\n\n### Integration Test Pattern\n\n```typescript\n// __tests__/routes/users.test.ts\nimport { describe, it, expect, beforeAll, afterAll } from 'vitest';\nimport { createApp } from '../../app.js';\nimport { testDb, migrate, cleanup } from '../helpers/db.js';\n\ndescribe('POST /users', () => {\n  let app: any;\n\n  beforeAll(async () => {\n    await migrate(testDb);\n    app = createApp({ db: testDb });\n  });\n\n  afterAll(async () => { await cleanup(testDb); });\n\n  it('creates user and returns 201', async () => {\n    const res = await app.request('/users', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify({ email: 'new@example.com', name: 'New User' }),\n    });\n\n    expect(res.status).toBe(201);\n    const body = await res.json();\n    expect(body.data.email).toBe('new@example.com');\n  });\n\n  it('rejects invalid email with 400', async () => {\n    const res = await app.request('/users', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify({ email: 'not-an-email', name: 'Bad' }),\n    });\n\n    expect(res.status).toBe(400);\n  });\n});\n```\n\n### 7 Testing Rules\n\n1. **Test behavior, not implementation** — assert outputs, not internal method calls\n2. **Each test is independent** — no shared mutable state between tests\n3. **Name tests as specifications** — `it('rejects expired JWT with 401')`\n4. **Fast unit tests, isolated integration tests** — use `pool: 'forks'` for DB tests\n5. **Mock at boundaries** — mock repositories in service tests, real DB in integration tests\n6. **Test error paths** — 400s, 404s, 409s, 500s, not just happy paths\n7. **CI enforces coverage** — fail build if coverage drops below threshold\n\n---\n\n## Phase 10: Graceful Shutdown & Process Management\n\n### Shutdown Handler\n\n```typescript\n// index.ts\nimport { serve } from '@hono/node-server';\nimport { createApp } from './app.js';\nimport { logger } from './utils/logger.js';\nimport { env } from './config/env.js';\nimport { db } from './config/database.js';\n\nconst app = createApp();\nconst server = serve({ fetch: app.fetch, port: env.PORT });\n\nlogger.info({ port: env.PORT }, 'Server started');\n\nconst shutdown = async (signal: string) => {\n  logger.info({ signal }, 'Shutdown signal received');\n\n  // Stop accepting new connections\n  server.close(() => { logger.info('HTTP server closed'); });\n\n  // Drain existing work (give 10s)\n  const timeout = setTimeout(() => {\n    logger.error('Forced shutdown after timeout');\n    process.exit(1);\n  }, 10_000);\n\n  try {\n    // Close DB pool, Redis, queues, etc.\n    await db.$client.end();\n    logger.info('Database pool closed');\n    clearTimeout(timeout);\n    process.exit(0);\n  } catch (err) {\n    logger.error({ err }, 'Error during shutdown');\n    process.exit(1);\n  }\n};\n\nprocess.on('SIGTERM', () => shutdown('SIGTERM'));\nprocess.on('SIGINT', () => shutdown('SIGINT'));\n\n// Catch unhandled errors\nprocess.on('unhandledRejection', (err) => {\n  logger.fatal({ err }, 'Unhandled rejection');\n  process.exit(1);\n});\n```\n\n---\n\n## Phase 11: Production Deployment\n\n### Multi-Stage Dockerfile\n\n```dockerfile\n# Build stage\nFROM node:22-alpine AS builder\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci --ignore-scripts\nCOPY tsconfig.json ./\nCOPY src/ src/\nCOPY drizzle/ drizzle/\nRUN npm run build\nRUN npm ci --omit=dev --ignore-scripts\n\n# Production stage\nFROM node:22-alpine\nRUN apk add --no-cache tini dumb-init\nWORKDIR /app\nCOPY --from=builder /app/dist dist/\nCOPY --from=builder /app/drizzle drizzle/\nCOPY --from=builder /app/node_modules node_modules/\nCOPY --from=builder /app/package.json .\n\nENV NODE_ENV=production\nUSER node\nEXPOSE 3000\nENTRYPOINT [\"tini\", \"--\"]\nCMD [\"node\", \"dist/index.js\"]\n```\n\n### GitHub Actions CI\n\n```yaml\nname: CI\non: [push, pull_request]\njobs:\n  test:\n    runs-on: ubuntu-latest\n    services:\n      postgres:\n        image: postgres:16\n        env: { POSTGRES_DB: test, POSTGRES_PASSWORD: test }\n        ports: ['5432:5432']\n        options: >-\n          --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-node@v4\n        with: { node-version: 22, cache: npm }\n      - run: npm ci\n      - run: npm run lint\n      - run: npm run typecheck\n      - run: npm test -- --coverage\n        env: { DATABASE_URL: 'postgresql://postgres:test@localhost:5432/test' }\n      - run: npm run build\n```\n\n### Production Checklist\n\n**P0 — Mandatory:**\n- [ ] `NODE_ENV=production`\n- [ ] Strict TypeScript (`tsc --noEmit` passes)\n- [ ] All env vars validated at startup\n- [ ] Health + readiness endpoints\n- [ ] Graceful shutdown handles SIGTERM\n- [ ] Structured JSON logging (no console.log)\n- [ ] Error handler doesn't leak stack traces\n- [ ] Rate limiting enabled\n- [ ] CORS configured (not wildcard)\n- [ ] Security headers set\n\n**P1 — Recommended:**\n- [ ] Request ID tracing through all logs\n- [ ] Database connection pooling configured\n- [ ] Dependency audit clean (`npm audit`)\n- [ ] Docker image < 200MB\n- [ ] Response compression (gzip/brotli)\n- [ ] API versioning strategy decided\n- [ ] Monitoring/alerting configured\n\n---\n\n## Phase 12: Performance Optimization\n\n### Priority Stack\n\n| # | Technique | Impact | Effort |\n|---|-----------|--------|--------|\n| 1 | Connection pooling (DB + Redis) | High | Low |\n| 2 | Response caching (Redis/in-memory) | High | Medium |\n| 3 | Query optimization (indexes, N+1) | High | Medium |\n| 4 | JSON serialization (fast-json-stringify) | Medium | Low |\n| 5 | Compression middleware | Medium | Low |\n| 6 | Streaming responses for large payloads | Medium | Medium |\n| 7 | Worker threads for CPU-bound work | High | High |\n| 8 | HTTP/2 + keep-alive | Low | Low |\n\n### Common Optimizations\n\n```typescript\n// N+1 prevention — batch with dataloader or SQL JOIN\n// ❌ Bad: for loop with individual queries\nfor (const order of orders) {\n  order.user = await db.query.users.findFirst({ where: eq(users.id, order.userId) });\n}\n\n// ✅ Good: single query with join\nconst ordersWithUsers = await db\n  .select()\n  .from(orders)\n  .leftJoin(users, eq(orders.userId, users.id))\n  .where(inArray(orders.id, orderIds));\n\n// In-memory caching for hot data\nimport { LRUCache } from 'lru-cache';\nconst cache = new LRUCache<string, any>({ max: 1000, ttl: 60_000 });\n\nasync function getCachedUser(id: string) {\n  const cached = cache.get(id);\n  if (cached) return cached;\n  const user = await userRepo.findById(id);\n  cache.set(id, user);\n  return user;\n}\n```\n\n---\n\n## Phase 13: Background Jobs & Queues\n\n### BullMQ Pattern\n\n```typescript\n// jobs/email.job.ts\nimport { Queue, Worker } from 'bullmq';\nimport { env } from '../config/env.js';\nimport { logger } from '../utils/logger.js';\n\nconst connection = { url: env.REDIS_URL };\n\nexport const emailQueue = new Queue('email', { connection });\n\nexport const emailWorker = new Worker('email', async (job) => {\n  const { to, subject, body } = job.data;\n  logger.info({ jobId: job.id, to }, 'Sending email');\n  // Send via provider\n  await sendEmail({ to, subject, body });\n}, {\n  connection,\n  concurrency: 5,\n  limiter: { max: 10, duration: 1000 }, // 10 per second\n});\n\nemailWorker.on('failed', (job, err) => {\n  logger.error({ jobId: job?.id, err }, 'Email job failed');\n});\n```\n\n### Job Rules\n\n1. **Idempotent jobs** — same job ID running twice produces same result\n2. **Retry with backoff** — exponential backoff for transient failures\n3. **Dead letter queue** — failed jobs after max retries go to DLQ for inspection\n4. **Job timeout** — set `removeOnComplete` and `removeOnFail` TTLs\n5. **Monitor queue health** — queue length, processing time, failure rate\n\n---\n\n## Phase 14: Advanced Patterns\n\n### Dependency Injection (Manual)\n\n```typescript\n// app.ts — compose dependencies explicitly\nexport function createApp(deps?: { db?: Database; redis?: Redis }) {\n  const database = deps?.db ?? defaultDb;\n  const app = new Hono();\n\n  // Compose service graph\n  const userRepo = new UserRepository(database);\n  const userService = new UserService(userRepo);\n\n  // Mount routes with injected services\n  app.route('/users', createUserRoutes(userService));\n\n  return app;\n}\n```\n\n### Rate Limiting\n\n```typescript\n// middleware/rate-limit.ts (using hono-rate-limiter)\nimport { rateLimiter } from 'hono-rate-limiter';\n\nexport const apiRateLimit = rateLimiter({\n  windowMs: 60_000,\n  limit: 100,\n  keyGenerator: (c) => c.get('jwtPayload')?.sub || c.req.header('x-forwarded-for') || 'anonymous',\n  message: { error: { code: 'RATE_LIMITED', message: 'Too many requests' } },\n});\n```\n\n### WebSocket Pattern (Hono + @hono/node-ws)\n\n```typescript\nimport { createNodeWebSocket } from '@hono/node-ws';\n\nconst { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app });\n\napp.get('/ws', upgradeWebSocket((c) => ({\n  onOpen(evt, ws) { logger.info('WebSocket connected'); },\n  onMessage(evt, ws) {\n    const data = JSON.parse(evt.data.toString());\n    // Handle message\n    ws.send(JSON.stringify({ type: 'ack', id: data.id }));\n  },\n  onClose() { logger.info('WebSocket disconnected'); },\n})));\n```\n\n---\n\n## 2025+ Recommended Stack\n\n| Layer | Recommended | Alternative |\n|-------|-------------|-------------|\n| **Runtime** | Node.js 22 LTS | Bun |\n| **Framework** | Hono | Fastify |\n| **Language** | TypeScript (strict) | — |\n| **Validation** | Zod | Valibot |\n| **ORM** | Drizzle | Prisma |\n| **Database** | PostgreSQL | SQLite (dev) |\n| **Cache** | Redis / Upstash | LRU in-memory |\n| **Auth** | JWT + refresh | Lucia, Better Auth |\n| **Queue** | BullMQ | pg-boss |\n| **Testing** | Vitest | — |\n| **Logging** | Pino | — |\n| **Linting** | Biome | ESLint + Prettier |\n| **CI/CD** | GitHub Actions | — |\n| **Deploy** | Docker + Railway/Fly | Vercel (serverless) |\n\n---\n\n## 10 Commandments\n\n1. **Validate everything at the boundary** — Zod on every route\n2. **Fail fast at startup** — env vars, DB connection, migrations\n3. **Structured logging everywhere** — JSON, request IDs, no console.log\n4. **Custom errors with codes** — never throw strings\n5. **Cursor pagination** — never OFFSET\n6. **Graceful shutdown** — drain connections on SIGTERM\n7. **Test error paths** — 4xx/5xx matter more than happy paths\n8. **Type everything** — no `any`, branded IDs, Zod-derived types\n9. **Security by default** — rate limit, helmet, CORS, audit deps\n10. **Keep it simple** — 50 lines/function, 300 lines/file, flat is better than nested\n\n---\n\n## 10 Common Mistakes\n\n| # | Mistake | Fix |\n|---|---------|-----|\n| 1 | `console.log` in production | Use Pino with JSON output |\n| 2 | `any` type everywhere | `unknown` + type narrowing |\n| 3 | No input validation | Zod middleware on every route |\n| 4 | OFFSET pagination | Cursor-based with keyset |\n| 5 | No graceful shutdown | SIGTERM handler with drain timeout |\n| 6 | Hardcoded config | Zod-validated env at startup |\n| 7 | Fat controllers | Thin routes → services → repositories |\n| 8 | No error hierarchy | Custom AppError with codes |\n| 9 | Testing only happy paths | Test 400/401/404/409/500 explicitly |\n| 10 | No request tracing | Request ID middleware + propagation |\n\n---\n\n## Quality Scoring (0-100)\n\n| Dimension | Weight | What to Assess |\n|-----------|--------|----------------|\n| Type safety | 20% | Strict TS, no `any`, branded types |\n| Error handling | 15% | Custom errors, global handler, no leaks |\n| Testing | 15% | Coverage >80%, integration tests, error paths |\n| Security | 15% | Auth, validation, rate limiting, headers |\n| Observability | 10% | Structured logging, health checks, metrics |\n| Performance | 10% | Connection pooling, caching, N+1 prevention |\n| Code structure | 10% | Layer separation, file size limits, DI |\n| Deployment | 5% | Docker, CI/CD, graceful shutdown |\n\n---\n\n## Edge Cases\n\n### Startup/MVP\n- Start with Hono + SQLite (Drizzle) + Vitest — ship in hours\n- Add Postgres when you need concurrent writes\n- Skip Redis until you need caching or queues\n\n### Monorepo (Turborepo/Nx)\n- Shared packages: `@org/types`, `@org/db`, `@org/validation`\n- Each service is its own package with independent build/test\n- Use workspace protocol: `\"@org/types\": \"workspace:*\"`\n\n### Serverless (Vercel/Cloudflare Workers)\n- Hono runs everywhere — same code, different adapters\n- Use serverless DB drivers (Neon, PlanetScale, Turso)\n- No graceful shutdown needed — stateless by design\n\n### Legacy Express Migration\n- Add TypeScript incrementally (`.ts` files alongside `.js`)\n- Wrap Express routes with Zod validation middleware\n- Replace `console.log` with Pino one file at a time\n- Migrate to Hono/Fastify route-by-route using adapter\n\n---\n\n## Natural Language Commands\n\n- \"Set up a new TypeScript API project\" → Phase 1-3 (structure + config + env)\n- \"Add authentication to my API\" → Phase 7 (JWT + RBAC)\n- \"Review my error handling\" → Phase 4 (error hierarchy + handler)\n- \"Add database with Drizzle\" → Phase 6 (schema + repository + migrations)\n- \"Set up testing\" → Phase 9 (Vitest + unit + integration patterns)\n- \"Prepare for production deployment\" → Phase 11 (Docker + CI + checklist)\n- \"Optimize API performance\" → Phase 12 (priority stack + patterns)\n- \"Add background jobs\" → Phase 13 (BullMQ + patterns)\n- \"Audit my API security\" → Phase 7 security checklist\n- \"Set up logging and monitoring\" → Phase 8 (Pino + request ID + health)\n- \"Review my project structure\" → Phase 1 (structure + rules)\n- \"Full health check\" → Quick Health Check + Quality Scoring\n\n---\n\n⚡ Built by [AfrexAI](https://afrexai-cto.github.io/context-packs/) — AI-powered business automation.\n","readmeExcerpt":"Node.js & TypeScript Production Engineering Complete methodology for building production-grade Node.js backends with TypeScript. Covers architecture, frameworks, error handling, database patterns, security, testing, observability, and deployment. --- Quick Health Check (/16) Run through these 8 signals — score 0 (missing) or 2 (present): | # | Signal | Check | |---|--------|-------| | 1 | Strict TypeScript | \"strict\"","codeSnippets":[],"executableExamples":[{"language":"text","snippet":"src/\n├── index.ts              # Entry point — bootstrap only\n├── app.ts                # App factory (createApp)\n├── config/\n│   ├── env.ts            # Environment validation (Zod)\n│   └── database.ts       # DB connection config\n├── routes/\n│   ├── index.ts          # Route registry\n│   ├── users.ts          # /users routes\n│   └── orders.ts         # /orders routes\n├── services/\n│   ├── user.service.ts   # Business logic\n│   └── order.service.ts\n├── repositories/\n│   ├── user.repo.ts      # Data access (Drizzle/Prisma)\n│   └── order.repo.ts\n├── middleware/\n│   ├── auth.ts           # Authentication\n│   ├── validate.ts       # Request validation\n│   ├── error-handler.ts  # Global error handler\n│   └── request-id.ts     # Correlation ID\n├── errors/\n│   └── index.ts          # Custom error classes\n├── types/\n│   └── index.ts          # Shared types\n├── utils/\n│   └── index.ts          # Pure utility functions\n├── jobs/                 # Background jobs/queues\n│   └── email.job.ts\n└── __tests__/            # Tests mirror src/ structure\n    ├── services/\n    └── routes/\ndrizzle/                  # Database migrations\n├── 0001_create_users.sql\n└── meta/\ntsconfig.json\npackage.json\nDockerfile\ndocker-compose.yml\n.env.example"},{"language":"json","snippet":"{\n  \"compilerOptions\": {\n    \"target\": \"ES2022\",\n    \"module\": \"NodeNext\",\n    \"moduleResolution\": \"NodeNext\",\n    \"lib\": [\"ES2022\"],\n    \"outDir\": \"dist\",\n    \"rootDir\": \"src\",\n    \"strict\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"noImplicitOverride\": true,\n    \"noPropertyAccessFromIndexSignature\": true,\n    \"exactOptionalPropertyTypes\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"isolatedModules\": true,\n    \"declaration\": true,\n    \"declarationMap\": true,\n    \"sourceMap\": true,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": true\n  },\n  \"include\": [\"src\"],\n  \"exclude\": [\"node_modules\", \"dist\", \"**/*.test.ts\"]\n}"},{"language":"typescript","snippet":"// types/branded.ts\ndeclare const __brand: unique symbol;\ntype Brand<T, B extends string> = T & { [__brand]: B };\n\nexport type UserId = Brand<string, 'UserId'>;\nexport type OrderId = Brand<string, 'OrderId'>;\nexport type Email = Brand<string, 'Email'>;\n\nexport function UserId(id: string): UserId { return id as UserId; }\nexport function Email(email: string): Email {\n  if (!email.includes('@')) throw new ValidationError('Invalid email');\n  return email as Email;\n}"},{"language":"typescript","snippet":"// config/env.ts\nimport { z } from 'zod';\n\nconst EnvSchema = z.object({\n  NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),\n  PORT: z.coerce.number().int().min(1).max(65535).default(3000),\n  DATABASE_URL: z.string().url(),\n  REDIS_URL: z.string().url().optional(),\n  JWT_SECRET: z.string().min(32),\n  JWT_EXPIRES_IN: z.string().default('15m'),\n  LOG_LEVEL: z.enum(['fatal', 'error', 'warn', 'info', 'debug', 'trace']).default('info'),\n  CORS_ORIGINS: z.string().transform(s => s.split(',')).default('*'),\n  RATE_LIMIT_MAX: z.coerce.number().default(100),\n  RATE_LIMIT_WINDOW_MS: z.coerce.number().default(60_000),\n});\n\nexport type Env = z.infer<typeof EnvSchema>;\n\n// Validate at import time — fail fast\nconst parsed = EnvSchema.safeParse(process.env);\nif (!parsed.success) {\n  console.error('❌ Invalid environment variables:', parsed.error.flatten().fieldErrors);\n  process.exit(1);\n}\n\nexport const env = parsed.data;"},{"language":"typescript","snippet":"// errors/index.ts\nexport class AppError extends Error {\n  constructor(\n    message: string,\n    public readonly code: string,\n    public readonly statusCode: number,\n    public readonly details?: Record<string, unknown>,\n  ) {\n    super(message);\n    this.name = this.constructor.name;\n    Error.captureStackTrace(this, this.constructor);\n  }\n}\n\nexport class ValidationError extends AppError {\n  constructor(message: string, details?: Record<string, unknown>) {\n    super(message, 'VALIDATION_ERROR', 400, details);\n  }\n}\n\nexport class NotFoundError extends AppError {\n  constructor(resource: string, id: string) {\n    super(`${resource} not found: ${id}`, 'NOT_FOUND', 404, { resource, id });\n  }\n}\n\nexport class UnauthorizedError extends AppError {\n  constructor(message = 'Authentication required') {\n    super(message, 'UNAUTHORIZED', 401);\n  }\n}\n\nexport class ForbiddenError extends AppError {\n  constructor(message = 'Insufficient permissions') {\n    super(message, 'FORBIDDEN', 403);\n  }\n}\n\nexport class ConflictError extends AppError {\n  constructor(message: string, details?: Record<string, unknown>) {\n    super(message, 'CONFLICT', 409, details);\n  }\n}\n\nexport class RateLimitError extends AppError {\n  constructor(retryAfterMs: number) {\n    super('Too many requests', 'RATE_LIMITED', 429, { retryAfterMs });\n  }\n}"},{"language":"typescript","snippet":"// middleware/error-handler.ts\nimport type { ErrorHandler } from 'hono';\nimport { AppError } from '../errors/index.js';\nimport { logger } from '../utils/logger.js';\n\nexport const errorHandler: ErrorHandler = (err, c) => {\n  const requestId = c.get('requestId');\n\n  if (err instanceof AppError) {\n    if (err.statusCode >= 500) {\n      logger.error({ err, requestId }, 'Server error');\n    } else {\n      logger.warn({ err, requestId }, 'Client error');\n    }\n\n    return c.json({\n      error: { code: err.code, message: err.message, details: err.details },\n    }, err.statusCode as any);\n  }\n\n  // Unexpected errors — don't leak internals\n  logger.error({ err, requestId }, 'Unhandled error');\n  return c.json({\n    error: { code: 'INTERNAL_ERROR', message: 'An unexpected error occurred' },\n  }, 500);\n};"}],"parameters":{},"dependencies":[],"permissions":[],"extractedFiles":[],"languages":["typescript"],"docsSourceLabel":"CLAWHUB","editorialOverview":"Node.js & TypeScript Production Engineering Node.js & TypeScript Production Engineering Complete methodology for building production-grade Node.js backends with TypeScript. Covers architecture, frameworks, error handling, database patterns, security, testing, observability, and deployment. --- Quick Health Check (/16) Run through these 8 signals — score 0 (missing) or 2 (present): | # | Signal | Check | |---|--------|-------| | 1 | Strict TypeScript | \"strict\"","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":360,"uniquenessScore":67,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-04-15T00:45:39.800Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-04-15T00:45:39.800Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-09T18:14:22.932Z","emptyReason":null},"items":[{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-10T18:48:31.762Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}