{"id":"1662a2f2-8e84-4313-a026-6b342ee4b0cb","shortId":"CCvncj","kind":"skill","title":"nodejs-backend","tagline":">-","description":"# Node.js Backend\n\n**Verify before implementing**: For framework-specific APIs (Express 5, Fastify 5, Node.js 22+ built-ins), search current docs via `search_docs` before writing code. Training data may lag current releases.\n\n## Framework Selection\n\n| Context | Choose | Why |\n|---------|--------|-----|\n| Edge/Serverless | Hono | Zero-dep, fastest cold starts |\n| Performance API | Fastify | Higher throughput than Express, built-in schema validation |\n| Enterprise/team | NestJS | DI, decorators, structured conventions |\n| Legacy/ecosystem | Express | Most middleware, widest adoption |\n\nAsk user: deployment target, cold start needs, team experience, existing codebase.\n\n## Architecture\n\n```\nsrc/\n├── routes/          # HTTP: parse request, call service, format response\n├── middleware/       # Auth, validation, rate limiting, logging\n├── services/        # Business logic (no HTTP types)\n├── repositories/    # Data access only (queries, ORM)\n├── config/          # Env, DB pool, constants\n└── types/           # Shared TypeScript interfaces\n```\n\n- Routes never contain business logic\n- Services never import Request/Response\n- Repositories never throw HTTP errors\n- Dependencies point inward only (Clean Architecture rule): routes -> services -> repositories. Never the reverse.\n- For scripts/prototypes: single file is fine -- ask \"will this grow?\"\n\n## TypeScript Rules\n\n- Use `import type { }` for type-only imports -- eliminates runtime overhead\n- Prefer `interface` for object shapes (2-5x faster type resolution than intersections)\n- Prefer `unknown` over `any` -- forces explicit narrowing\n- Use `z.infer<typeof Schema>` as single source of truth -- never duplicate types and schemas\n- Minimize `as` assertions -- use type guards instead\n- Add explicit return types to exported functions (faster declaration emit)\n- Untyped package? `declare module 'pkg' { const v: unknown; export default v; }` in `types/ambient.d.ts`\n\n## Validation\n\n**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.\n\n## Error Handling\n\nCustom error hierarchy: `AppError(message, statusCode, isOperational)` → `ValidationError(400)`, `NotFoundError(404)`, `UnauthorizedError(401)`, `ForbiddenError(403)`, `ConflictError(409)`\n\nCentralized handler middleware:\n- `AppError` → return `{ error: message }` with statusCode\n- Unknown → log full stack, return 500 + generic message in production\n- Async wrapper: `const asyncHandler = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);`\n\nCodes: 400 bad input | 401 no auth | 403 no permission | 404 missing | 409 conflict | 422 business rule | 429 rate limited | 500 server fault\n\n## API Design\n\n**Contract-first**: define route schemas (Zod schemas, Fastify JSON Schema, or OpenAPI spec) before writing handler logic. The schema is the contract -- implementation follows. Generate OpenAPI/Swagger docs from these schemas for interactive API documentation.\n\n- **Hyrum's Law awareness**: every observable response field, ordering, or timing becomes a dependency for callers. Use Zod schemas or Fastify response schemas to control exactly what's serialized -- never return raw ORM objects or untyped objects from handlers.\n- **Addition over modification**: add new optional fields rather than changing or removing existing ones. Removing a field from a response schema breaks callers silently. Deprecate first (mark in OpenAPI spec), remove in a later version.\n- **Consistent error envelope**: all errors -- validation, auth, not-found, application -- must produce the same `{ error: { code, message, details? } }` structure. Centralize in the error handler middleware. Callers build error handling once; inconsistent errors force per-endpoint special cases.\n- **Boundary validation**: validate at the middleware/route handler level (Zod `.parse()` on request body/params, Fastify schema validation). Services and repositories trust that input was validated at entry -- no redundant checks scattered through business logic.\n- **Third-party responses are untrusted data**: validate shape and content of external API responses before using them in logic, rendering, or decision-making. A compromised or misbehaving service can return unexpected types, malicious content, or missing fields. Parse through a Zod schema before use.\n- **Resources**: plural nouns (`/users`), max 2 nesting levels (`/users/:id/orders`)\n- **Methods**: GET read | POST create | PUT replace | PATCH partial | DELETE remove\n- **Versioning**: URL path `/api/v1/`\n- **Response**: `{ data, pagination?: { page, limit, total, totalPages } }`\n- **Queries**: `?page=1&limit=20&status=active&sort=createdAt,desc`\n- Return `Location` header on 201. Use 204 for successful DELETE with no body.\n\n## Async Patterns\n\n| Pattern | Use When |\n|---------|----------|\n| `async/await` | Sequential operations |\n| `Promise.all` | Parallel independent ops |\n| `Promise.allSettled` | Parallel, some may fail |\n| `Promise.race` | Timeout or first-wins |\n\nNever use readFileSync or other sync methods in production -- use `fs.promises` or stream equivalents. Offload CPU work to worker threads (Piscina). Stream large payloads.\n\n## Production Resilience\n\n- **Fail-fast env validation**: parse and validate all environment variables at startup with a Zod schema (`const env = envSchema.parse(process.env)`). If invalid, crash before serving traffic. Never discover a missing env var on the first request that needs it.\n- **Health endpoints**: expose both `/health` (shallow, always 200 if process is alive) and `/ready` (deep, verifies database, cache, and critical dependencies are reachable). Load balancers probe `/ready` for traffic routing; monitoring probes `/health` for process liveness. Don't conflate them.\n- **Caching**: Redis cache-aside for DB/API responses; in-memory LRU with TTL for hot paths. Always invalidate on writes.\n- **Load shedding**: `@fastify/under-pressure` (or equivalent) -- monitor event loop delay, heap, RSS; return 503 when thresholds exceeded.\n- **Response schemas**: In Fastify, always define response schemas -- enables `fast-json-stringify` for 2-3x faster serialization.\n- **Circuit breaker**: use `opossum` for outbound service calls. States: CLOSED (normal) -> OPEN (failing, return fallback) -> HALF_OPEN (probe). Prevents cascade failures when downstream services are down.\n\n## Discipline\n\n- Simplicity first -- every change as simple as possible, impact minimal code\n- Only touch what's necessary -- avoid introducing unrelated changes\n- No hacky workarounds -- if a fix feels wrong, step back and implement the clean solution\n- Before adding a new abstraction, verify it appears in 3+ places. If not, inline it.\n- If a fix requires bypassing TypeScript (`as any`, non-null assertions on untrusted data, `// @ts-ignore`), treat it as a design smell and find the typed solution\n- Verify: `tsc --noEmit && npm test` pass with zero warnings before declaring done\n\n## Verify\n\n- `tsc --noEmit` passes with zero errors\n- `npm test` passes with zero failures\n- No TypeScript bypasses (`as any`, `@ts-ignore`) in new code\n\n## References\n\n- [TypeScript config](./references/typescript-config.md) -- tsconfig, ESM, branded types, compiler performance\n- [Security](./references/security.md) -- JWT, password hashing, rate limiting, OWASP\n- [API design patterns](./references/api-design.md) -- pagination, filtering, sorting, deprecation\n- [Database & production](./references/database-production.md) -- connection pooling, transactions, Docker, logging","tags":["nodejs","backend","skills","iliaal","agent-skills","ai-coding-assistant","ai-tools","claude-code"],"capabilities":["skill","source-iliaal","skill-nodejs-backend","topic-agent-skills","topic-ai-coding-assistant","topic-ai-tools","topic-claude-code","topic-skills"],"categories":["ai-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/iliaal/ai-skills/nodejs-backend","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add iliaal/ai-skills","source_repo":"https://github.com/iliaal/ai-skills","install_from":"skills.sh"}},"qualityScore":"0.456","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 13 github stars · SKILL.md body (7,657 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:07:02.711Z","embedding":null,"createdAt":"2026-05-09T01:05:35.727Z","updatedAt":"2026-05-18T19:07:02.711Z","lastSeenAt":"2026-05-18T19:07:02.711Z","tsv":"'-3':806 '-5':179 '/api/v1':594 '/health':718,746 '/ready':727,740 '/references/api-design.md':973 '/references/database-production.md':980 '/references/security.md':963 '/references/typescript-config.md':955 '/users':573,578 '1':604 '2':178,575,805 '20':606 '200':721 '201':616 '204':618 '22':19 '3':881 '400':275,319 '401':279,322 '403':281,325 '404':277,328 '409':283,330 '422':332 '429':335 '5':15,17 '500':298,338 '503':787 'abstract':876 'access':110 'activ':608 'ad':873 'add':212,420 'addit':417 'adopt':74 'aliv':725 'alway':720,771,795 'api':13,52,341,376,537,970 'appear':879 'apperror':270,287 'applic':462 'architectur':86,142 'asid':758 'ask':75,156 'assert':207,898 'async':303,625 'async/await':630 'asynchandl':306 'auth':97,324,458 'avoid':853 'awar':381 'back':866 'backend':3,5 'bad':320 'balanc':738 'becom':389 'bodi':624 'body/params':503 'boundari':245,491 'brand':958 'break':438 'breaker':811 'build':479 'built':21,59 'built-in':20,58 'busi':103,126,333,522 'bypass':891,943 'cach':731,754,757 'cache-asid':756 'call':92,817 'caller':393,439,478 'cascad':829 'case':490 'catch':316 'central':284,472 'chang':426,840,856 'check':519 'choos':41 'circuit':810 'clean':141,870 'close':819 'code':31,318,468,847,951 'codebas':85 'cold':49,79 'compil':960 'compromis':550 'config':114,954 'conflat':752 'conflict':331 'conflicterror':282 'connect':981 'consist':452 'const':227,305,691 'constant':118 'contain':125 'content':534,559 'context':40 'contract':344,365 'contract-first':343 'control':402 'convent':68 'cpu':663 'crash':697 'creat':584 'createdat':610 'critic':733 'current':24,36 'custom':267 'data':33,109,530,596,901 'databas':730,978 'db':116,250 'db/api':760 'decis':547 'decision-mak':546 'declar':220,224,926 'decor':66 'deep':728 'default':231 'defin':346,796 'delay':783 'delet':589,621 'dep':47 'depend':137,391,734 'deploy':77 'deprec':441,977 'desc':611 'design':342,909,971 'detail':470 'di':65 'disciplin':836 'discov':702 'doc':25,28,370 'docker':984 'document':377 'done':927 'downstream':832 'dri':263 'duplic':201 'edge/serverless':43 'elimin':170 'emit':221 'enabl':799 'endpoint':488,715 'enterprise/team':63 'entri':248,516 'env':115,252,677,692,705 'envelop':454 'environ':683 'envschema.parse':693 'equival':661,779 'error':136,265,268,289,453,456,467,475,480,484,934 'esm':957 'event':781 'everi':382,839 'exact':403 'exceed':790 'exist':84,429 'experi':83 'explicit':191,213 'export':217,230 'expos':716 'express':14,57,70 'extend':257 'extern':536 'fail':641,675,822 'fail-fast':674 'failur':830,940 'fallback':824 'fast':676,801 'fast-json-stringifi':800 'faster':181,219,808 'fastest':48 'fastifi':16,53,241,351,398,504,794 'fastify/under-pressure':777 'fault':340 'feel':863 'field':385,423,433,562 'file':153 'filter':975 'find':912 'fine':155 'first':345,442,646,709,838 'first-win':645 'fix':862,889 'fn':307,312 'follow':367 'forbiddenerror':280 'forc':190,485 'format':94 'found':461 'framework':11,38 'framework-specif':10 'fs.promises':658 'full':295 'function':218 'generat':368 'generic':299 'get':581 'grow':159 'guard':210 'hacki':858 'half':825 'handl':266,481 'handler':285,359,416,476,497 'hash':966 'header':614 'health':714 'heap':784 'hierarchi':269 'higher':54 'hono':44 'hot':769 'http':89,106,135 'hyrum':378 'id/orders':579 'ignor':904,948 'impact':845 'implement':8,366,868 'import':130,163,169 'in':22 'in-memori':762 'inconsist':483 'independ':635 'infer':238 'inlin':885 'input':321,512 'instead':211 'interact':375 'interfac':122,174 'intersect':185 'introduc':854 'invalid':696,772 'inward':139 'isoper':273 'json':352,802 'jwt':964 'lag':35 'larg':670 'later':450 'law':380 'legacy/ecosystem':69 'level':498,577 'limit':100,337,599,605,968 'live':749 'load':737,775 'locat':613 'log':101,294,985 'logic':104,127,360,523,543 'loop':782 'lru':765 'make':548 'malici':558 'mark':443 'max':574 'may':34,640 'memori':764 'merg':261 'messag':271,290,300,469 'method':580,654 'middlewar':72,96,286,477 'middleware/route':496 'minim':205,846 'misbehav':552 'miss':329,561,704 'modif':419 'modul':225 'monitor':744,780 'must':463 'narrow':192 'nativ':242 'necessari':852 'need':81,712 'nest':576 'nestj':64 'never':124,129,133,147,200,407,648,701 'new':421,875,950 'next':310,315,317 'node.js':4,18 'nodej':2 'nodejs-backend':1 'noemit':918,930 'non':896 'non-nul':895 'normal':820 'not-found':459 'notfounderror':276 'noun':572 'npm':919,935 'null':897 'object':176,411,414 'observ':383 'offload':662 'omit':259 'one':430 'op':251,636 'open':821,826 'openapi':355,445 'openapi/swagger':369 'oper':632 'opossum':813 'option':422 'order':386 'orm':113,410 'outbound':815 'overhead':172 'owasp':969 'packag':223 'page':598,603 'pagin':597,974 'parallel':634,638 'pars':90,500,563,679 'parti':526 'partial':260,588 'pass':921,931,937 'password':965 'patch':587 'path':593,770 'pattern':626,627,972 'payload':671 'per':487 'per-endpoint':486 'perform':51,961 'permiss':327 'pick':258 'piscina':668 'pkg':226 'place':882 'plural':571 'point':138 'pool':117,982 'possibl':844 'post':583 'prefer':173,186 'prevent':828 'probe':739,745,827 'process':723,748 'process.env':694 'produc':464 'product':302,656,672,979 'promise.all':633 'promise.allsettled':637 'promise.race':642 'promise.resolve':311 'put':585 'queri':112,602 'rate':99,336,967 'rather':424 'raw':409 'reachabl':736 'read':582 'readfilesync':650 'redi':755 'redund':518 'refer':952 'releas':37 'remov':428,431,447,590 'render':544 'replac':586 'repositori':108,132,146,509 'req':308,313 'request':91,247,502,710 'request/response':131 'requir':890 'res':309,314 'resili':673 'resolut':183 'resourc':570 'respons':95,384,399,436,527,538,595,761,791,797 'return':214,288,297,408,555,612,786,823 'revers':149 'rout':88,123,144,347,743 'rss':785 'rule':143,161,334 'runtim':171 'scatter':520 'schema':61,204,264,348,350,353,362,373,396,400,437,505,567,690,792,798 'scripts/prototypes':151 'search':23,27 'secur':962 'select':39 'sequenti':631 'serial':406,809 'serv':699 'server':339 'servic':93,102,128,145,507,553,816,833 'shallow':719 'shape':177,532 'share':120 'shed':776 'silent':440 'simpl':842 'simplic':837 'singl':152,196 'skill' 'skill-nodejs-backend' 'smell':910 'solut':871,915 'sort':609,976 'sourc':197 'source-iliaal' 'spec':356,446 'special':489 'specif':12 'src':87 'stack':296 'start':50,80 'startup':255,686 'state':818 'status':607 'statuscod':272,292 'step':865 'stream':660,669 'stringifi':803 'structur':67,471 'success':620 'sync':653 'target':78 'team':82 'test':920,936 'third':525 'third-parti':524 'thread':667 'threshold':789 'throughput':55 'throw':134 'time':388 'timeout':643 'topic-agent-skills' 'topic-ai-coding-assistant' 'topic-ai-tools' 'topic-claude-code' 'topic-skills' 'total':600 'totalpag':601 'touch':849 'traffic':700,742 'train':32 'transact':983 'treat':905 'trust':510 'truth':199 'ts':903,947 'ts-ignor':902,946 'tsc':917,929 'tsconfig':956 'ttl':767 'type':107,119,164,167,182,202,209,215,557,914,959 'type-on':166 'typebox':240 'types/ambient.d.ts':234 'typescript':121,160,237,892,942,953 'unauthorizederror':278 'unexpect':556 'unknown':187,229,293 'unrel':855 'untrust':529,900 'untyp':222,413 'url':592 'use':162,193,208,256,394,540,569,617,628,649,657,812 'user':76 'v':228,232 'valid':62,98,235,243,457,492,493,506,514,531,678,681 'validationerror':274 'var':253,706 'variabl':684 'verifi':6,729,877,916,928 'version':451,591 'via':26 'warn':924 'widest':73 'win':647 'work':664 'workaround':859 'worker':666 'wrapper':304 'write':30,358,774 'wrong':864 'x':180,807 'z.infer':194 'zero':46,923,933,939 'zero-dep':45 'zod':236,349,395,499,566,689","prices":[{"id":"6628cffd-b366-4416-a8df-aa551ca7228b","listingId":"1662a2f2-8e84-4313-a026-6b342ee4b0cb","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"iliaal","category":"ai-skills","install_from":"skills.sh"},"createdAt":"2026-05-09T01:05:35.727Z"}],"sources":[{"listingId":"1662a2f2-8e84-4313-a026-6b342ee4b0cb","source":"github","sourceId":"iliaal/ai-skills/nodejs-backend","sourceUrl":"https://github.com/iliaal/ai-skills/tree/master/skills/nodejs-backend","isPrimary":false,"firstSeenAt":"2026-05-09T01:05:35.727Z","lastSeenAt":"2026-05-18T19:07:02.711Z"}],"details":{"listingId":"1662a2f2-8e84-4313-a026-6b342ee4b0cb","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"iliaal","slug":"nodejs-backend","github":{"repo":"iliaal/ai-skills","stars":13,"topics":["agent-skills","ai-coding-assistant","ai-tools","claude-code","skills"],"license":"mit","html_url":"https://github.com/iliaal/ai-skills","pushed_at":"2026-05-16T13:15:17Z","description":"Curated collection of agent skills for AI coding assistants.","skill_md_sha":"f480649458b4ea5c5fa0fdf4cad92627cb531b53","skill_md_path":"skills/nodejs-backend/SKILL.md","default_branch":"master","skill_tree_url":"https://github.com/iliaal/ai-skills/tree/master/skills/nodejs-backend"},"layout":"multi","source":"github","category":"ai-skills","frontmatter":{"name":"nodejs-backend","description":">-"},"skills_sh_url":"https://skills.sh/iliaal/ai-skills/nodejs-backend"},"updatedAt":"2026-05-18T19:07:02.711Z"}}