Contract Types Overview

Learn about the different contract types in ContractSpec and when to use each one for spec-first development.

Core Contract Types

Operations

API endpoints with input/output schemas, validation, and policy.

defineCommand / defineQuery

Events

Domain events with typed payloads and PII handling.

defineEvent

Capabilities

Feature groupings that link operations, events, and UIs.

defineCapability

Presentations

UI specifications for rendering data and handling interactions.

definePresentation

1) Operations (Commands & Queries)

Operations are the backbone of your API. Commands mutate state, queries read state. Both have typed input/output schemas.

src/contracts/user.operation.ts
import { defineCommand, defineQuery } from "@contractspec/lib.contracts";
import { SchemaModel, ScalarTypeEnum } from "@contractspec/lib.schema";

// Command: Mutates state (creates a user)
export const CreateUserCommand = defineCommand({
  meta: {
    key: "user.create",
    version: "1.0.0",
    description: "Create a new user account",
    stability: "stable",
    owners: ["platform.core"],
    tags: ["users", "auth"],
  },
  io: {
    input: new SchemaModel({
      name: "CreateUserInput",
      fields: {
        email: { type: ScalarTypeEnum.Email(), isOptional: false },
        name: { type: ScalarTypeEnum.NonEmptyString(), isOptional: false },
      },
    }),
    output: new SchemaModel({
      name: "CreateUserOutput",
      fields: {
        id: { type: ScalarTypeEnum.UUID(), isOptional: false },
        email: { type: ScalarTypeEnum.Email(), isOptional: false },
      },
    }),
    errors: {
      EMAIL_EXISTS: {
        description: "Email already registered",
        http: 409,
        when: "User with this email already exists",
      },
    },
  },
  policy: { auth: "admin" },
});

// Query: Reads state (gets user by ID)
export const GetUserQuery = defineQuery({
  meta: {
    key: "user.get",
    version: "1.0.0",
    description: "Get user by ID",
    stability: "stable",
    owners: ["platform.core"],
    tags: ["users"],
  },
  io: {
    input: new SchemaModel({
      name: "GetUserInput",
      fields: {
        id: { type: ScalarTypeEnum.UUID(), isOptional: false },
      },
    }),
    output: new SchemaModel({
      name: "GetUserOutput",
      fields: {
        id: { type: ScalarTypeEnum.UUID(), isOptional: false },
        email: { type: ScalarTypeEnum.Email(), isOptional: false },
        name: { type: ScalarTypeEnum.String(), isOptional: false },
      },
    }),
  },
  policy: { auth: "user" },
});

2) Events

Events represent domain occurrences. They have typed payloads with PII field marking for compliance.

src/contracts/user.event.ts
import { defineEvent } from "@contractspec/lib.contracts";
import { SchemaModel, ScalarTypeEnum } from "@contractspec/lib.schema";

export const UserCreatedEvent = defineEvent({
  meta: {
    key: "user.created",
    version: "1.0.0",
    description: "Emitted when a new user is created",
    stability: "stable",
    owners: ["platform.core"],
    tags: ["users", "auth"],
  },
  // Mark PII fields for redaction in logs/exports
  pii: ["email"],
  payload: new SchemaModel({
    name: "UserCreatedPayload",
    fields: {
      userId: { type: ScalarTypeEnum.UUID(), isOptional: false },
      email: { type: ScalarTypeEnum.Email(), isOptional: false },
      createdAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
    },
  }),
});

export const UserDeletedEvent = defineEvent({
  meta: {
    key: "user.deleted",
    version: "1.0.0",
    description: "Emitted when a user is deleted",
    stability: "stable",
    owners: ["platform.core"],
    tags: ["users"],
  },
  payload: new SchemaModel({
    name: "UserDeletedPayload",
    fields: {
      userId: { type: ScalarTypeEnum.UUID(), isOptional: false },
      deletedAt: { type: ScalarTypeEnum.DateTime(), isOptional: false },
      reason: { type: ScalarTypeEnum.String(), isOptional: true },
    },
  }),
});

3) Capabilities

Capabilities group related operations, events, and presentations. They define feature boundaries and dependencies.

src/contracts/user-management.capability.ts
import { defineCapability } from "@contractspec/lib.contracts";

