Skip to content

foo-log-inc/micro-contracts

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

122 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

micro-contracts

npm version License: MIT Node.js TypeScript OpenAPI

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 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

Architecture

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.
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.
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.
6 Screen Spec Declare frontend screen contracts (ViewModel, navigation, analytics events) in OpenAPI. One YAML drives typed navigation maps and event hooks. See Screen Spec.

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 and extension properties 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").

# 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
// 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/ 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) for complete examples and configuration.

Screen Spec — Frontend Screen Contracts

Standard OpenAPI constructs can also define frontend screen contracts — bridging the API layer and UI components. A single YAML file drives ViewModel types, typed navigation maps, and analytics event hooks.

Screen Spec (OpenAPI YAML)
  ├── ViewModel Types       (from schemas — zero new template work)
  ├── Navigation Map        (from response links → typed routing)
  ├── Event Hooks           (from inline x-event → typed analytics)
  └── Interaction Bindings  (from x-interactions → typed in-page interactions)

Enable with screen: true in module config:

modules:
  myScreens:
    openapi: spec/screens/screens.yaml
    screen: true
    outputs:
      screen-navigation:
        output: frontend/src/screens/navigation.generated.ts
        template: screen-navigation.hbs
      screen-events:
        output: frontend/src/screens/events.generated.ts
        template: screen-events.hbs

Initialize a screen module with starter files:

npx micro-contracts init myScreens --screens

📖 Deep Dive: See Screen Spec for the full guide — YAML structure, x-screen-* extensions, TemplateContext.screens, lint rules, and custom template examples.


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

Screen Spec Extensions

Used in modules with screen: true. See Screen Spec for details.

Extension Type Placement Description
x-screen-const string GET operation Stable constant name (e.g., HOME)
x-screen-id string GET operation Traceability ID (e.g., SCR-001)
x-screen-name string GET operation Generated symbol name (e.g., HomePage)
x-back-navigation boolean GET operation Supports history-based back navigation
x-event string | object | $ref GET / links / post,put,patch,delete / x-interactions Inline analytics event declaration (v0.14+)
x-interactions array GET operation In-page interaction bindings with optional events (v0.14+)
x-events array GET operation Deprecated — use inline x-event instead

Example

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:

# spec/billing/openapi/billing.yaml
info:
  x-micro-contracts-depend-on:
    - core.User.getUsers
    - core.User.getUserById

Import via generated deps/:

// ✅ 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.<id>.output string yes Output file path
outputs.<id>.template string yes Handlebars template file path
outputs.<id>.overwrite boolean no Overwrite existing files (default: true)
outputs.<id>.condition string no hasPublicEndpoints | hasOverlays | always (default: always)
outputs.<id>.enabled boolean no Enable/disable this output (default: true)
outputs.<id>.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.<name>

Key Type Required Description
openapi string yes Path to OpenAPI spec file
screen boolean no Enable screen spec mode (x-screen-* extensions, TemplateContext.screens)
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.<id>.enabled boolean no Enable/disable specific output for this module
outputs.<id>.* 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

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

Machine-readable contract: The full CLI specification is available as cli-contract.yaml (CLI Contracts format). For the detailed reference with exit codes, output contracts, and AI agent policies, see CLI Reference (full).

generate

Generate code from OpenAPI specifications.

Option Description
-c, --config <path> Path to config file
-m, --module <names> 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 <path> Directory for manifest (default: packages/)
--force Bypass input hash cache and always regenerate
--no-cache Run without reading or writing input hash cache

init <module>

Initialize a new module structure with starter templates.

Option Description
-d, --dir <path> Base directory (default: src)
-i, --openapi <path> OpenAPI spec to process (auto-adds extensions)
-o, --output <path> Output path for processed OpenAPI
--skip-templates Skip creating starter templates
--screens Initialize as screen spec module (generates screen templates and starter spec)

lint <input>

Lint OpenAPI specification.

Option Description
--strict Treat warnings as errors

check

Run guardrail checks.

