Class Diagrams for API and Service Contracts with Mermaid

Mermaid class diagrams are a great way to document API and service contracts: request/response DTOs, reusable envelopes, pagination, errors, and service interfaces. This guide shows practical patterns and examples you can keep in git alongside your code.

Table of Contents

Class diagrams aren’t just for OO design docs.

They’re incredibly useful for API and service contracts:

  • What fields exist in requests/responses?
  • Which types are shared across services?
  • What’s required vs optional?
  • How do errors, pagination, and versions work?

Mermaid class diagrams let you keep that contract next to the code, review it in PRs, and update it as your API evolves.

This guide focuses on contract-first diagramming: DTOs, envelopes, error shapes, and service interfaces.


A quick refresher

Mermaid class diagrams start with classDiagram.

Source

Rendered

classDiagram class Order { +string id +string status +decimal total }

Visibility markers (useful even for contracts)

  • + public (exposed in the contract)
  • - private (internal detail)
  • # protected (usually not needed for contracts)
  • ~ package/internal

For API contracts, it’s common to use + for all public fields and omit internal ones.


1) Model request/response DTOs (the fastest win)

A clean “GET” response

Source

Rendered

classDiagram class GetOrderResponse { +Order order +RequestMeta meta } class Order { +string id +string status +Money total +datetime createdAt } class Money { +string currency +decimal amount } class RequestMeta { +string requestId +datetime generatedAt } GetOrderResponse *-- Order Order *-- Money GetOrderResponse *-- RequestMeta

Quick relationship legend (use consistently)

  • A *-- B composition (“A contains B”)
  • A o-- B aggregation (“A has a B, but B may exist independently”)
  • A --> B association (“A references/uses B”)
  • A <|-- B inheritance (“B is a type of A”)

For API DTOs, composition (*--) is usually the clearest default.


2) Multiplicity: show “one vs many” explicitly

This is how you prevent contract misunderstandings.

Source

Rendered

classDiagram class Order { +string id +LineItem[] items } class LineItem { +string sku +int quantity +Money price } class Money { +string currency +decimal amount } Order "1" *-- "1..*" LineItem LineItem *-- Money

Tip: If it can be empty, say so (0..*). If it’s optional, show it (0..1).


3) Design an “envelope” that scales (meta + paging)

Many APIs return a consistent wrapper. Document it once, reuse it everywhere.

Source

Rendered

classDiagram class ApiResponse~T~ { +T data +RequestMeta meta +Error[] errors } class RequestMeta { +string requestId +datetime generatedAt +string version } class Error { +string code +string message +string traceId } ApiResponse~T~ *-- RequestMeta ApiResponse~T~ o-- Error

Pagination as a reusable contract

Source

Rendered

classDiagram class PageMeta { +int limit +string nextCursor +bool hasMore } class ListOrdersResponse { +Order[] orders +PageMeta page } class Order { +string id +string status +Money total } class Money { +string currency +decimal amount } ListOrdersResponse *-- PageMeta ListOrdersResponse "1" *-- "0..*" Order Order *-- Money


4) Errors that don’t surprise clients

In incidents, unclear error shapes cause secondary failures (bad retries, missing IDs, no correlation).

Document your error model explicitly.

Source

Rendered

classDiagram class Problem { +string type +string title +int status +string detail +string instance +string traceId } class ValidationProblem { +FieldError[] fieldErrors } class FieldError { +string field +string message } Problem <|-- ValidationProblem ValidationProblem *-- FieldError

If you include traceId / requestId in errors, clients can paste it into a ticket and you can find the exact failure fast.


5) Service interfaces: show who calls what (without drawing architecture)

Class diagrams can document service interfaces and dependencies without turning into a full architecture diagram.

A small “contract surface” for a service

Source

Rendered

classDiagram class OrdersService { +GetOrder(id: string) Order +ListOrders(customerId: string, limit: int, cursor: string) Order[] +CreateOrder(cmd: CreateOrderCommand) Order } class CreateOrderCommand { +string customerId +LineItem[] items +string idempotencyKey } class LineItem { +string sku +int quantity } OrdersService --> CreateOrderCommand CreateOrderCommand *-- LineItem

Note the idempotencyKey—it’s part of the contract. If it exists, document it.


6) Versioning strategies, shown clearly

Versioning becomes messy when “v1 vs v2” isn’t explicit. Class diagrams help you show compatibility.

Source

Rendered

classDiagram class OrderV1 { +string id +string status +decimal total } class OrderV2 { +string id +string status +Money total +datetime createdAt } class Money { +string currency +decimal amount } OrderV1 <|-- OrderV2 OrderV2 *-- Money

If a “v2” is not truly a subtype of “v1”, don’t force inheritance—use separate types and show mapping in a note or a short sequence diagram.


7) Styling: use classes to highlight contract vs internal details

When you’re documenting contracts, you often want to distinguish public API models from internal-only models.

Source

Rendered

classDiagram classDef public fill:#eef,stroke:#446,stroke-width:1px; classDef internal fill:#f5f5f5,stroke:#888,stroke-dasharray: 5 5; class PublicOrder { +string id +string status +Money total } class InternalOrderRow { -string pk -string sk -string payloadJson } class Money { +string currency +decimal amount } PublicOrder *-- Money PublicOrder --> InternalOrderRow : derived from class PublicOrder public class Money public class InternalOrderRow internal


8) A blog-ready checklist for contract diagrams

Before you publish:

  • Keep one diagram focused on a single endpoint or service surface.
  • Name types the way they appear in the API: CreateOrderCommand, Problem, PageMeta.
  • Show multiplicity (0..1, 1..*) anywhere it matters.
  • Prefer composition (*--) for “contains” relationships in payloads.
  • Include operationally useful fields like requestId / traceId / idempotencyKey.
  • If the diagram gets too big, split it into:
    • Public contract (DTOs + envelopes)
    • Internal model (storage rows, aggregates)

Where to go next

If class diagrams are working for you, the next Mermaid diagram type to learn for contracts is ER diagrams—they’re excellent for showing persistence models and relationships without mixing them into your API DTOs.

Have a project or a problem?

Talk with a senior engineer for practical recommendations—no obligation.

Leave a comment

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Posts

Categories

Get a free consultation from Reliable Penguin

Submit the form—or for immediate service call 866-649-7984.