Schema-First: JSON Schemas for Microservice Data Contracts

Published on June 11, 2025by Claudio Teixeira

Learn how to use JSON Schema and a central registry to manage data contracts in a microservices architecture.

The Silent Killer of Microservice Architectures

In a microservice ecosystem, services communicate constantly, often through message queues like AWS SQS. But how do you ensure the data sent by one service can be understood by another? This is the challenge of data contracts. Get it wrong, and you face a cascade of cryptic errors, broken deployments, and inter-team friction.

This post explores the journey from a fragile, code-first approach to a robust, schema-first strategy for managing data contracts, ensuring your services communicate reliably and can evolve independently.

C4 Agent JSON Validation

The Common Starting Point: The Code-First Fallacy

When a new feature requires adding a field to a message, the simplest solution often feels like the best one. A developer on a producer team might update a language-specific interface and share it.

For example, a TypeScript interface for an SQS event might look like this:

// Shared in a common library or copied between projects
interface FullSQSEvent {
appointmentId: string;
appointmentStatus: string;
sipSupplierName?: string; // The newly added field
}

This seems fine at first, but it's a trap. You've just created a "code-first" contract, where the code is the contract.

The Bottleneck: Why Code-First Fails at Scale

This approach quickly becomes a major source of instability:

No Single Source of Truth: The "truth" is scattered across every service that copies the interface. Updating it everywhere is manual and error-prone. Language-Specific: A TypeScript interface is useless to a consumer service written in Python, Go, or C#. No Automated Validation: There's no guarantee that the data sent actually conforms to the interface. A bug in the producer can send malformed data, crashing the consumer. Versioning Chaos: How do you manage changes? Sending files over Slack or updating a shared library leads to version conflicts and confusion. To escape this, we need a language-agnostic blueprint: a formal schema.

The Industry Standard: Schema-First with a Central Registry

The schema-first approach treats the data structure as a formal contract, defined in a language-agnostic format before any code is written. For JSON, the industry standard is JSON Schema.

This model introduces a central, authoritative source for all data contracts, enabling automation, validation, and true service decoupling.

Schema-First Architecture Diagram

Schema-First Architecture Diagram

Implementing the Schema-First Workflow

Here’s how to put this architecture into practice.

1. The Contract: The JSON Schema File

Instead of a TypeScript interface, we define our FullSQSEvent in a FullSQSEvent.v1.schema.json file. This is our blueprint.

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "FullSQSEvent",
"description": "Defines the event structure for an appointment update.",
"type": "object",
"properties": {
"appointmentId": { "type": "string" },
"appointmentStatus": { "type": "string" },
"sipSupplierName": { "type": "string" }
// ... all other fields defined here
},
"required": [
"appointmentId",
"appointmentStatus"
// ... all non-optional fields
]
}

2. The Registry: Your Source of Truth

The schema file needs a home.

Pragmatic Start (Git): The easiest way to begin is to create a dedicated Git repository (e.g., company-schemas). All teams have access, and changes are managed through pull requests. This provides versioning, reviews, and a clear history. Enterprise Grade (Schema Registry Service): For larger organizations, tools like AWS Glue Schema Registry or Confluent Schema Registry offer advanced features like automated compatibility checking to prevent breaking changes.

3. The Consumer: Automated Validation and Code Generation

The consumer service now has an automated, reliable workflow.

A. Generate Types from the Schema: Tools like json-schema-to-typescript can read the schema and automatically generate the TypeScript interfaces. This eliminates manual updates.


This command reads the schema and creates a perfect TypeScript interface
npx json2ts --input ./schemas/FullSQSEvent.v1.schema.json --output ./src/types.ts

B. Validate Messages at Runtime: Before processing any message from SQS, the consumer uses a library like ajv to validate the incoming data against the schema.

import Ajv from "ajv";
import schema from "./schemas/FullSQSEvent.v1.schema.json";

const ajv = new Ajv();
const validate = ajv.compile(schema);

function handleSqsMessage(messageBody: string) {
const data = JSON.parse(messageBody);

if (!validate(data)) {
// Malformed data! Reject it.
console.error("Invalid message received:", validate.errors);
// Move to a Dead-Letter Queue (DLQ)
return;
}

// At this point, data is guaranteed to match the contract.
processValidMessage(data);
}

Benefits of Schema-First Contracts

Rock-Solid Reliability: Automated validation prevents malformed data from crashing your services. True Decoupling: Services can evolve independently. As long as the contract is respected, teams don't need to coordinate every little change. Language Agnostic: The same schema can be used to generate classes in Python, C#, Java, and types in TypeScript. Documentation as a By-product: The schema is the ultimate, always-up-to-date documentation for your data structures. Clear Change Management: Using a Git repository for schemas means all changes are reviewed and approved via pull requests.

Next Steps & Considerations

Tooling: Integrate tools like ajv and json-schema-to-typescript into your development lifecycle. CI/CD Pipelines: Add a step to your CI pipeline that fetches the latest schemas and generates types, ensuring your services are never out of sync. Schema Evolution: Establish rules for evolving schemas. Adding new, optional fields is safe. Changing a type or making an optional field required is a breaking change that needs careful management. By treating your data contracts as first-class citizens with a schema-first approach, you build a more resilient, scalable, and collaborative microservice architecture.

Resources

https://www.jsonschemavalidator.net/