export const UserManagementCapability = defineCapability({
  meta: {
    key: "user.management",
    version: "1.0.0",
    description: "User account management features",
    stability: "stable",
    owners: ["platform.core"],
    tags: ["users", "core"],
    kind: "api",
  },
  // Operations/events this capability provides
  provides: [
    { surface: "operation", key: "user.create", version: "1.0.0" },
    { surface: "operation", key: "user.get", version: "1.0.0" },
    { surface: "operation", key: "user.delete", version: "1.0.0" },
    { surface: "event", key: "user.created", version: "1.0.0" },
    { surface: "event", key: "user.deleted", version: "1.0.0" },
  ],
  // Dependencies on other capabilities
  requires: [
    { key: "auth.core", reason: "Needs authentication for user operations" },
  ],
});

// Capabilities can extend other capabilities
export const AdminUserCapability = defineCapability({
  meta: {
    key: "user.admin",
    version: "1.0.0",
    description: "Admin-only user management features",
    stability: "stable",
    owners: ["platform.core"],
    tags: ["users", "admin"],
    kind: "api",
  },
  // Inherit from base capability
  extends: { key: "user.management", version: "1.0.0" },
  provides: [
    { surface: "operation", key: "user.ban", version: "1.0.0" },
    { surface: "operation", key: "user.impersonate", version: "1.0.0" },
  ],
});

4) Presentations

Presentations define UI specifications. They link to capabilities and specify how data should be displayed.

src/contracts/user-list.presentation.ts
import { definePresentation } from "@contractspec/lib.contracts";

export const UserListPresentation = definePresentation({
  meta: {
    key: "users.list",
    version: "1.0.0",
    description: "Display a list of users with search and pagination",
    stability: "stable",
    owners: ["platform.core"],
    tags: ["users", "ui"],
  },
  // Link to capability that provides data
  capability: { key: "user.management", version: "1.0.0" },
  // UI specifications
  config: {
    title: "Users",
    layout: "table",
    columns: ["name", "email", "createdAt", "status"],
    actions: ["view", "edit", "delete"],
    pagination: { defaultPageSize: 25 },
    search: { fields: ["name", "email"] },
  },
});

5) Additional Contract Types

ContractSpec provides specialized contracts for different concerns:

Policy

definePolicy

RBAC/ABAC rules, rate limits, and access control.

Workflow

defineWorkflow

Multi-step processes with states, transitions, and SLAs.

Translation

defineTranslation

i18n messages with ICU format and locale fallbacks.

Integration

defineIntegration

External service connections and API adapters.

Form

defineFormSpec

Form definitions with fields, validation, and layouts.

Data View

defineDataView

Read-only data projections and aggregations.

Feature

defineFeature

Feature flags and progressive rollout configurations.

Test

defineTestSpec

Contract-driven test scenarios and fixtures.

6) Registering Contracts

Each contract type has a registry for lookup and validation.

src/contracts/registry.ts
import {
  OperationSpecRegistry,
  installOp,
} from "@contractspec/lib.contracts/operations";
import { EventRegistry } from "@contractspec/lib.contracts";
import { CapabilityRegistry } from "@contractspec/lib.contracts/capabilities";

// Import your contracts
import { CreateUserCommand, GetUserQuery } from "./user.operation";
import { UserCreatedEvent } from "./user.event";
import { UserManagementCapability } from "./user-management.capability";

// Create registries
export const operationRegistry = new OperationSpecRegistry();
export const eventRegistry = new EventRegistry();
export const capabilityRegistry = new CapabilityRegistry();

// Register operations with handlers
installOp(operationRegistry, CreateUserCommand, async (input) => {
  // Your implementation here
  return { id: "uuid", email: input.email };
});

installOp(operationRegistry, GetUserQuery, async (input) => {
  // Your implementation here
  return { id: input.id, email: "user@example.com", name: "User" };
});

// Register events and capabilities
eventRegistry.register(UserCreatedEvent);
capabilityRegistry.register(UserManagementCapability);

Contract Type Decision Guide

When you need...Use this contract
An API endpoint that changes datadefineCommand
An API endpoint that reads datadefineQuery
Async notification of something that happeneddefineEvent
Group related specs under a featuredefineCapability
Define UI rendering specificationsdefinePresentation
Access control and rate limitingdefinePolicy
Multi-step business processesdefineWorkflow

Want visual contract design?

Studio provides a visual editor for creating and managing contracts with team collaboration and version control.

Join Studio waitlist