Contract & Architecture Evolution Manager for NestJS
Automated API diffing, breaking-change detection, and living documentation —
built for teams that move fast without breaking things.
- Why ArchGuard?
- Features
- Installation
- NestJS Setup
- Quick Start
- Configuration
- CLI Commands
- Diff Report Example
- CI/CD Integration
- Programmatic Usage
- Roadmap
- Contributing
- License
| Problem | Without ArchGuard | With ArchGuard |
|---|---|---|
| API contract changes | Discovered by broken Frontend | Caught instantly, before merge |
| Documentation drift | Swagger is always outdated | Auto-generated from live spec |
| Change history | "Who changed this route?" 🤷 | Git-tracked diff reports forever |
| Breaking changes in PR | Found in code review (maybe) | Blocked at CI, flagged automatically |
- 🔍 Automatic API Snapshotting — Captures your OpenAPI/Swagger spec at any point in time
- 🔴 Breaking-Change Detection — Flags removed endpoints, deleted required fields, changed parameter types
- 🟡 Non-Breaking Change Tracking — Tracks added endpoints, optional parameters, response expansions
- 📄 Markdown Diff Reports — Beautiful, readable reports committed directly into your git history
- 📚 Living Architecture Index — Auto-maintained
INDEX.mdwith every diff ever generated - 🤝 NestJS-First — Designed for the NestJS +
@nestjs/swaggerecosystem; works with any OpenAPI 3.x spec - 🤖 GitHub Actions Integration — Posts diff reports as PR comments, blocks merges on breaking changes
- 🔗 Git Auto-Commit — Optionally auto-commits snapshots and reports so your architecture history is always versioned
- 📦 Library Mode — Use ArchGuard programmatically in your own scripts or tooling
npm install --save-dev archguard-cliThen add scripts to your package.json:
{
"scripts": {
"arch:init": "archguard init",
"arch:snapshot": "archguard snapshot",
"arch:diff": "archguard diff",
"arch:history": "archguard history"
}
}npm install -g archguard-clinpx archguard-cli init
npx archguard-cli snapshot
npx archguard-cli diffArchGuard reads your live OpenAPI spec. Make sure Swagger is enabled in your NestJS app:
// src/main.tsimport{NestFactory}from'@nestjs/core';import{DocumentBuilder,SwaggerModule}from'@nestjs/swagger';import{AppModule}from'./app.module';asyncfunctionbootstrap(){constapp=awaitNestFactory.create(AppModule);constconfig=newDocumentBuilder().setTitle('My API').setVersion('1.0').build();constdocument=SwaggerModule.createDocument(app,config);SwaggerModule.setup('api',app,document);// ↑ This exposes the JSON spec at: http://localhost:3000/api-jsonawaitapp.listen(3000);}bootstrap();ArchGuard fetches
http://localhost:3000/api-jsonby default.
You can override this inarchguard.config.jsonor via--url.
# 1. Initialize ArchGuard in your NestJS project
npx archguard-cli init
# 2. Start your NestJS app
npm run start:dev
# 3. Capture the current API as a baseline snapshot
npx archguard-cli snapshot
# 4. Make changes to your API (add a route, change a DTO, remove a param...)# 5. Generate a diff report
npx archguard-cli diffExpected output for archguard diff:
🛡 ArchGuard Diff
✔ Current snapshot captured
✔ Analysis complete — 3 change(s) detected
────────────────────────────────────────────────────────────
📊 Changes: 3 | 🔴 Breaking: 1
➕ Added: 1 endpoints | ➖ Removed: 1 | 🔄 Modified: 1
📐 Schemas: +0 ~1 -0
────────────────────────────────────────────────────────────
✔ Report saved: ./docs/architecture/history/diff-2024-01-15T10-30-00-to-2024-01-15T14-45-00.md
✔ Diff completed! Check the report for details.
The generated report appears at ./docs/architecture/history/diff-latest.md — ready to commit and share with your Frontend team.
ArchGuard is configured via archguard.config.json in your project root (created automatically by archguard init).
{
"projectName": "payments-api",
"historyPath": "./docs/architecture/history",
"snapshotPath": "./.archguard",
"swaggerUrl": "http://localhost:3000/api-json",
"notify": {
"breakingChangesOnly": false
},
"git": {
"autoCommit": true,
"commitMessage": "chore(docs): update architecture history [skip ci]"
}
}| Option | Type | Default | Description |
|---|---|---|---|
projectName | string | folder name | Display name for your project |
historyPath | string | "./docs/architecture/history" | Where diff reports are stored |
snapshotPath | string | "./.archguard" | Where snapshots are stored |
swaggerUrl | string | "http://localhost:3000/api-json" | URL to fetch the OpenAPI JSON spec |
notify.breakingChangesOnly | boolean | false | Only flag breaking changes |
git.autoCommit | boolean | false | Auto-commit snapshots and reports |
git.commitMessage | string | "chore(docs): ..." | Commit message template |
| Command | Description | Options |
|---|---|---|
archguard init | Initialize ArchGuard in the current directory | --force overwrite existing config |
archguard snapshot | Capture the current OpenAPI spec | --url <url> override Swagger URL |
archguard diff | Compare latest snapshot with current spec | --url <url>, --no-commit |
archguard history | List stored snapshots and diff reports | --snapshots, --reports |
Creates:
archguard.config.json— project configuration.archguard/— snapshot storage (add to.gitignoreif desired)docs/architecture/history/— diff report history (commit this!)- Appends ArchGuard entries to
.gitignore
archguard snapshot [--url http://localhost:3000/api-json]Fetches your OpenAPI spec and saves a timestamped JSON snapshot. Always writes snapshot-latest.json as the baseline for the next diff.
archguard diff [--url <url>] [--no-commit]- Loads
snapshot-latest.jsonas baseline - Fetches the current spec from your running app
- Runs the diff engine (endpoints + schemas + parameters + responses)
- Classifies each change as breaking, non-breaking, or informational
- Generates a Markdown report with Before/After details
- Updates
INDEX.mdand optionally auto-commits to git
archguard history [--snapshots] [--reports]Lists all snapshots and diff reports with timestamps.
Generated reports look like this:
# 🛡 ArchGuard Diff Report**Project:** payments-api **From:**`snapshot-2024-01-14T10-00-00.json`**To:**`snapshot-2024-01-15T14-30-00.json`## 📊 Summary| Metric | Count ||--------|-------|| 🔴 Breaking Changes |**2**|| Total Changes | 5 || ➕ Added Endpoints | 1 || ➖ Removed Endpoints | 1 || 🔄 Modified Endpoints | 0 || Modified Schemas | 2 |> ⚠️ **WARNING:** 2 breaking change(s) require immediate Frontend attention.## 🔌 Endpoint Changes### ➖ 🔴 `DELETE /v1/users/{id}`**Severity:** BREAKING **Change:**[Users] Endpoint removed: Delete user **Frontend Impact:** ⚠️ Remove all calls to this endpoint from your codebase.
---### ➕ 🟢 `POST /v2/users/{id}/deactivate`**Severity:** NON-BREAKING **Change:**[Users] Endpoint added: Deactivate user **Frontend Impact:** New endpoint available — implement integration if needed.Copy .github/workflows/archguard.yml to your NestJS repository:
name: 🛡 ArchGuard API Contract Checkon:
push:
branches: [main]pull_request:
branches: [main]jobs:
api-contract-check:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4with:
node-version: '20'cache: 'npm'
- name: Install dependenciesrun: npm ci
- name: Start NestJS apprun: | npm run start:prod & timeout 60 bash -c 'until curl -sf http://localhost:3000/api-json; do sleep 2; done' - name: Restore snapshot cacheuses: actions/cache@v4with:
path: .archguard/key: archguard-snapshot-${{ github.base_ref || github.ref_name }}
- name: Run ArchGuard diffif: github.event_name == 'pull_request'run: npx archguard-cli diff --no-commit
- name: Comment PR with diffif: github.event_name == 'pull_request'uses: actions/github-script@v7with:
script: | const fs = require('fs'); const file = 'docs/architecture/history/diff-latest.md'; if (!fs.existsSync(file)) return; const body = fs.readFileSync(file, 'utf-8').slice(0, 65000); await github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: `## 🛡 ArchGuard API Diff\n\n${body}` }); - name: Block merge on breaking changesif: github.event_name == 'pull_request'run: | if grep -q "Breaking Changes | \*\*[1-9]" docs/architecture/history/diff-latest.md 2>/dev/null; then echo "🔴 Breaking API changes detected! Fix before merging." exit 1 fi - name: Save snapshot on main pushif: github.ref == 'refs/heads/main' && github.event_name == 'push'run: npx archguard-cli snapshotUse ArchGuard as a library in your own scripts:
import{captureSnapshot,loadLatestSnapshot,saveSnapshot,generateDiff,generateMarkdownReport,saveReport,loadConfig,}from'archguard-cli';constconfig=loadConfig();// reads archguard.config.json// Capture current stateconstcurrent=awaitcaptureSnapshot(config);// Load previous stateconstprevious=loadLatestSnapshot(config);if(previous){// Generate diffconstreport=generateDiff(previous,current);console.log(`Breaking changes: ${report.summary.breakingChanges}`);// Generate and save Markdown reportconstmarkdown=generateMarkdownReport(report,config);saveReport(markdown,report,config);}// Save current as new baselinesaveSnapshot(current,config);| Phase | Feature | Status |
|---|---|---|
| Phase 1 | Core CLI — init, snapshot, diff, history | ✅ Done |
| Phase 2 | GitHub Actions CI/CD + PR comments | ✅ Done |
| Phase 3 | 47 unit tests + Jest coverage | ✅ Done |
| Phase 4 | Static extraction from NestJS decorators (no running server) | 🔨 In Progress |
| Phase 5 | Frontend SDK type generation from diff | 📋 Planned |
| Phase 6 | Web dashboard — visual architecture timeline | 📋 Planned |
Contributions are welcome!
# 1. Fork and clone
git clone https://github.com/YvesDeSa/ArchGuard.git
cd ArchGuard
# 2. Install dependencies
npm install
# 3. Run tests
npm test# 4. Develop with live TypeScript (no build step)
npm run dev -- snapshot --url http://localhost:3000/api-json
# 5. Build
npm run buildsrc/
├── bin/
│ └── archguard.ts # CLI entry point (Commander.js)
├── commands/
│ ├── init.ts # archguard init
│ ├── snapshot.ts # archguard snapshot
│ ├── diff.ts # archguard diff
│ └── history.ts # archguard history
├── core/
│ ├── snapshot.ts # Snapshot capture & persistence
│ ├── differ.ts # OpenAPI diff engine (breaking change detection)
│ ├── reporter.ts # Markdown report generator
│ └── git-integration.ts # simple-git auto-commit
├── utils/
│ ├── config.ts # Config loader & path resolution
│ └── logger.ts # Chalk-powered logger
├── types/
│ └── index.ts # All TypeScript interfaces
├── __tests__/
│ ├── differ.test.ts # 35 diff engine tests
│ ├── snapshot.test.ts # Snapshot I/O tests
│ └── reporter.test.ts # Report generation tests
└── index.ts # Public library API
MIT © Yves De Sá