https://github.com/foo-ogawa/micro-contracts
Contract-first vertical slices for TypeScript Web/API systems. OpenAPI as Single Source of Truth with enforceable guardrails.
https://github.com/foo-ogawa/micro-contracts
api-design code-generation contract-first developer-tools microservices monolith openapi typescript
Last synced: 22 days ago
JSON representation
Contract-first vertical slices for TypeScript Web/API systems. OpenAPI as Single Source of Truth with enforceable guardrails.
- Host: GitHub
- URL: https://github.com/foo-ogawa/micro-contracts
- Owner: foo-ogawa
- License: mit
- Created: 2026-01-06T01:08:34.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-01-12T12:53:46.000Z (3 months ago)
- Last Synced: 2026-01-12T15:23:56.846Z (3 months ago)
- Topics: api-design, code-generation, contract-first, developer-tools, microservices, monolith, openapi, typescript
- Language: TypeScript
- Homepage:
- Size: 426 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# micro-contracts
[](https://www.npmjs.com/package/micro-contracts) [](https://opensource.org/licenses/MIT) [](https://nodejs.org/) [](https://www.typescriptlang.org/) [](https://www.openapis.org/)
**Contract-first vertical slices for TypeScript Web/API systems.**
micro-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)**.
Contracts 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.
## Design Philosophy

