jsontoschema
All posts
schemaapijsonmigration

Diffing API Versions with Schemas

API versioning announcements tell you what changed. Migration guides list the new fields. But neither shows you the full structural difference between v1 and v2 in a format you can reason about quickly. Raw JSON diffs are noisy because every value difference shows up alongside the structural ones. Schema diffs isolate the changes that actually matter to your code.

Why raw diffs fail for structural comparison

Suppose you are migrating from /v1/orders to /v2/orders. You save a response from each version and run diff. The output is a wall of changes: different order IDs, different timestamps, different customer names. Buried somewhere in that wall is the fact that shipping_address changed from a flat string to a nested object, and discount went from number to number | null.

Those two structural changes are what will break your code. The value differences are irrelevant. But in a raw diff, they are indistinguishable.

Schema diffs show only shape changes

Generate a schema from each version and diff those instead.

# Save schemas for each version
curl -s https://api.example.com/v1/orders/42 \
  | npx @maisondigital/jsontoschema > v1-schema.txt

curl -s https://api.example.com/v2/orders/42 \
  | npx @maisondigital/jsontoschema > v2-schema.txt

# Diff the schemas
diff --unified v1-schema.txt v2-schema.txt

The output contains only structural differences:

 {
   id: number,
   status: string,
-  shipping_address: string,
+  shipping_address: {
+    line1: string,
+    line2?: string,
+    city: string,
+    postal_code: string,
+    country: string
+  },
-  discount: number,
+  discount: number | null,
   items: [
     {
       sku: string,
       quantity: number,
-      price: number
+      price: number,
+      currency?: string
     }
   ]
 }

Three changes are immediately visible. shipping_address expanded from a string to an object. discount became nullable. Each order item gained an optional currency field. No noise from differing values.

Handling multiple sample responses

A single response might not capture every optional field. If your API returns different shapes depending on the data (admin vs regular user, domestic vs international order), generate schemas from several responses and merge them before diffing.

# Merge multiple v2 responses into one schema
cat v2-response-*.json \
  | jq -s '.' \
  | npx @maisondigital/jsontoschema > v2-schema.txt

Wrapping multiple responses in an array with jq -s '.' lets the schema generator merge their shapes. Fields that appear in some responses but not others get marked optional, giving you the complete picture.

When to use this

Schema diffing is most useful during planned migrations: moving between API versions, swapping a third-party provider, or upgrading a dependency that changes its output format. It answers a specific question. What changed in the shape of this data, and what do I need to update in my code?

For automated, ongoing detection of shape changes, schema snapshot testing in CI is a better fit. For one-time investigation during a migration, a quick schema diff in the terminal gets you answers in seconds.

Generate schemas from your API responses and diff them with the converter.