Stack conventions
Node.js (server-side JavaScript)
Node.js services, command-line tools and libraries in JavaScript, tested with the repository's runner (node:test, Vitest, Jest).
Node.js services, command-line tools and libraries in JavaScript, tested with the repository's runner (node:test, Vitest, Jest).
Used for: node.
Principles
- Follow the module system the package declares (
"type": "module"means ESM withimport/export); do not mixrequireinto ESM files. - Never block the event loop: no synchronous fs/crypto/zlib calls on request paths, and move CPU-heavy work to worker threads or a queue.
- Use async/await end to end; every promise is awaited, returned or explicitly handled — no floating promises.
- Validate and parse all external input (HTTP bodies, query strings, headers, env, messages) at the edge with a schema, and pass typed, trusted values inward.
- Read configuration from the environment once at startup, validate it, and fail fast with a clear message when it is missing.
- Shut down gracefully: on SIGTERM/SIGINT stop accepting work, drain in-flight requests, close pools, then exit with a meaningful code.
- Treat errors as values with context: throw
Errorinstances (withcause), never strings, and handle them at one boundary per entry point. - Prefer built-in platform APIs (
fetch,node:test,AbortController,node:crypto) over extra dependencies when they do the job. - Keep dependencies few, maintained and pinned through the lockfile; review what a new package pulls in before adding it.
Conventions
Accessibility
Concurrency
- Remember a single process shares state across requests: no request data in module-level variables.
- Use
AsyncLocalStoragefor request-scoped context (request id, user) rather than threading globals. - Use worker_threads for CPU-bound work; use a queue for long-running jobs instead of holding the request open.
Dependencies
- Commit the lockfile and install with
npm ci(or the repo's package manager equivalent) in CI. - Prefer well-maintained packages with few transitive dependencies; remove unused ones.
- Do not add a dependency for something the standard library already provides (fetch, test runner, UUID via
crypto.randomUUID).
Documentation
- JSDoc/TSDoc on exported functions explains intent, errors thrown and units; keep README run/test instructions current.
- Document every environment variable the service reads, with its default.
Errors
- Throw
Errorsubclasses with a stablecodeandcause; never throw strings or plain objects. - Register
process.on('unhandledRejection')anduncaughtExceptiononly to log and exit — do not try to keep running in an unknown state. - Do not swallow errors in empty
catchblocks; rethrow with context or handle deliberately.
Logging
- Use the repo's structured logger (pino, winston, the framework logger); log JSON with a request/correlation id, not
console.login services. - Never log secrets, tokens, passwords or full request bodies containing personal data; use redaction options.
Naming
- camelCase for variables and functions, PascalCase for classes, UPPER_SNAKE_CASE for true constants and env var names.
- kebab-case or the repo's existing style for file names; one style per repo.
- Import built-ins with the
node:prefix (node:fs/promises,node:path).
Performance
- Reuse clients and connection pools (database, HTTP keep-alive agents); create them once at startup.
- Run independent awaits concurrently with
Promise.all/Promise.allSettled, with a concurrency limit for large fan-outs. - Paginate and bound every query and in-memory collection that grows with input.
Security
- Use parameterised queries or the ORM's bindings; never build SQL, shell commands or file paths by string concatenation with user input.
- Avoid
child_process.execwith interpolated input; useexecFile/spawnwith an argument array. - Set security headers (helmet or framework equivalent), limit request body size, and rate-limit authentication endpoints.
- Guard against prototype pollution: do not merge untrusted objects into plain objects; prefer
Object.create(null)orMapfor dictionaries from input. - Load secrets from the environment or a secret manager; never commit them or read them from the repo.
Structure
- Keep
src/for application code and tests beside it or undertest/, matching the repo. - Separate app construction from listening (
createApp()vsserver.listen) so tests can start the app without a port. - Declare supported runtime in
package.jsonenginesand keep.nvmrc/.node-versionaligned; target an active LTS line.
Testing
- Use the repo's runner (node:test, Vitest, Jest, Mocha); test services with injected fakes and HTTP routes through the app instance (e.g. supertest or
app.inject). - Tests are deterministic: no real network, fake timers or injected clocks, and a fresh state per test.
- Cover the error paths: validation failures, downstream timeouts and not-found cases.
- Unit-test modules with injected fakes; integration-test HTTP routes through the app instance and data access against a real database in a container where the change touches them.
- Name test files after the module (
orders.test.js) and test titles after the behaviour ('rejects an order without lines'). - Done means tested: new or changed behaviour ships with tests in the same change, and a fixed bug gets a regression test that fails without the fix.
Review checklist
- Is every promise awaited or explicitly handled?
- Is all external input validated with a schema before use?
- Are there no synchronous fs/crypto/zlib calls on request paths?
- Do outbound HTTP/database calls have timeouts?
- Are SQL queries and shell invocations parameterised (no string-built commands)?
- Are errors thrown as Error instances with useful context and handled at a single boundary?
- Are secrets and personal data kept out of logs?
- Is configuration read from the environment and validated at startup?
- Does the change keep the module system consistent (ESM vs CommonJS) with the package?
- Are new dependencies necessary, maintained and reflected in the lockfile?
- Do tests cover the error and validation paths without real network calls?
- Is graceful shutdown preserved for any new server, pool or worker?
Quality checks (run when your repository has the tool configured)
| Check | Kind | Tool | Blocks the pull request |
|---|---|---|---|
eslint |
lint | eslint | no |
prettier |
format | prettier | no |
biome |
lint | biome | no |
npm-audit |
security | npm audit | no |
tsc |
typecheck | tsc | no |
gitleaks |
security | gitleaks | no |