CI/CD Basics
10 examples to structure CI (pull request validation) and CD (release deploy) for Node.js 24 TypeScript services - 7 basic and 3 intermediate.
Prerequisites
mkdir node-service && cd node-service
npm init -y
npm pkg set type=module
npm install express@5
npm install -D typescript@5.6 vitest eslint @types/nodeFor GitHub Actions specifics, see GitHub Actions for Node and Quality Gates.
Basic Examples
1. PR Pipeline Stages
PR opened / push
-> install (npm ci)
-> lint
-> typecheck
-> unit test
-> (optional) integration test
-> block merge if any step fails
- PR pipeline proves the commit is safe to merge
- No deploy in PR pipeline for most teams (preview envs are optional)
- Fast feedback: target under 10 minutes for unit stages
2. Release Pipeline Stages
Tag / merge to main
-> install + test (repeat gates)
-> build (tsc / docker build)
-> scan image / audit
-> push artifact (registry / S3 zip)
-> deploy staging
-> smoke test staging
-> deploy production (manual approval or progressive)
- Release pipeline produces the deployable artifact
- Same git SHA from test to prod
- See CI/CD Best Practices
3. package.json CI Scripts
{
"scripts": {
"lint": "eslint src",
"typecheck": "tsc --noEmit",
"test": "vitest run",
"build": "tsc -p tsconfig.json",
"audit:ci": "npm audit --audit-level=high"
}
}- CI calls npm scripts, not raw tool commands scattered in YAML
tsc --noEmitcatches type errors without emitting files- Pin tool versions in
package.jsondevDependencies
4. Minimal GitHub Actions PR Workflow
# .github/workflows/ci.yml
name: CI
on:
pull_request:
push:
branches: [main]
jobs:
test:
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 lint
- run: npm run typecheck
- run: npm run testcache: npmspeeds installs when lockfile unchanged- Run on
pull_requestandpushtomain - See GitHub Actions for Node
5. Fail Fast Ordering
steps:
- run: npm ci
- run: npm run lint # seconds
- run: npm run typecheck # seconds
- run: npm run test # minutes
- run: npm run build # after tests pass- Put cheapest checks first
- Do not build Docker before unit tests pass
- Parallel jobs for lint vs test if repo is large
6. Environment Separation
| Environment | Trigger | Purpose |
|---|---|---|
ci | Every PR | Validation only |
staging | Merge to main | Pre-prod integration |
production | Tag or approved workflow | Customer traffic |
- Staging uses production-like config with scrubbed data
- Production deploy requires approval or canary - Canary and Progressive Delivery
7. Artifact Immutability
- name: Build and push image
run: |
IMAGE=ghcr.io/acme/api:${{ github.sha }}
docker build -t "$IMAGE" .
docker push "$IMAGE"- Tag images with
github.sha, not:latestalone - Deploy staging and prod with the same digest
- Rollback = redeploy previous SHA
Intermediate Examples
8. Reusable Workflow
# .github/workflows/reusable-node-ci.yml
on:
workflow_call:
inputs:
node-version:
required: false
type: string
default: "24"
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: npm
- run: npm ci
- run: npm run lint && npm run typecheck && npm run test# .github/workflows/ci.yml
jobs:
call-ci:
uses: ./.github/workflows/reusable-node-ci.yml- DRY across microservices monorepo
- Each service workflow passes its working directory if needed
9. Release Workflow with Environment Protection
# .github/workflows/release.yml
name: Release
on:
push:
tags: ["v*.*.*"]
jobs:
deploy-staging:
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v4
- run: npm ci && npm test && npm run build
- run: ./scripts/deploy.sh staging ${{ github.sha }}
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment: production
steps:
- run: ./scripts/deploy.sh production ${{ github.sha }}- GitHub
environmentadds required reviewers for production - Staging must pass before production job starts
10. Conventional Commits Gate
- name: Validate commit messages on PR
uses: wagoid/commitlint-github-action@v6- Enables automated semver via Semantic Release
- Enforce in PR pipeline, not at release time only
FAQs
Should PR pipelines deploy anywhere?
Optional preview environments per PR - Preview Environments. Default: validate only, deploy on merge.
CI on main after merge?
Yes. Re-run gates on main to catch merge skew. Some teams trust PR-only; re-running on main is safer.
Where does Docker build belong?
Release pipeline after tests, or PR pipeline only for Dockerfile changes with scan gate. Never skip tests before image push.
Monorepo strategy?
Path filters: paths: ['services/api/**'] trigger only affected workflows. Use Turborepo or Nx for affected detection.
How do Lambda deploys fit?
CI builds zip artifact; CD runs aws lambda update-function-code with the same SHA-named S3 object.
What about secrets in CI?
GitHub Actions secrets and OIDC to AWS. No long-lived AWS keys in repo. See Secrets Managers.
Related
- GitHub Actions for Node - matrix and cache
- Quality Gates - lint, audit, coverage
- Semantic Release - automated versioning
- Preview Environments - PR deploys
- CI/CD Best Practices - section checklist
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.