{"id":36573418,"url":"https://github.com/foo-ogawa/micro-contracts","last_synced_at":"2026-03-15T19:41:47.976Z","repository":{"id":332038463,"uuid":"1128659199","full_name":"foo-ogawa/micro-contracts","owner":"foo-ogawa","description":"Contract-first vertical slices for TypeScript Web/API systems. OpenAPI as Single Source of Truth with enforceable guardrails.","archived":false,"fork":false,"pushed_at":"2026-01-12T12:53:46.000Z","size":436,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-12T15:23:56.846Z","etag":null,"topics":["api-design","code-generation","contract-first","developer-tools","microservices","monolith","openapi","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/foo-ogawa.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-06T01:08:34.000Z","updated_at":"2026-01-12T12:53:48.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/foo-ogawa/micro-contracts","commit_stats":null,"previous_names":["foo-ogawa/micro-contracts"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/foo-ogawa/micro-contracts","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foo-ogawa%2Fmicro-contracts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foo-ogawa%2Fmicro-contracts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foo-ogawa%2Fmicro-contracts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foo-ogawa%2Fmicro-contracts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/foo-ogawa","download_url":"https://codeload.github.com/foo-ogawa/micro-contracts/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/foo-ogawa%2Fmicro-contracts/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28442247,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-15T00:55:22.719Z","status":"online","status_checked_at":"2026-01-15T02:00:08.019Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-design","code-generation","contract-first","developer-tools","microservices","monolith","openapi","typescript"],"created_at":"2026-01-12T07:20:52.127Z","updated_at":"2026-03-15T19:41:47.969Z","avatar_url":"https://github.com/foo-ogawa.png","language":"TypeScript","readme":"# micro-contracts\n\n[![npm version](https://img.shields.io/npm/v/micro-contracts.svg)](https://www.npmjs.com/package/micro-contracts) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) [![Node.js](https://img.shields.io/badge/Node.js-18%2B-339933?logo=node.js\u0026logoColor=white)](https://nodejs.org/) [![TypeScript](https://img.shields.io/badge/TypeScript-5.0%2B-3178C6?logo=typescript\u0026logoColor=white)](https://www.typescriptlang.org/) [![OpenAPI](https://img.shields.io/badge/OpenAPI-3.x-6BA539?logo=openapiinitiative\u0026logoColor=white)](https://www.openapis.org/)\n\n**Contract-first vertical slices for TypeScript Web/API systems.**\n\nmicro-contracts is a contract-first toolchain for TypeScript Web/API development. It tackles common failure modes—**frontend/backend contract drift**, **duplicated \"common\" rules**, and **accidental breaking changes in public APIs**—by treating **OpenAPI as the Single Source of Truth (SSoT)**.\n\nContracts alone aren't enough—they must be **enforceable**. micro-contracts includes **[Enforceable Guardrails](docs/development-guardrails.md)** that prevent both humans and AI from bypassing the contract-first workflow: blocking direct edits to generated files, detecting drift, and verifying security declarations match implementations.\n\n## Design Philosophy\n\n![Architecture](docs/architecture.svg)\n\nThe core architecture is organized along two axes:\n\n| Axis | Description | Example |\n|------|-------------|---------|\n| **Vertical (feature-aligned slices)** | A *module* is a feature-aligned contract boundary. The same contract spans UI (frontend) and API (backend). | `core`, `billing`, `users` |\n| **Horizontal (cross-cutting concerns)** | Auth, tenancy, rate limiting, and shared error behavior are applied consistently via **OpenAPI Overlays**. | `x-middleware: [requireAuth, tenantIsolation]` |\n\n### Key Differentiators\n\n| # | Differentiator | What it means |\n|---|----------------|---------------|\n| 1 | **Vertical Modules + Horizontal Overlays** | Feature-aligned modules as contract boundaries; cross-cutting concerns (auth, rate-limit) injected via [OpenAPI Overlays](https://www.openapis.org/blog/2024/10/22/announcing-overlay-specification). |\n| 2 | **OpenAPI as SSoT → Multi-artifact generation** | Single spec generates contract packages, server routes, and frontend clients. No manual sync required. |\n| 3 | **Enforceable Guardrails** | Built-in checks prevent bypassing contract-first workflow—blocks direct edits to generated files, detects drift, verifies security declarations. See **[Guardrails](docs/development-guardrails.md)**. |\n| 4 | **Public Surface Governance** | `contract-published` is extracted (not duplicated) from the master contract. `x-micro-contracts-non-exportable` fails generation if internal data leaks. |\n| 5 | **Explicit Module Dependencies** | `x-micro-contracts-depend-on` declares cross-module dependencies. `deps/` re-exports only declared types; enables impact analysis. |\n\n---\n\n## Who is this for?\n\n| Scenario | Why micro-contracts helps |\n|----------|---------------------------|\n| **Modular monolith → microservices** | Same contracts work in monolith or split services; dependency tracking prevents hidden coupling |\n| **Multiple teams sharing OpenAPI** | Explicit module dependencies make cross-team impact visible |\n| **Published API with compatibility SLA** | `contract-published` extraction + `x-micro-contracts-non-exportable` fail-fast prevents accidental exposure |\n| **Cross-cutting concerns at scale** | OpenAPI Overlays inject auth/rate-limit/tenancy without copy-paste |\n\n**Not the best fit for:** Single-developer projects, auto-generated UI from schema, multi-language SDK generation (use OpenAPI Generator instead).\n\n---\n\n## Quick Start\n\n\u003e **Prerequisites**: Node.js 18+, TypeScript 5.0+, ESM (`\"type\": \"module\"`).\n\n```bash\n# 1. Install\nnpm install --save-dev micro-contracts\n\n# 2. Initialize module structure\nnpx micro-contracts init core --openapi path/to/your/spec.yaml\n\n# 3. Generate all code\nnpx micro-contracts generate\n```\n\n```typescript\n// 4. Use in your server\nimport { registerRoutes } from './core/routes.generated.js';\nawait registerRoutes(fastify);\n```\n\n\u003e **What `init` creates**: The `init` command creates starter templates for **Fastify** (server) and **fetch API** (client).\n\u003e These are scaffolds to get you started — modify them for your framework (Express, Hono, Axios, etc.) or add new output types.\n\u003e\n\u003e **📦 Full working example**: See [`examples/`](./examples/) for a complete project with multiple modules, overlays, and cross-module dependencies.\n\n---\n\n## Core Concepts\n\n### OpenAPI as Single Source of Truth (SSoT)\n\n```\nOpenAPI spec (spec/{module}/openapi/*.yaml)\n    ↓ micro-contracts generate\nContract packages (packages/contract/{module}/)\n    ├── schemas/types.ts       # Request/Response types\n    ├── services/              # Service interfaces\n    └── overlays/              # Overlay handler interfaces\n    ↓\nServer routes + Frontend clients (generated via templates)\n```\n\n### Modules vs Services\n\n| Concept | Definition | Example |\n|---------|------------|---------|\n| **Module** | Logical contract boundary (OpenAPI + Service) | `core`, `billing`, `users` |\n| **Service** | Deployment unit (can contain 1+ modules) | `api-server` |\n\nA monolith may have multiple modules in one service. Start with multiple modules in one service and split later as needed.\n\n### Contract Packages\n\n| Package | Description | Compatibility Policy |\n|---------|-------------|---------------------|\n| `contract` | Master contract (all APIs) | Internal APIs can change freely |\n| `contract-published` | Public APIs only (`x-micro-contracts-published: true`) | Must maintain backward compatibility |\n\n**Key insight**: `contract-published` is **extracted from** `contract` (not generated separately). This ensures a single SSoT.\n\n### Cross-cutting Concerns with Overlays\n\n1. Mark operations with `x-middleware` (or custom extensions) in OpenAPI\n2. Define overlay that adds params/responses when extension is present\n3. Generator applies overlays and produces `openapi.generated.yaml`\n4. Generate code from the result\n\n\u003e **📖 Deep Dive**: See **[OpenAPI Overlays (Deep Dive)](docs/overlays-deep-dive.md)** for complete examples and configuration.\n\n---\n\n## Directory Structure\n\n```\nproject/\n├── spec/                              # ✅ Human-edited (contract source of truth)\n│   ├── spectral.yaml                  #    Global lint rules\n│   ├── default/templates/             #    Handlebars templates (customizable)\n│   ├── _shared/\n│   │   ├── openapi/                   #    Shared schemas (ProblemDetails, etc.)\n│   │   └── overlays/                  #    Cross-module overlays\n│   └── {module}/\n│       ├── openapi/{module}.yaml      #    OpenAPI spec\n│       └── overlays/                  #    Module-specific overlays\n│\n├── packages/                          # ❌ Auto-generated (DO NOT EDIT)\n│   ├── contract/{module}/\n│   │   ├── schemas/                   #    Types, validators\n│   │   ├── services/                  #    Service interfaces\n│   │   ├── overlays/                  #    Overlay handler interfaces\n│   │   └── deps/                      #    Re-exports from dependencies\n│   └── contract-published/{module}/   #    Public API subset\n│\n├── server/src/{module}/\n│   ├── routes.generated.ts            # ❌ Auto-generated (template: fastify-routes.hbs)\n│   ├── services/                      # ✅ Human-edited (service implementations)\n│   └── overlays/                      # ✅ Human-edited (overlay implementations)\n│\n└── frontend/src/{module}/\n    └── api.generated.ts               # ❌ Auto-generated (template: fetch-client.hbs)\n```\n\n\u003e **Note**: `*.generated.ts` files are generated from Handlebars templates in `spec/default/templates/`.\n\u003e You can customize or replace templates for different frameworks (Express, Hono, Axios, etc.).\n\u003e\n\u003e **Why commit generated files?** Generated artifacts are committed to enable code review of contract changes and CI drift detection. If spec changes but generated code doesn't match, CI fails.\n\n---\n\n## OpenAPI Extensions\n\n### Required Extensions\n\n| Extension | Type | Description |\n|-----------|------|-------------|\n| `x-micro-contracts-service` | string | Service class name (e.g., `User`, `Order`) |\n| `x-micro-contracts-method` | string | Method name to call (should match `operationId`) |\n\n### Optional Extensions\n\n| Extension | Type | Description |\n|-----------|------|-------------|\n| `x-micro-contracts-published` | boolean | Include in `contract-published` (compatibility SLA) |\n| `x-micro-contracts-non-exportable` | boolean | Mark as non-exportable (fails if used in published endpoints) |\n| `x-micro-contracts-depend-on` | string[] | Explicit dependencies on other modules' published APIs |\n\n### Example\n\n```yaml\npaths:\n  /api/users:\n    get:\n      operationId: getUsers\n      x-micro-contracts-service: User\n      x-micro-contracts-method: getUsers\n      x-micro-contracts-published: true\n      x-middleware: [requireAuth]            # Custom extension for overlays\n      responses:\n        '200':\n          description: Success\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/UserListResponse'\n```\n\n### Module Dependencies\n\nDeclare dependencies with `x-micro-contracts-depend-on`:\n\n```yaml\n# spec/billing/openapi/billing.yaml\ninfo:\n  x-micro-contracts-depend-on:\n    - core.User.getUsers\n    - core.User.getUserById\n```\n\nImport via generated `deps/`:\n\n```typescript\n// ✅ Recommended: Import from deps/\nimport type { User } from '@project/contract/billing/deps/core';\n\n// ❌ Avoid: Direct contract-published import\nimport type { User } from '@project/contract-published/core/schemas';\n```\n\n---\n\n## Configuration\n\nCreate `micro-contracts.config.yaml`. All paths support `{module}` placeholder.\n\n### defaults\n\n| Key | Type | Required | Description |\n|-----|------|----------|-------------|\n| `contract.output` | string | yes | Output directory for contract packages |\n| `contract.serviceTemplate` | string | no | Custom Handlebars template for service interface generation |\n| `contractPublic.output` | string | yes | Output directory for public contract packages |\n| `outputs.\u003cid\u003e.output` | string | yes | Output file path |\n| `outputs.\u003cid\u003e.template` | string | yes | Handlebars template file path |\n| `outputs.\u003cid\u003e.overwrite` | boolean | no | Overwrite existing files (default: `true`) |\n| `outputs.\u003cid\u003e.condition` | string | no | `hasPublicEndpoints` \\| `hasOverlays` \\| `always` (default: `always`) |\n| `outputs.\u003cid\u003e.enabled` | boolean | no | Enable/disable this output (default: `true`) |\n| `outputs.\u003cid\u003e.config` | object | no | Template-specific configuration passed to context |\n| `overlays.shared` | string[] | no | Overlay files applied to all modules |\n| `overlays.collision` | string | no | `error` \\| `warn` \\| `last-wins` (default: `error`) |\n| `docs.enabled` | boolean | no | Enable documentation generation (default: `true`) |\n| `docs.template` | string | no | Documentation template |\n| `sharedModuleName` | string | no | Shared module name for overlays |\n\n### modules.\\\u003cname\\\u003e\n\n| Key | Type | Required | Description |\n|-----|------|----------|-------------|\n| `openapi` | string | yes | Path to OpenAPI spec file |\n| `contract.output` | string | no | Override contract output directory |\n| `contract.serviceTemplate` | string | no | Override custom service interface template |\n| `contractPublic.output` | string | no | Override public contract output directory |\n| `outputs.\u003cid\u003e.enabled` | boolean | no | Enable/disable specific output for this module |\n| `outputs.\u003cid\u003e.*` | — | no | Override any output config field |\n| `overlays` | string[] | no | Module-specific overlay files |\n| `dependsOn` | string[] | no | Dependencies (`{module}.{service}.{method}`) |\n| `spectral` | string | no | Module-specific Spectral config path |\n| `docs.enabled` | boolean | no | Override documentation generation |\n\n### Example\n\n```yaml\ndefaults:\n  contract:\n    output: packages/contract/{module}\n  contractPublic:\n    output: packages/contract-published/{module}\n  outputs:\n    server-routes:\n      output: server/src/{module}/routes.generated.ts\n      template: spec/default/templates/fastify-routes.hbs\n    frontend-api:\n      output: frontend/src/{module}/api.generated.ts\n      template: spec/default/templates/fetch-client.hbs\n  overlays:\n    shared:\n      - spec/_shared/overlays/middleware.overlay.yaml\n\nmodules:\n  core:\n    openapi: openapi/core.yaml\n  billing:\n    openapi: openapi/billing.yaml\n    dependsOn:\n      - core.User.getUsers\n    outputs:\n      frontend-api:\n        enabled: false\n```\n\n---\n\n## CLI Reference\n\n### generate\n\nGenerate code from OpenAPI specifications.\n\n| Option | Description |\n|--------|-------------|\n| `-c, --config \u003cpath\u003e` | Path to config file |\n| `-m, --module \u003cnames\u003e` | Module names, comma-separated (default: all) |\n| `--contracts-only` | Generate contract packages only |\n| `--server-only` | Generate server routes only |\n| `--frontend-only` | Generate frontend clients only |\n| `--docs-only` | Generate documentation only |\n| `--skip-lint` | Skip linting before generation |\n| `--no-manifest` | Skip manifest generation |\n| `--manifest-dir \u003cpath\u003e` | Directory for manifest (default: `packages/`) |\n| `--force` | Bypass input hash cache and always regenerate |\n| `--no-cache` | Run without reading or writing input hash cache |\n\n### init \\\u003cmodule\\\u003e\n\nInitialize a new module structure with starter templates.\n\n| Option | Description |\n|--------|-------------|\n| `-d, --dir \u003cpath\u003e` | Base directory (default: `src`) |\n| `-i, --openapi \u003cpath\u003e` | OpenAPI spec to process (auto-adds extensions) |\n| `-o, --output \u003cpath\u003e` | Output path for processed OpenAPI |\n| `--skip-templates` | Skip creating starter templates |\n\n### lint \\\u003cinput\\\u003e\n\nLint OpenAPI specification.\n\n| Option | Description |\n|--------|-------------|\n| `--strict` | Treat warnings as errors |\n\n### check\n\nRun guardrail checks.\n\n| Option | Description |\n|--------|-------------|\n| `--only \u003cchecks\u003e` | Run only specific checks (comma-separated) |\n| `--skip \u003cchecks\u003e` | Skip specific checks (comma-separated) |\n| `--gate \u003cgates\u003e` | Run checks for specific gates only (1-5) |\n| `-v, --verbose` | Enable verbose output |\n| `--fix` | Auto-fix issues where possible |\n| `-g, --guardrails \u003cpath\u003e` | Path to `guardrails.yaml` |\n| `-d, --generated-dir \u003cpath\u003e` | Path to generated files directory (default: `packages/`) |\n| `--changed-files \u003cpath\u003e` | Path to file containing changed files (for CI) |\n| `--list` | List available checks |\n| `--list-gates` | List available gates |\n\n### pipeline\n\nRun full guardrails pipeline: **Gate 1,2 → Generate → Gate 3,4,5**.\n\n| Option | Description |\n|--------|-------------|\n| `-c, --config \u003cpath\u003e` | Path to config file |\n| `-v, --verbose` | Enable verbose output |\n| `--skip \u003cchecks\u003e` | Skip specific checks (comma-separated) |\n| `--continue-on-error` | Continue running even if a step fails |\n| `-g, --guardrails \u003cpath\u003e` | Path to `guardrails.yaml` |\n| `-d, --generated-dir \u003cpath\u003e` | Path to generated files directory (default: `packages/`) |\n| `--no-manifest` | Skip manifest generation |\n| `--skip-lint` | Skip linting before generation |\n| `--contracts-only` | Generate contract packages only |\n| `--server-only` | Generate server routes only |\n| `--frontend-only` | Generate frontend clients only |\n| `--docs-only` | Generate documentation only |\n| `--force` | Bypass input hash cache and always regenerate |\n| `--no-cache` | Run without reading or writing input hash cache |\n\n\u003e See **[Enforceable Guardrails](docs/development-guardrails.md)** for gate details and CI configuration.\n\n### deps\n\nAnalyze module dependencies.\n\n| Option | Description |\n|--------|-------------|\n| `-c, --config \u003cpath\u003e` | Path to config file |\n| `-m, --module \u003cname\u003e` | Module to analyze |\n| `--graph` | Output dependency graph (Mermaid) |\n| `--impact \u003cref\u003e` | Analyze impact of changing a specific API |\n| `--who-depends-on \u003cref\u003e` | Find modules that depend on a specific API |\n| `--validate` | Validate dependencies against OpenAPI declarations |\n\n### guardrails-init\n\nCreate a `guardrails.yaml` configuration file.\n\n| Option | Description |\n|--------|-------------|\n| `-o, --output \u003cpath\u003e` | Output path (default: `guardrails.yaml`) |\n\n### manifest\n\nGenerate or verify manifest for generated artifacts.\n\n| Option | Description |\n|--------|-------------|\n| `-d, --dir \u003cpath\u003e` | Directory to scan (default: `packages/`) |\n| `--verify` | Verify existing manifest |\n| `-o, --output \u003cpath\u003e` | Output manifest path |\n\n---\n\n## Generated Code\n\n### Service Interface\n\n```typescript\n// packages/contract/core/services/UserServiceApi.ts\nexport interface UserServiceApi {\n  getUsers(input: UserService_getUsersInput): Promise\u003cUserListResponse\u003e;\n  getUserById(input: UserService_getUserByIdInput): Promise\u003cUser\u003e;\n}\n```\n\n### Service Implementation\n\n```typescript\n// server/src/core/services/UserService.ts\nimport type { UserServiceApi } from '@project/contract/core/services/UserServiceApi.js';\n\nexport class UserService implements UserServiceApi {\n  async getUsers(input) {\n    // Input is HTTP-agnostic: { limit?: number, offset?: number }\n    return { users: [...], total: 100 };\n  }\n}\n```\n\n---\n\n## Related Documentation\n\n| Document | Description |\n|----------|-------------|\n| **[Examples](./examples/)** | Complete working project with multiple modules, overlays, and cross-module dependencies |\n| **[OpenAPI Overlays (Deep Dive)](docs/overlays-deep-dive.md)** | Complete overlay examples, JSONPath patterns, template context |\n| **[Enforceable Guardrails (AI-ready)](docs/development-guardrails.md)** | CI integration, security checks, allowlist configuration |\n\n---\n\n## Comparison with Similar Tools\n\n| Aspect | micro-contracts | OpenAPI Generator | ts-rest |\n|--------|-----------------|-------------------|---------|\n| **Primary focus** | Contract governance (server + frontend + CI) | Multi-language SDK generation | TypeScript-first contract |\n| **SSoT** | OpenAPI | OpenAPI | TypeScript |\n| **Multi-artifact generation** | ✅ contract + routes + clients | △ SDK-focused (different goal) | ✅ Strong client/server alignment |\n| **Enforceable guardrails** | ✅ Built-in (drift, no direct edit, CI gates) | ❌ Requires separate design | ❌ Requires separate design |\n| **Public API governance** | ✅ `contract-published` + fail-fast | ❌ Manual | ❌ N/A |\n| **Module dependencies** | ✅ `x-micro-contracts-depend-on` + `deps/` | ❌ Manual | ❌ Manual |\n| **Cross-cutting concerns** | ✅ OpenAPI Overlays | ❌ Manual | △ Code-level implementation |\n\n\n---\n\n## License\n\nMIT\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffoo-ogawa%2Fmicro-contracts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffoo-ogawa%2Fmicro-contracts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffoo-ogawa%2Fmicro-contracts/lists"}