The core architecture is organized along two axes:
| Axis | Description | Example |
|------|-------------|---------|
| **Vertical (feature-aligned slices)** | A *module* is a feature-aligned contract boundary. The same contract spans UI (frontend) and API (backend). | `core`, `billing`, `users` |
| **Horizontal (cross-cutting concerns)** | Auth, tenancy, rate limiting, and shared error behavior are applied consistently via **OpenAPI Overlays**. | `x-middleware: [requireAuth, tenantIsolation]` |
### Key Differentiators
| # | Differentiator | What it means |
|---|----------------|---------------|
| 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). |
| 2 | **OpenAPI as SSoT → Multi-artifact generation** | Single spec generates contract packages, server routes, and frontend clients. No manual sync required. |
| 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)**. |
| 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. |
| 5 | **Explicit Module Dependencies** | `x-micro-contracts-depend-on` declares cross-module dependencies. `deps/` re-exports only declared types; enables impact analysis. |
---
## Who is this for?
| Scenario | Why micro-contracts helps |
|----------|---------------------------|
| **Modular monolith → microservices** | Same contracts work in monolith or split services; dependency tracking prevents hidden coupling |
| **Multiple teams sharing OpenAPI** | Explicit module dependencies make cross-team impact visible |
| **Published API with compatibility SLA** | `contract-published` extraction + `x-micro-contracts-non-exportable` fail-fast prevents accidental exposure |
| **Cross-cutting concerns at scale** | OpenAPI Overlays inject auth/rate-limit/tenancy without copy-paste |
**Not the best fit for:** Single-developer projects, auto-generated UI from schema, multi-language SDK generation (use OpenAPI Generator instead).
---
## Quick Start
> **Prerequisites**: Node.js 18+, TypeScript 5.0+, ESM (`"type": "module"`).
```bash
# 1. Install
npm install --save-dev micro-contracts
# 2. Initialize module structure
npx micro-contracts init core --openapi path/to/your/spec.yaml
# 3. Generate all code
npx micro-contracts generate
```
```typescript
// 4. Use in your server
import { registerRoutes } from './core/routes.generated.js';
await registerRoutes(fastify);
```
> **What `init` creates**: The `init` command creates starter templates for **Fastify** (server) and **fetch API** (client).
> These are scaffolds to get you started — modify them for your framework (Express, Hono, Axios, etc.) or add new output types.
>
> **📦 Full working example**: See [`examples/`](./examples/) for a complete project with multiple modules, overlays, and cross-module dependencies.
---
## Core Concepts
### OpenAPI as Single Source of Truth (SSoT)
```
OpenAPI spec (spec/{module}/openapi/*.yaml)
↓ micro-contracts generate
Contract packages (packages/contract/{module}/)
├── schemas/types.ts # Request/Response types
├── services/ # Service interfaces
└── overlays/ # Overlay handler interfaces
↓
Server routes + Frontend clients (generated via templates)
```
### Modules vs Services
| Concept | Definition | Example |
|---------|------------|---------|
| **Module** | Logical contract boundary (OpenAPI + Service) | `core`, `billing`, `users` |
| **Service** | Deployment unit (can contain 1+ modules) | `api-server` |
A monolith may have multiple modules in one service. Start with multiple modules in one service and split later as needed.
### Contract Packages
| Package | Description | Compatibility Policy |
|---------|-------------|---------------------|
| `contract` | Master contract (all APIs) | Internal APIs can change freely |
| `contract-published` | Public APIs only (`x-micro-contracts-published: true`) | Must maintain backward compatibility |
**Key insight**: `contract-published` is **extracted from** `contract` (not generated separately). This ensures a single SSoT.
### Cross-cutting Concerns with Overlays
1. Mark operations with `x-middleware` (or custom extensions) in OpenAPI
2. Define overlay that adds params/responses when extension is present
3. Generator applies overlays and produces `openapi.generated.yaml`
4. Generate code from the result
> **📖 Deep Dive**: See **[OpenAPI Overlays (Deep Dive)](docs/overlays-deep-dive.md)** for complete examples and configuration.
---
## Directory Structure
```
project/
├── spec/ # ✅ Human-edited (contract source of truth)
│ ├── spectral.yaml # Global lint rules
│ ├── default/templates/ # Handlebars templates (customizable)
│ ├── _shared/
│ │ ├── openapi/ # Shared schemas (ProblemDetails, etc.)
│ │ └── overlays/ # Cross-module overlays
│ └── {module}/
│ ├── openapi/{module}.yaml # OpenAPI spec
│ └── overlays/ # Module-specific overlays
│
├── packages/ # ❌ Auto-generated (DO NOT EDIT)
│ ├── contract/{module}/
│ │ ├── schemas/ # Types, validators
│ │ ├── services/ # Service interfaces
│ │ ├── overlays/ # Overlay handler interfaces
│ │ └── deps/ # Re-exports from dependencies
│ └── contract-published/{module}/ # Public API subset
│
├── server/src/{module}/
│ ├── routes.generated.ts # ❌ Auto-generated (template: fastify-routes.hbs)
│ ├── services/ # ✅ Human-edited (service implementations)
│ └── overlays/ # ✅ Human-edited (overlay implementations)
│
└── frontend/src/{module}/
└── api.generated.ts # ❌ Auto-generated (template: fetch-client.hbs)
```
> **Note**: `*.generated.ts` files are generated from Handlebars templates in `spec/default/templates/`.
> You can customize or replace templates for different frameworks (Express, Hono, Axios, etc.).
>
> **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.
---
## OpenAPI Extensions
### Required Extensions
| Extension | Type | Description |
|-----------|------|-------------|
| `x-micro-contracts-service` | string | Service class name (e.g., `User`, `Order`) |
| `x-micro-contracts-method` | string | Method name to call (should match `operationId`) |
### Optional Extensions
| Extension | Type | Description |
|-----------|------|-------------|
| `x-micro-contracts-published` | boolean | Include in `contract-published` (compatibility SLA) |
| `x-micro-contracts-non-exportable` | boolean | Mark as non-exportable (fails if used in published endpoints) |
| `x-micro-contracts-depend-on` | string[] | Explicit dependencies on other modules' published APIs |
### Example
```yaml
paths:
/api/users:
get:
operationId: getUsers
x-micro-contracts-service: User
x-micro-contracts-method: getUsers
x-micro-contracts-published: true
x-middleware: [requireAuth] # Custom extension for overlays
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/UserListResponse'
```
### Module Dependencies
Declare dependencies with `x-micro-contracts-depend-on`:
```yaml
# spec/billing/openapi/billing.yaml
info:
x-micro-contracts-depend-on:
- core.User.getUsers
- core.User.getUserById
```
Import via generated `deps/`:
```typescript
// ✅ Recommended: Import from deps/
import type { User } from '@project/contract/billing/deps/core';
// ❌ Avoid: Direct contract-published import
import type { User } from '@project/contract-published/core/schemas';
```
---
## Configuration
Create `micro-contracts.config.yaml`. All paths support `{module}` placeholder.
### defaults
| Key | Type | Required | Description |
|-----|------|----------|-------------|
| `contract.output` | string | yes | Output directory for contract packages |
| `contract.serviceTemplate` | string | no | Custom Handlebars template for service interface generation |
| `contractPublic.output` | string | yes | Output directory for public contract packages |
| `outputs..output` | string | yes | Output file path |
| `outputs..template` | string | yes | Handlebars template file path |
| `outputs..overwrite` | boolean | no | Overwrite existing files (default: `true`) |
| `outputs..condition` | string | no | `hasPublicEndpoints` \| `hasOverlays` \| `always` (default: `always`) |
| `outputs..enabled` | boolean | no | Enable/disable this output (default: `true`) |
| `outputs..config` | object | no | Template-specific configuration passed to context |
| `overlays.shared` | string[] | no | Overlay files applied to all modules |
| `overlays.collision` | string | no | `error` \| `warn` \| `last-wins` (default: `error`) |
| `docs.enabled` | boolean | no | Enable documentation generation (default: `true`) |
| `docs.template` | string | no | Documentation template |
| `sharedModuleName` | string | no | Shared module name for overlays |
### modules.\
| Key | Type | Required | Description |
|-----|------|----------|-------------|
| `openapi` | string | yes | Path to OpenAPI spec file |
| `contract.output` | string | no | Override contract output directory |
| `contract.serviceTemplate` | string | no | Override custom service interface template |
| `contractPublic.output` | string | no | Override public contract output directory |
| `outputs..enabled` | boolean | no | Enable/disable specific output for this module |
| `outputs..*` | — | no | Override any output config field |
| `overlays` | string[] | no | Module-specific overlay files |
| `dependsOn` | string[] | no | Dependencies (`{module}.{service}.{method}`) |
| `spectral` | string | no | Module-specific Spectral config path |
| `docs.enabled` | boolean | no | Override documentation generation |
### Example
```yaml
defaults:
contract:
output: packages/contract/{module}
contractPublic:
output: packages/contract-published/{module}
outputs:
server-routes:
output: server/src/{module}/routes.generated.ts
template: spec/default/templates/fastify-routes.hbs
frontend-api:
output: frontend/src/{module}/api.generated.ts
template: spec/default/templates/fetch-client.hbs
overlays:
shared:
- spec/_shared/overlays/middleware.overlay.yaml
modules:
core:
openapi: openapi/core.yaml
billing:
openapi: openapi/billing.yaml
dependsOn:
- core.User.getUsers
outputs:
frontend-api:
enabled: false
```
---
## CLI Reference
### generate
Generate code from OpenAPI specifications.
| Option | Description |
|--------|-------------|
| `-c, --config ` | Path to config file |
| `-m, --module ` | Module names, comma-separated (default: all) |
| `--contracts-only` | Generate contract packages only |
| `--server-only` | Generate server routes only |
| `--frontend-only` | Generate frontend clients only |
| `--docs-only` | Generate documentation only |
| `--skip-lint` | Skip linting before generation |
| `--no-manifest` | Skip manifest generation |
| `--manifest-dir ` | Directory for manifest (default: `packages/`) |
| `--force` | Bypass input hash cache and always regenerate |
| `--no-cache` | Run without reading or writing input hash cache |
### init \
Initialize a new module structure with starter templates.
| Option | Description |
|--------|-------------|
| `-d, --dir ` | Base directory (default: `src`) |
| `-i, --openapi ` | OpenAPI spec to process (auto-adds extensions) |
| `-o, --output ` | Output path for processed OpenAPI |
| `--skip-templates` | Skip creating starter templates |
### lint \
Lint OpenAPI specification.
| Option | Description |
|--------|-------------|
| `--strict` | Treat warnings as errors |
### check
Run guardrail checks.
| Option | Description |
|--------|-------------|
| `--only ` | Run only specific checks (comma-separated) |
| `--skip ` | Skip specific checks (comma-separated) |
| `--gate ` | Run checks for specific gates only (1-5) |
| `-v, --verbose` | Enable verbose output |
| `--fix` | Auto-fix issues where possible |
| `-g, --guardrails ` | Path to `guardrails.yaml` |
| `-d, --generated-dir ` | Path to generated files directory (default: `packages/`) |
| `--changed-files ` | Path to file containing changed files (for CI) |
| `--list` | List available checks |
| `--list-gates` | List available gates |
### pipeline
Run full guardrails pipeline: **Gate 1,2 → Generate → Gate 3,4,5**.
| Option | Description |
|--------|-------------|
| `-c, --config ` | Path to config file |
| `-v, --verbose` | Enable verbose output |
| `--skip ` | Skip specific checks (comma-separated) |
| `--continue-on-error` | Continue running even if a step fails |
| `-g, --guardrails ` | Path to `guardrails.yaml` |
| `-d, --generated-dir ` | Path to generated files directory (default: `packages/`) |
| `--no-manifest` | Skip manifest generation |
| `--skip-lint` | Skip linting before generation |
| `--contracts-only` | Generate contract packages only |
| `--server-only` | Generate server routes only |
| `--frontend-only` | Generate frontend clients only |
| `--docs-only` | Generate documentation only |
| `--force` | Bypass input hash cache and always regenerate |
| `--no-cache` | Run without reading or writing input hash cache |
> See **[Enforceable Guardrails](docs/development-guardrails.md)** for gate details and CI configuration.
### deps
Analyze module dependencies.
| Option | Description |
|--------|-------------|
| `-c, --config ` | Path to config file |
| `-m, --module ` | Module to analyze |
| `--graph` | Output dependency graph (Mermaid) |
| `--impact ` | Analyze impact of changing a specific API |
| `--who-depends-on ` | Find modules that depend on a specific API |
| `--validate` | Validate dependencies against OpenAPI declarations |
### guardrails-init
Create a `guardrails.yaml` configuration file.
| Option | Description |
|--------|-------------|
| `-o, --output ` | Output path (default: `guardrails.yaml`) |
### manifest
Generate or verify manifest for generated artifacts.
| Option | Description |
|--------|-------------|
| `-d, --dir ` | Directory to scan (default: `packages/`) |
| `--verify` | Verify existing manifest |
| `-o, --output ` | Output manifest path |
---
## Generated Code
### Service Interface
```typescript
// packages/contract/core/services/UserServiceApi.ts
export interface UserServiceApi {
getUsers(input: UserService_getUsersInput): Promise;
getUserById(input: UserService_getUserByIdInput): Promise;
}
```
### Service Implementation
```typescript
// server/src/core/services/UserService.ts
import type { UserServiceApi } from '@project/contract/core/services/UserServiceApi.js';
export class UserService implements UserServiceApi {
async getUsers(input) {
// Input is HTTP-agnostic: { limit?: number, offset?: number }
return { users: [...], total: 100 };
}
}
```
---
## Related Documentation
| Document | Description |
|----------|-------------|
| **[Examples](./examples/)** | Complete working project with multiple modules, overlays, and cross-module dependencies |
| **[OpenAPI Overlays (Deep Dive)](docs/overlays-deep-dive.md)** | Complete overlay examples, JSONPath patterns, template context |
| **[Enforceable Guardrails (AI-ready)](docs/development-guardrails.md)** | CI integration, security checks, allowlist configuration |
---
## Comparison with Similar Tools
| Aspect | micro-contracts | OpenAPI Generator | ts-rest |
|--------|-----------------|-------------------|---------|
| **Primary focus** | Contract governance (server + frontend + CI) | Multi-language SDK generation | TypeScript-first contract |
| **SSoT** | OpenAPI | OpenAPI | TypeScript |
| **Multi-artifact generation** | ✅ contract + routes + clients | △ SDK-focused (different goal) | ✅ Strong client/server alignment |
| **Enforceable guardrails** | ✅ Built-in (drift, no direct edit, CI gates) | ❌ Requires separate design | ❌ Requires separate design |
| **Public API governance** | ✅ `contract-published` + fail-fast | ❌ Manual | ❌ N/A |
| **Module dependencies** | ✅ `x-micro-contracts-depend-on` + `deps/` | ❌ Manual | ❌ Manual |
| **Cross-cutting concerns** | ✅ OpenAPI Overlays | ❌ Manual | △ Code-level implementation |
---
## License
MIT