Option Description
--only <checks> Run only specific checks (comma-separated)
--skip <checks> Skip specific checks (comma-separated)
--gate <gates> Run checks for specific gates only (1-5)
-v, --verbose Enable verbose output
--fix Auto-fix issues where possible
-g, --guardrails <path> Path to guardrails.yaml
-d, --generated-dir <path> Path to generated files directory (default: packages/)
--changed-files <path> 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> Path to config file
-v, --verbose Enable verbose output
--skip <checks> Skip specific checks (comma-separated)
--continue-on-error Continue running even if a step fails
-g, --guardrails <path> Path to guardrails.yaml
-d, --generated-dir <path> 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 for gate details and CI configuration.

deps

Analyze module dependencies.

Option Description
-c, --config <path> Path to config file
-m, --module <name> Module to analyze
--graph Output dependency graph (Mermaid)
--impact <ref> Analyze impact of changing a specific API
--who-depends-on <ref> 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 <path> Output path (default: guardrails.yaml)

manifest

Generate or verify manifest for generated artifacts.

Option Description
-d, --dir <path> Directory to scan (default: packages/)
--verify Verify existing manifest
-o, --output <path> Output manifest path

audit-openapi

Run LLM-based OpenAPI design quality audit. Evaluates path design, module boundary alignment, schema bloat, and cross-cutting concern coverage. Requires agent-contracts-runtime.

Option Description
-c, --config <path> Path to config file
-m, --module <name> Module name to audit (default: all)
-a, --adapter <name> SDK adapter (claude, openai, gemini, mock)
--model <name> LLM model override
--show-prompt Output the constructed prompt without calling LLM
--fail-on <level> Minimum severity for non-zero exit (warning, error, critical)
-o, --output <file> Write result to a file
--report-format <fmt> Output format (json, text, yaml; default: text)

review-published

Review published API surface for internal type leakage and backward compatibility risks.

Option Description
-c, --config <path> Path to config file
-m, --module <name> Module name to review (default: all)
-a, --adapter <name> SDK adapter (claude, openai, gemini, mock)
--model <name> LLM model override
--show-prompt Output the constructed prompt without calling LLM
--fail-on <level> Minimum severity for non-zero exit
-o, --output <file> Write result to a file
--report-format <fmt> Output format (default: text)

propose-overlays

Propose cross-cutting overlay candidates for authentication, tenancy, rate limiting, and audit logging.

Option Description
-c, --config <path> Path to config file
-m, --module <name> Module name to analyze (default: all)
-a, --adapter <name> SDK adapter (claude, openai, gemini, mock)
--model <name> LLM model override
--show-prompt Output the constructed prompt without calling LLM
--fail-on <level> Minimum severity for non-zero exit
-o, --output <file> Write result to a file
--report-format <fmt> Output format (default: json)

audit-guardrails

Audit guardrails configuration for drift detection and lint rule coverage. File permission and editing checks have been moved to artifact-contracts.

Option Description
-c, --config <path> Path to config file
-g, --guardrails <path> Path to guardrails.yaml
-a, --adapter <name> SDK adapter (claude, openai, gemini, mock)
--model <name> LLM model override
--show-prompt Output the constructed prompt without calling LLM
--fail-on <level> Minimum severity for non-zero exit
-o, --output <file> Write result to a file
--report-format <fmt> Output format (default: text)

LLM commands require agent-contracts-runtime as a peer dependency. Install it to use these commands, or use --show-prompt to inspect the prompt without calling the LLM.


Generated Code

Service Interface

// packages/contract/core/services/UserServiceApi.ts
export interface UserServiceApi {
  getUsers(input: UserService_getUsersInput): Promise<UserListResponse>;
  getUserById(input: UserService_getUserByIdInput): Promise<User>;
}

Service Implementation

// 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 Complete working project with multiple modules, overlays, and cross-module dependencies
CLI Reference Full CLI reference with exit codes, output contracts, and AI agent policies
CLI Contract Machine-readable CLI specification (CLI Contracts format)
Screen Spec Frontend screen contracts — ViewModel, navigation, analytics events in OpenAPI
OpenAPI Overlays (Deep Dive) Complete overlay examples, JSONPath patterns, template context
Enforceable Guardrails (AI-ready) 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

About

Contract-first vertical slices for TypeScript Web/API systems. OpenAPI as Single Source of Truth with enforceable guardrails.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors