Quality Gates
Block merges and deploys until lint, tests, typecheck, and security audit pass on Node.js 24 TypeScript code.
Search across all documentation pages
Block merges and deploys until lint, tests, typecheck, and security audit pass on Node.js 24 TypeScript code.
Quick-reference recipe card - copy-paste ready.
steps:
- run: npm ci
- run: npm run lint
- run: npm run typecheck
- run: npm run test
- run: npm audit --audit-level=high
- run: npm run buildWhen to reach for this: Before any deploy to staging or production. Quality gates are non-negotiable for production Node APIs.
{
"scripts": {
"lint": "eslint src --max-warnings 0",
"typecheck": "tsc -p tsconfig.json --noEmit",
"test": "vitest run --coverage",
"test:unit": "vitest run --project unit",
"test:integration": "vitest run --project integration",
"build": "tsc -p tsconfig.json",
"audit:ci": "npm audit --audit-level=high",
"gates": "npm run lint && npm run typecheck && npm run test && npm run audit:ci && npm run build"
}
}# .github/workflows/ci.yml (excerpt)
jobs:
gates:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "24"
cache: npm
- run: npm ci
- run: npm run gates
docker-scan:
needs: gates
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: docker build -t api:ci .
- uses: aquasecurity/trivy-action@master
with:
image-ref: api:ci
severity: CRITICAL,HIGH
exit-code: 1What this demonstrates:
gates script local developers run before push--max-warnings 0 treats lint warnings as failures| Gate | Typical duration | Fail fast? |
|---|---|---|
| lint | 5-30s | Yes |
| typecheck | 10-60s | Yes |
| unit test | 1-5 min | Yes |
| integration test | 2-15 min | After unit |
| npm audit | 5-20s | Yes (policy-dependent) |
| docker build + scan | 2-10 min | Release only |
// eslint.config.js excerpt
export default [
{
rules: {
"no-console": "error",
"@typescript-eslint/no-floating-promises": "error",
},
},
];Ban console.log in src/; use structured logging.
{
"scripts": {
"typecheck": "tsc -p tsconfig.json --noEmit"
}
}Run in CI even if build emits types. --noEmit is faster for PR feedback.
// vitest.config.ts
export default {
test: {
coverage: {
thresholds: {
lines: 80,
functions: 80,
branches: 70,
},
},
},
};Start with diff coverage on changed files if full-repo thresholds are too harsh initially.
Configure in GitHub:
gates (and docker-scan for release branches)npm audit noise on transitive deps - blocks every PR. Fix: audit-level=high, Renovate for bumps, or npm audit fix playbook.build runs tsc - build may skip strict files. Fix: explicit typecheck job.src/ on every run (fast enough).Dockerfile changes.npm ci fails or audit is meaningless. Fix: enforce package-lock.json in repo.| Alternative | Use When | Don't Use When |
|---|---|---|
| Strict gates on every PR | Production APIs | Early prototype repos (temporary) |
| Nightly full integration | Slow E2E suites | You skip all integration on PR |
| SonarQube / CodeClimate | Org-wide quality metrics | Small team with ESLint + Vitest enough |
| Snyk / Dependabot | CVE triage automation | Replacing npm audit entirely without policy |
Use path filters to skip CI when only *.md changes, but keep filters narrow. Most Node PRs touch code.
Fail on moderate or above and maintain SBOM export (npm sbom) in release pipeline.
No. They catch mechanical issues; humans catch design and security logic flaws.
Same gates script before esbuild zip. Scan zip with trivy fs if not using Docker.
Affected-package detection (Nx/Turbo) per service, but never skip gates on a service whose code changed.
No. Smoke test is a gate between staging and production - CI/CD Basics.
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.
Reviewed by Chris St. John·Last updated Jul 18, 2026