<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged json-schema</title>
<link>https://coderlegion.com/tag/json-schema</link>
<description>Powered by Question2Answer</description>
<item>
<title>🧩 **Engineering a Machine-Validatable Contributor Evidence Workflow**</title>
<link>https://coderlegion.com/30305/engineering-a-machine-validatable-contributor-evidence-workflow</link>
<description>&lt;p&gt;🧩 &lt;strong&gt;Engineering a Machine-Validatable Contributor Evidence Workflow&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I just merged a new infrastructure milestone into MyZubster:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PR #1572 — standardized contributor interoperability checkpoints&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The problem I wanted to solve was not just “how do we document a successful contributor test?”&lt;/p&gt;
&lt;p&gt;The real problem was:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How do we make technical evidence portable across contributors, technologies and verification paths without losing provenance or inflating claims?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Until now, MyZubster already had reproducible contributor checkpoints, but the structure was mostly expressed through Markdown, PR discussions and manually reviewed evidence.&lt;/p&gt;
&lt;p&gt;That works at small scale.&lt;/p&gt;
&lt;p&gt;It does not scale well when multiple contributors are producing different kinds of evidence.&lt;/p&gt;
&lt;p&gt;So I introduced a common machine-readable format:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;myzubster.interoperability-checkpoint.v1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Each checkpoint now describes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;contributor identity&lt;/li&gt;
&lt;li&gt;repository / PR&lt;/li&gt;
&lt;li&gt;immutable commit reference&lt;/li&gt;
&lt;li&gt;technical bridge type&lt;/li&gt;
&lt;li&gt;exact scope&lt;/li&gt;
&lt;li&gt;test environment&lt;/li&gt;
&lt;li&gt;reproduction procedure&lt;/li&gt;
&lt;li&gt;observed output&lt;/li&gt;
&lt;li&gt;evidence state&lt;/li&gt;
&lt;li&gt;limitations&lt;/li&gt;
&lt;li&gt;canonical provenance&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;why-the-limitations-field-matters&quot;&gt;Why the limitations field matters&lt;/h4&gt;
&lt;p&gt;One of the most important design decisions was making limitations mandatory.&lt;/p&gt;
&lt;p&gt;A technical checkpoint should not silently become a broader claim.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;p&gt;A successful broker-to-agent test does not automatically mean:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;full decentralization&lt;/li&gt;
&lt;li&gt;direct P2P networking&lt;/li&gt;
&lt;li&gt;security certification&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A research Knowledge Card marked &lt;code&gt;SUPPORTED&lt;/code&gt; should not become “scientifically validated.”&lt;/p&gt;
&lt;p&gt;A signed webhook regression should not become “payment settlement verified.”&lt;/p&gt;
&lt;p&gt;The schema is intentionally designed to preserve those boundaries.&lt;/p&gt;
&lt;h4 id=&quot;evidence-state-is-constrained-by-observation&quot;&gt;Evidence state is constrained by observation&lt;/h4&gt;
&lt;p&gt;The validator enforces an important rule:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;TESTED&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;requires:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;observation.result = PASS&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;That sounds obvious, but encoding it into the schema means it is no longer just a documentation convention.&lt;/p&gt;
&lt;p&gt;It becomes something CI can reject.&lt;/p&gt;
&lt;h4 id=&quot;ajv-ci-integration&quot;&gt;AJV + CI integration&lt;/h4&gt;
&lt;p&gt;I added an AJV-based validator and connected it to the existing &lt;strong&gt;Continuous Evidence Gate&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;So the pipeline now looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Contributor work
        ↓
Scoped reproducible test
        ↓
Machine-readable checkpoint
        ↓
JSON Schema validation
        ↓
Continuous Evidence Gate
        ↓
Human review
        ↓
Canonical merge
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important distinction:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;automation validates evidence structure; it does not replace engineering judgment.&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id=&quot;testing-the-format-across-different-contribution-types&quot;&gt;Testing the format across different contribution types&lt;/h4&gt;
&lt;p&gt;I did not want the first schema version to be designed around one specific contributor.&lt;/p&gt;
&lt;p&gt;So the initial dataset already includes multiple technical paths.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;N4K48 / Nicola&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Runtime interoperability:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;MyZubster VPS
→ authenticated HTTPS broker
→ contributor-controlled agent
→ local catalog
→ result returned to Bridge
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;wasim-builds&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Security regression:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;unconfigured auth → rejected
missing credential → rejected
wrong credential → rejected
correct credential → accepted
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;khongten124 / Open Period Care&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Semantic evidence bridge:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;public research evidence
→ structured ingestion
→ contributor-scoped retrieval
→ Knowledge Cards returned
→ original SUPPORTED state preserved
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Aming9303&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Signed webhook regression:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;HMAC signing
→ stable delivery identity
→ unsigned request rejection
→ replay protection
→ stale-event rejection
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These are very different systems.&lt;/p&gt;
&lt;p&gt;That is exactly why using the same evidence structure is useful.&lt;/p&gt;
&lt;h4 id=&quot;current-maturity&quot;&gt;Current maturity&lt;/h4&gt;
&lt;p&gt;I would describe the workflow as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;repeatable
→ auditable
→ multi-contributor
→ machine-validatable
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next layer will be:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;automated ingestion
→ Contributor Passport linkage
→ Knowledge Graph integration
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;while preserving the original evidence state and provenance.&lt;/p&gt;
&lt;h4 id=&quot;merge-result&quot;&gt;Merge result&lt;/h4&gt;
&lt;p&gt;PR #1572 passed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CI – Test &amp;amp; Lint&lt;/li&gt;
&lt;li&gt;Security Audit&lt;/li&gt;
&lt;li&gt;Continuous Evidence Gate&lt;/li&gt;
&lt;li&gt;policy checks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Canonical merge:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;2be06b7e34f22744ecf81a9daef12f7ba11864be&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;What I like about this milestone is that it is not visually impressive.&lt;/p&gt;
&lt;p&gt;There is no shiny UI involved.&lt;/p&gt;
&lt;p&gt;But it improves something much more fundamental:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;the trust model between contributors and the platform.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Instead of:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;“This was tested.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;the system can increasingly answer:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Who tested it?&lt;br&gt;
Against which commit?&lt;br&gt;
In which environment?&lt;br&gt;
Using which procedure?&lt;br&gt;
What was observed?&lt;br&gt;
What does the evidence not prove?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That is the direction I want to keep pushing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Evidence should be inspectable data, not just prose.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;coderlegion-softwareengineering-jsonschema-ajv-ci-devops-systemdesign-opensource-reproducibility-testing-developertools&quot;&gt;CoderLegion #SoftwareEngineering #JSONSchema #AJV #CI #DevOps #SystemDesign #OpenSource #Reproducibility #Testing #DeveloperTools&lt;/h2&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30305/engineering-a-machine-validatable-contributor-evidence-workflow</guid>
<pubDate>Thu, 08 Oct 2026 13:50:59 +0000</pubDate>
</item>
<item>
<title>Catching AI Agent &quot;Schema Drift&quot; before production breaks 🛡️</title>
<link>https://coderlegion.com/26228/catching-ai-agent-schema-drift-before-production-breaks</link>
<description>&lt;p&gt;Got challenged by a senior SRE recently: &quot;Generating MCP configs isn't enough. You need to validate the runtime surface to prove reliability.&quot;&lt;br&gt;
&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=10803388401555381062&quot; alt=&quot;&quot;&gt;&lt;br&gt;
They were 100% right. Silent API changes (Schema Drift) are the #1 reason autonomous AI agents break in production.&lt;/p&gt;
&lt;p&gt;So we built the SEOSiri Drift Detector.&lt;/p&gt;
&lt;p&gt;Now, when you test an endpoint on our portal, it doesn't just ping the server. It executes a live JSON-RPC 2.0 handshake at the Cloudflare Edge and generates a timestamped, cryptographically tagged JSON Drift Receipt.&lt;/p&gt;
&lt;p&gt;It captures:&lt;br&gt;
✅ The exact edge gateway target&lt;br&gt;
✅ The tools/list request snapshot&lt;br&gt;
✅ The bounded response schema&lt;br&gt;
✅ A cryptographic DRIFT_PROOF signature&lt;/p&gt;
&lt;p&gt;You can download this receipt and plug it directly into your CI/CD pipelines to run automated diffs. If an upstream schema drifts, your pipeline catches it instantly. No manual monitoring required.&lt;/p&gt;
&lt;p&gt;Check out the live sandbox and export a receipt yourself:&lt;br&gt;
🔗 &lt;a rel=&quot;nofollow&quot; href=&quot;https://developers.seosiri.com&quot;&gt;SEOSiri MCP Portal&lt;/a&gt; &lt;/p&gt;
&lt;h2 id=&quot;modelcontextprotocol-devops-sre-aiagents-cloudflareworkers-seosiri&quot;&gt;ModelContextProtocol #DevOps #SRE #AIAgents #CloudflareWorkers #SEOSiri&lt;/h2&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26228/catching-ai-agent-schema-drift-before-production-breaks</guid>
<pubDate>Sat, 05 Sep 2026 01:13:31 +0000</pubDate>
</item>
<item>
<title>JsonToolBox</title>
<link>https://coderlegion.com/26226/jsontoolbox</link>
<description>&lt;p&gt;JsonToolBox is a free, open-source JSON toolkit built for developers, testers, technical writers, and anyone who works with structured data.&lt;/p&gt;
&lt;p&gt;It provides browser-based tools for common JSON tasks, including formatting and beautifying JSON, validating syntax, minifying output, viewing JSON in a structured tree, comparing two JSON documents, and converting JSON to other commonly used formats.&lt;/p&gt;
&lt;p&gt;JsonToolBox is designed for fast, focused use. You can open a tool, paste your data, perform the required operation, and copy or download the result without creating an account. JSON processing is performed locally in your browser, so your JSON content is not uploaded to a remote server for processing.&lt;/p&gt;
&lt;p&gt;Typical use cases include inspecting API responses, debugging configuration files, checking JSON before deployment, preparing examples for documentation, comparing payloads, and converting structured data for development workflows.&lt;/p&gt;
&lt;p&gt;The project is open source and available under the MIT License. The complete source code and development information are available on GitHub.&lt;/p&gt;
&lt;p&gt;Website: &lt;a rel=&quot;nofollow&quot; href=&quot;https://jsontoolbox.cc/&quot;&gt;https://jsontoolbox.cc&lt;/a&gt;&lt;br&gt;
Source code: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/kbmjj123/jsontoolbox.cc&quot;&gt;https://github.com/kbmjj123/jsontoolbox.cc&lt;/a&gt;&lt;/p&gt;
</description>
<category>Launches</category>
<guid isPermaLink="true">https://coderlegion.com/26226/jsontoolbox</guid>
<pubDate>Fri, 04 Sep 2026 22:58:29 +0000</pubDate>
</item>
<item>
<title>JSON Schema in the Wild: Real World Applications &amp; HAL</title>
<link>https://coderlegion.com/6206/json-schema-in-the-wild-real-world-applications-hal</link>
<description>&lt;p&gt;&lt;em&gt;Part 3 of 3: Where Theory Meets Production&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Welcome to the final part of our JSON Schema journey! We've covered the theory and implementation – now let's see how JSON Schema solves real problems in production environments. Plus, we'll dive into HAL (Hypermedia Application Language) and how it plays beautifully with JSON Schema.&lt;/p&gt;
&lt;h3 id=&quot;1-api-validation-middleware-your-apis-bodyguard-&quot;&gt;1. API Validation Middleware (Your APIs' Bodyguard)&lt;/h3&gt;
&lt;p&gt;Let's build bulletproof API validation that catches bad data before it causes havoc:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import express from 'express';
import Ajv from 'ajv';
import addFormats from 'ajv-formats';

const ajv = new Ajv({ allErrors: true });
addFormats(ajv);

// Generic validation middleware
function validateRequest(schema: any, target: 'body' | 'query' | 'params' = 'body') {
  const validate = ajv.compile(schema);
  
  return (req: any, res: any, next: any) =&amp;gt; {
    const dataToValidate = req[target];
    
    if (!validate(dataToValidate)) {
      return res.status(400).json({
        error: 'Validation failed',
        details: validate.errors?.map(err =&amp;gt; ({
          field: err.instancePath || err.params?.missingProperty,
          message: err.message,
          value: err.data
        }))
      });
    }
    
    // Store validated data (with defaults applied!)
    req.validatedData = dataToValidate;
    next();
  };
}

// Define your schemas
const createUserSchema = {
  type: &quot;object&quot;,
  properties: {
    name: { type: &quot;string&quot;, minLength: 1, maxLength: 100 },
    email: { type: &quot;string&quot;, format: &quot;email&quot; },
    age: { type: &quot;number&quot;, minimum: 13, maximum: 120 },
    role: { enum: [&quot;user&quot;, &quot;admin&quot;], default: &quot;user&quot; }
  },
  required: [&quot;name&quot;, &quot;email&quot;, &quot;age&quot;],
  additionalProperties: false
};

const updateUserSchema = {
  type: &quot;object&quot;,
  properties: {
    name: { type: &quot;string&quot;, minLength: 1, maxLength: 100 },
    email: { type: &quot;string&quot;, format: &quot;email&quot; },
    age: { type: &quot;number&quot;, minimum: 13, maximum: 120 }
  },
  minProperties: 1, // At least one field must be provided
  additionalProperties: false
};

// Use in your routes
const app = express();
app.use(express.json());

app.post('/users', 
  validateRequest(createUserSchema, 'body'),
  (req, res) =&amp;gt; {
    // req.validatedData contains clean, validated data
    const user = createUser(req.validatedData);
    res.json(user);
  }
);

app.patch('/users/:id', 
  validateRequest(updateUserSchema, 'body'),
  (req, res) =&amp;gt; {
    const userId = req.params.id;
    const updates = req.validatedData;
    const user = updateUser(userId, updates);
    res.json(user);
  }
);

// Query parameter validation too!
const searchSchema = {
  type: &quot;object&quot;,
  properties: {
    q: { type: &quot;string&quot;, minLength: 1 },
    page: { type: &quot;string&quot;, pattern: &quot;^[1-9]\\d*$&quot;, default: &quot;1&quot; },
    limit: { type: &quot;string&quot;, pattern: &quot;^(10|25|50|100)$&quot;, default: &quot;25&quot; }
  },
  required: [&quot;q&quot;],
  additionalProperties: false
};

app.get('/search', 
  validateRequest(searchSchema, 'query'),
  (req, res) =&amp;gt; {
    const { q, page, limit } = req.validatedData;
    const results = searchUsers(q, parseInt(page), parseInt(limit));
    res.json(results);
  }
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;2-data-pipeline-validation-etl-with-confidence-&quot;&gt;2. Data Pipeline Validation (ETL with Confidence)&lt;/h3&gt;
&lt;p&gt;When processing large datasets, catching bad data early saves hours of debugging:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;// data-pipeline.ts
import Ajv from 'ajv';
import addFormats from 'ajv-formats';

const ajv = new Ajv({ allErrors: true });
addFormats(ajv);

// Schema for incoming data records
const dataRecordSchema = {
  type: &quot;object&quot;,
  properties: {
    id: { 
      type: &quot;string&quot;, 
      pattern: &quot;^[A-Z]{2}[0-9]{6}$&quot; // e.g., &quot;AB123456&quot;
    },
    timestamp: { 
      type: &quot;string&quot;, 
      format: &quot;date-time&quot; 
    },
    value: { 
      type: &quot;number&quot;,
      minimum: 0
    },
    category: { 
      enum: [&quot;sales&quot;, &quot;marketing&quot;, &quot;support&quot;, &quot;development&quot;] 
    },
    metadata: {
      type: &quot;object&quot;,
      properties: {
        source: { type: &quot;string&quot; },
        confidence: { type: &quot;number&quot;, minimum: 0, maximum: 1 }
      },
      required: [&quot;source&quot;],
      additionalProperties: true // Allow extra metadata
    }
  },
  required: [&quot;id&quot;, &quot;timestamp&quot;, &quot;value&quot;, &quot;category&quot;],
  additionalProperties: false
};

const validateRecord = ajv.compile(dataRecordSchema);

interface ProcessingResult {
  validRecords: any[];
  invalidRecords: Array&amp;lt;{
    record: any;
    errors: string[];
  }&amp;gt;;
  stats: {
    total: number;
    valid: number;
    invalid: number;
    validationRate: number;
  };
}

export function processDataBatch(records: any[]): ProcessingResult {
  const validRecords: any[] = [];
  const invalidRecords: any[] = [];
  
  records.forEach((record, index) =&amp;gt; {
    if (validateRecord(record)) {
      validRecords.push(record);
    } else {
      invalidRecords.push({
        record,
        errors: validateRecord.errors?.map(err =&amp;gt; 
          `${err.instancePath || 'root'}: ${err.message}`
        ) || []
      });
    }
  });
  
  const total = records.length;
  const valid = validRecords.length;
  const invalid = invalidRecords.length;
  
  return {
    validRecords,
    invalidRecords,
    stats: {
      total,
      valid,
      invalid,
      validationRate: (valid / total) * 100
    }
  };
}

// Usage in your ETL process
async function runETL(inputFile: string) {
  const rawData = await loadDataFromFile(inputFile);
  const result = processDataBatch(rawData);
  
  console.log(`Processed ${result.stats.total} records:`);
  console.log(`✅ Valid: ${result.stats.valid} (${result.stats.validationRate.toFixed(1)}%)`);
  console.log(`❌ Invalid: ${result.stats.invalid}`);
  
  if (result.invalidRecords.length &amp;gt; 0) {
    console.log('\nInvalid records:');
    result.invalidRecords.slice(0, 5).forEach((invalid, i) =&amp;gt; {
      console.log(`Record ${i + 1}:`, invalid.errors.join(', '));
    });
  }
  
  // Process only valid records
  await saveToDatabase(result.validRecords);
  
  // Log invalid records for investigation
  if (result.invalidRecords.length &amp;gt; 0) {
    await saveInvalidRecords(result.invalidRecords);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;3-hal-hypermedia-application-language-schemas&quot;&gt;3. HAL (Hypermedia Application Language) Schemas&lt;/h3&gt;
&lt;p&gt;HAL makes your APIs self-describing with built-in navigation. Think JSON with links that tell clients what actions they can take next.&lt;/p&gt;
&lt;h4 id=&quot;basic-hal-structure-amp-schema&quot;&gt;Basic HAL Structure &amp;amp; Schema&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;// HAL link schema
const halLinkSchema = {
  type: &quot;object&quot;,
  properties: {
    href: { type: &quot;string&quot;, format: &quot;uri-reference&quot; },
    templated: { type: &quot;boolean&quot; },
    type: { type: &quot;string&quot; }
  },
  required: [&quot;href&quot;],
  additionalProperties: false
};

// Complete HAL resource schema
const halUserSchema = {
  type: &quot;object&quot;,
  properties: {
    // Resource data
    id: { type: &quot;number&quot; },
    name: { type: &quot;string&quot; },
    email: { type: &quot;string&quot;, format: &quot;email&quot; },
    
    // HAL links (required)
    _links: {
      type: &quot;object&quot;,
      properties: {
        self: halLinkSchema,
        edit: halLinkSchema,
        delete: halLinkSchema
      },
      required: [&quot;self&quot;],
      additionalProperties: halLinkSchema
    },
    
    // HAL embedded resources (optional)
    _embedded: {
      type: &quot;object&quot;,
      properties: {
        posts: {
          type: &quot;array&quot;,
          items: {
            type: &quot;object&quot;,
            properties: {
              id: { type: &quot;number&quot; },
              title: { type: &quot;string&quot; },
              _links: {
                type: &quot;object&quot;,
                properties: { self: halLinkSchema },
                required: [&quot;self&quot;]
              }
            },
            required: [&quot;id&quot;, &quot;title&quot;, &quot;_links&quot;]
          }
        }
      }
    }
  },
  required: [&quot;id&quot;, &quot;name&quot;, &quot;_links&quot;],
  additionalProperties: false
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;hal-response-builder&quot;&gt;HAL Response Builder&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import Ajv from 'ajv';
const ajv = new Ajv();
const validateHalUser = ajv.compile(halUserSchema);

class HalBuilder {
  static user(user: any, baseUrl: string) {
    const halUser = {
      ...user,
      _links: {
        self: { href: `${baseUrl}/users/${user.id}` },
        edit: { href: `${baseUrl}/users/${user.id}` },
        posts: { href: `${baseUrl}/users/${user.id}/posts` }
      }
    };
    
    // Add embedded posts if available
    if (user.posts?.length) {
      halUser._embedded = {
        posts: user.posts.map(post =&amp;gt; ({
          ...post,
          _links: { self: { href: `${baseUrl}/posts/${post.id}` } }
        }))
      };
    }
    
    if (!validateHalUser(halUser)) {
      throw new Error('Invalid HAL resource');
    }
    
    return halUser;
  }
}

// Usage in Express
app.get('/users/:id', async (req, res) =&amp;gt; {
  const user = await getUserById(req.params.id);
  const baseUrl = `${req.protocol}://${req.get('host')}`;
  
  res.json(HalBuilder.user(user, baseUrl));
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;4-testing-your-schemas-because-bugs-hide-in-edge-cases-&quot;&gt;4. Testing Your Schemas (Because Bugs Hide in Edge Cases)&lt;/h3&gt;
&lt;p&gt;Don't just validate production data – test your schemas themselves:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;// schema-tests.ts
import { describe, it, expect } from 'vitest';
import Ajv from 'ajv';
import addFormats from 'ajv-formats';
import { halUserSchema, userRegistrationSchema } from './schemas';

const ajv = new Ajv({ allErrors: true });
addFormats(ajv);

describe('User Registration Schema', () =&amp;gt; {
  const validate = ajv.compile(userRegistrationSchema);
  
  it('should accept valid user data', () =&amp;gt; {
    const validUser = {
      username: &quot;johndoe123&quot;,
      email: &quot;*Emails are not allowed*&quot;,
      password: &quot;SecurePass123!&quot;,
      age: 25
    };
    
    expect(validate(validUser)).toBe(true);
  });
  
  it('should reject invalid email', () =&amp;gt; {
    const invalidUser = {
      username: &quot;johndoe123&quot;,
      email: &quot;not-an-email&quot;,
      password: &quot;SecurePass123!&quot;,
      age: 25
    };
    
    expect(validate(invalidUser)).toBe(false);
    expect(validate.errors).toContainEqual(
      expect.objectContaining({
        instancePath: '/email',
        message: 'must match format &quot;email&quot;'
      })
    );
  });
  
  it('should reject weak password', () =&amp;gt; {
    const invalidUser = {
      username: &quot;johndoe123&quot;,
      email: &quot;*Emails are not allowed*&quot;,
      password: &quot;weak&quot;,
      age: 25
    };
    
    expect(validate(invalidUser)).toBe(false);
  });
  
  it('should apply defaults', () =&amp;gt; {
    const userData = {
      username: &quot;johndoe123&quot;,
      email: &quot;*Emails are not allowed*&quot;,
      password: &quot;SecurePass123!&quot;,
      age: 25
    };
    
    validate(userData);
    expect(userData.preferences?.newsletter).toBe(false);
    expect(userData.preferences?.theme).toBe('light');
  });
});

describe('HAL User Schema', () =&amp;gt; {
  const validate = ajv.compile(halUserSchema);
  
  it('should accept valid HAL user', () =&amp;gt; {
    const halUser = {
      id: 123,
      name: &quot;Alice&quot;,
      email: &quot;*Emails are not allowed*&quot;,
      createdAt: &quot;2023-01-01T00:00:00.000Z&quot;,
      _links: {
        self: { href: &quot;/users/123&quot; },
        edit: { href: &quot;/users/123&quot; }
      }
    };
    
    expect(validate(halUser)).toBe(true);
  });
  
  it('should require _links.self', () =&amp;gt; {
    const invalidHalUser = {
      id: 123,
      name: &quot;Alice&quot;,
      email: &quot;*Emails are not allowed*&quot;,
      _links: {
        edit: { href: &quot;/users/123&quot; }
      }
    };
    
    expect(validate(invalidHalUser)).toBe(false);
    expect(validate.errors).toContainEqual(
      expect.objectContaining({
        instancePath: '/_links',
        message: &quot;must have required property 'self'&quot;
      })
    );
  });
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;5-pro-tips-for-production-success&quot;&gt;5. Pro Tips for Production Success&lt;/h3&gt;
&lt;h4 id=&quot;1-schema-versioning-strategy&quot;&gt;1. Schema Versioning Strategy&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;// Keep schemas versioned for backward compatibility
const schemas = {
  'v1': {
    user: userSchemaV1,
    post: postSchemaV1
  },
  'v2': {
    user: userSchemaV2,
    post: postSchemaV2
  }
};

function getValidator(resource: string, version: string = 'v2') {
  const schema = schemas[version]?.[resource];
  if (!schema) {
    throw new Error(`Schema not found: ${resource} v${version}`);
  }
  return ajv.compile(schema);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;2-performance-monitoring&quot;&gt;2. Performance Monitoring&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;// Monitor validation performance
function createTimedValidator(schema: any, name: string) {
  const validate = ajv.compile(schema);
  
  return (data: any) =&amp;gt; {
    const start = process.hrtime.bigint();
    const result = validate(data);
    const end = process.hrtime.bigint();
    
    const duration = Number(end - start) / 1000000; // Convert to milliseconds
    console.log(`Validation ${name}: ${duration.toFixed(2)}ms`);
    
    return result;
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;3-schema-documentation-generation&quot;&gt;3. Schema Documentation Generation&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;// Generate documentation from your schemas
function generateSchemaDoc(schema: any, title: string) {
  const doc = {
    title,
    properties: {},
    required: schema.required || []
  };
  
  for (const [prop, definition] of Object.entries(schema.properties || {})) {
    doc.properties[prop] = {
      type: definition.type,
      description: definition.description || `${prop} field`,
      required: schema.required?.includes(prop) || false,
      example: generateExample(definition)
    };
  }
  
  return doc;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;wrapping-up-the-series&quot;&gt;Wrapping Up the Series&lt;/h3&gt;
&lt;p&gt;Congratulations! You've journeyed from JSON Schema basics to production-ready implementations. Here's what you've gained:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Part 1&lt;/strong&gt;: Understanding JSON Schema fundamentals and YAML compatibility&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 2&lt;/strong&gt;: Mastering AJV implementation with advanced features and TypeScript integration&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 3&lt;/strong&gt;: Real-world patterns including API validation, ETL pipelines, and HAL hypermedia APIs&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;your-next-steps&quot;&gt;Your Next Steps&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Start Small&lt;/strong&gt;: Pick one use case (maybe API validation) and implement it&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build Your Schema Library&lt;/strong&gt;: Create reusable schemas for your common data structures&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test Everything&lt;/strong&gt;: Write tests for both your schemas and your validation logic&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitor Performance&lt;/strong&gt;: Keep an eye on validation performance in production&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Document Your Schemas&lt;/strong&gt;: Good schemas are self-documenting, but examples help&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;You're now equipped to bring order to the chaos of unvalidated data. Go forth and validate with confidence!&lt;/em&gt; :rocket:&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Series Navigation:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/5669/json-schema-your-datas-new-best-friend&quot;&gt;Part 1: Understanding the Basics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/5928/json-schema-with-ajv-implementation-deep-dive&quot;&gt;Part 2: Implementation with AJV&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 3: Real-World Applications &amp;amp; HAL&lt;/strong&gt; ← You are here&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/6206/json-schema-in-the-wild-real-world-applications-hal</guid>
<pubDate>Wed, 08 Oct 2025 13:05:06 +0000</pubDate>
</item>
<item>
<title>JSON Schema with AJV: Implementation Deep Dive ⚡</title>
<link>https://coderlegion.com/5928/json-schema-with-ajv-implementation-deep-dive</link>
<description>&lt;p&gt;&lt;em&gt;Part 2 of 3: Getting Your Hands Dirty&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Welcome back! In &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/5669/json-schema-your-datas-new-best-friend&quot;&gt;Part 1&lt;/a&gt;, we covered the basics of JSON Schema and why it's awesome. Now it's time to roll up our sleeves and implement some real validation with AJV (Another JSON Schema Validator) – the Swiss Army knife of JSON validation.&lt;/p&gt;
&lt;h3 id=&quot;why-ajv-rules-the-validation-world&quot;&gt;Why AJV Rules the Validation World&lt;/h3&gt;
&lt;p&gt;AJV isn't just another validation library – it's THE validation library. Here's why developers love it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Blazingly Fast&lt;/strong&gt;: Compiles schemas to optimized JavaScript functions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fully Compliant&lt;/strong&gt;: Supports JSON Schema draft-07 and draft 2019-09&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extensible&lt;/strong&gt;: Custom keywords, formats, and error messages&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TypeScript Ready&lt;/strong&gt;: Excellent TypeScript support out of the box&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;getting-started-installation-and-basic-setup&quot;&gt;Getting Started: Installation and Basic Setup&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// The essentials
npm install ajv

// Optional but recommended additions
npm install ajv-formats ajv-errors ajv-keywords
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import Ajv from 'ajv';
import addFormats from 'ajv-formats';

// Create your AJV instance
const ajv = new Ajv({ 
  allErrors: true,        // Collect all errors, not just the first
  removeAdditional: true, // Remove additional properties
  useDefaults: true       // Apply default values
});

// Add common formats (email, date, uri, etc.)
addFormats(ajv);

// Now you're ready to validate!
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;your-first-real-validation&quot;&gt;Your First Real Validation&lt;/h3&gt;
&lt;p&gt;Let's build something practical – a user registration validator:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;const userRegistrationSchema = {
  type: &quot;object&quot;,
  properties: {
    username: {
      type: &quot;string&quot;,
      minLength: 3,
      maxLength: 20,
      pattern: &quot;^[a-zA-Z0-9_]+$&quot;
    },
    email: {
      type: &quot;string&quot;,
      format: &quot;email&quot;
    },
    password: {
      type: &quot;string&quot;,
      minLength: 8,
      pattern: &quot;^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&amp;amp;])[A-Za-z\\d@$!%*?&amp;amp;]&quot;
    },
    age: {
      type: &quot;number&quot;,
      minimum: 13,
      maximum: 120
    },
    preferences: {
      type: &quot;object&quot;,
      properties: {
        newsletter: { type: &quot;boolean&quot;, default: false },
        theme: { enum: [&quot;light&quot;, &quot;dark&quot;], default: &quot;light&quot; }
      },
      additionalProperties: false
    }
  },
  required: [&quot;username&quot;, &quot;email&quot;, &quot;password&quot;, &quot;age&quot;],
  additionalProperties: false
};

// Compile the schema (do this once, reuse many times)
const validateUser = ajv.compile(userRegistrationSchema);

// Test it out
function registerUser(userData: any) {
  const valid = validateUser(userData);
  
  if (!valid) {
    console.log('Validation failed:');
    validateUser.errors?.forEach(error =&amp;gt; {
      console.log(`- ${error.instancePath}: ${error.message}`);
    });
    return null;
  }
  
  console.log('✅ User data is valid!');
  return userData; // Now with defaults applied!
}

// Try it
const newUser = {
  username: &quot;coolguy123&quot;,
  email: &quot;*Emails are not allowed*&quot;,
  password: &quot;SuperSecret123!&quot;,
  age: 25
};

registerUser(newUser);
// ✅ User data is valid!
// Note: preferences.newsletter and preferences.theme get default values
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;advanced-ajv-features-that-ll-blow-your-mind&quot;&gt;Advanced AJV Features That'll Blow Your Mind&lt;/h3&gt;
&lt;h4 id=&quot;1-custom-keywords&quot;&gt;1. Custom Keywords&lt;/h4&gt;
&lt;p&gt;Sometimes the built-in validation isn't enough. Let's create a custom keyword:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;// Add a custom &quot;isAdult&quot; keyword
ajv.addKeyword({
  keyword: 'isAdult',
  type: 'number',
  schemaType: 'boolean',
  compile(schemaVal) {
    return function validate(data) {
      if (schemaVal) {
        return data &amp;gt;= 18;
      }
      return true;
    }
  },
  errors: false,
  metaSchema: {
    type: &quot;boolean&quot;
  }
});

// Use it in a schema
const adultSchema = {
  type: &quot;object&quot;,
  properties: {
    age: { 
      type: &quot;number&quot;, 
      isAdult: true 
    }
  }
};

const validateAdult = ajv.compile(adultSchema);
console.log(validateAdult({ age: 17 })); // false
console.log(validateAdult({ age: 21 })); // true
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;2-conditional-validation&quot;&gt;2. Conditional Validation&lt;/h4&gt;
&lt;p&gt;Real-world data often has complex relationships. AJV handles this beautifully:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;const conditionalSchema = {
  type: &quot;object&quot;,
  properties: {
    userType: { enum: [&quot;admin&quot;, &quot;user&quot;, &quot;guest&quot;] },
    permissions: { 
      type: &quot;array&quot;,
      items: { type: &quot;string&quot; }
    },
    adminKey: { type: &quot;string&quot; }
  },
  required: [&quot;userType&quot;],
  
  // If user is admin, they need permissions and adminKey
  if: { 
    properties: { 
      userType: { const: &quot;admin&quot; } 
    } 
  },
  then: { 
    required: [&quot;permissions&quot;, &quot;adminKey&quot;],
    properties: {
      permissions: { minItems: 1 }
    }
  },
  
  // If user is guest, no additional requirements
  else: {
    if: { 
      properties: { 
        userType: { const: &quot;guest&quot; } 
      } 
    },
    then: {
      // Guests can't have permissions
      not: {
        required: [&quot;permissions&quot;]
      }
    }
  }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;3-schema-composition-with-ref&quot;&gt;3. Schema Composition with $ref&lt;/h4&gt;
&lt;p&gt;Keep your schemas DRY and maintainable:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;const schemas = {
  // Base address schema
  address: {
    $id: &quot;https://example.com/schemas/address.json&quot;,
    type: &quot;object&quot;,
    properties: {
      street: { type: &quot;string&quot; },
      city: { type: &quot;string&quot; },
      zipCode: { 
        type: &quot;string&quot;, 
        pattern: &quot;^[0-9]{5}(-[0-9]{4})?$&quot; 
      }
    },
    required: [&quot;street&quot;, &quot;city&quot;, &quot;zipCode&quot;]
  },
  
  // Person schema using address
  person: {
    $id: &quot;https://example.com/schemas/person.json&quot;,
    type: &quot;object&quot;,
    properties: {
      name: { type: &quot;string&quot; },
      homeAddress: { $ref: &quot;address.json&quot; },
      workAddress: { $ref: &quot;address.json&quot; }
    }
  }
};

// Add schemas to AJV
ajv.addSchema(schemas.address);
ajv.addSchema(schemas.person);

const validatePerson = ajv.getSchema(&quot;https://example.com/schemas/person.json&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;error-handling-like-a-pro&quot;&gt;Error Handling Like a Pro&lt;/h3&gt;
&lt;p&gt;Default AJV errors are... functional. But we can make them much better:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import addErrors from 'ajv-errors';
addErrors(ajv);

const betterUserSchema = {
  type: &quot;object&quot;,
  properties: {
    username: {
      type: &quot;string&quot;,
      minLength: 3,
      maxLength: 20,
      pattern: &quot;^[a-zA-Z0-9_]+$&quot;
    },
    password: {
      type: &quot;string&quot;,
      minLength: 8,
      pattern: &quot;^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)&quot;
    }
  },
  required: [&quot;username&quot;, &quot;password&quot;],
  
  // Custom error messages
  errorMessage: {
    properties: {
      username: &quot;Username must be 3-20 characters long and contain only letters, numbers, and underscores&quot;,
      password: &quot;Password must be at least 8 characters with uppercase, lowercase, and a number&quot;
    },
    required: {
      username: &quot;Username is required&quot;,
      password: &quot;Password is required&quot;
    }
  }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;performance-tips-that-actually-matter&quot;&gt;Performance Tips That Actually Matter&lt;/h3&gt;
&lt;h4 id=&quot;1-compile-once-use-many-times&quot;&gt;1. Compile Once, Use Many Times&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;// ❌ DON'T do this
function validateUserData(data: any) {
  const validate = ajv.compile(userSchema); // Compiling every time!
  return validate(data);
}

// ✅ DO this instead
const validateUserData = ajv.compile(userSchema); // Compile once

function checkUser(data: any) {
  return validateUserData(data); // Reuse compiled function
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;2-use-removeadditional-for-large-objects&quot;&gt;2. Use removeAdditional for Large Objects&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;const ajv = new Ajv({ 
  removeAdditional: &quot;all&quot; // Strip unknown properties automatically
});

// Input: { name: &quot;Bob&quot;, age: 25, secretHackerData: &quot;evil&quot; }
// After validation: { name: &quot;Bob&quot;, age: 25 }
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;3-optimize-for-your-use-case&quot;&gt;3. Optimize for Your Use Case&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;// For APIs - fast validation, detailed errors
const apiAjv = new Ajv({ 
  allErrors: true,
  verbose: true 
});

// For data processing - fastest validation
const processingAjv = new Ajv({ 
  allErrors: false,
  verbose: false,
  validateSchema: false
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;typescript-integration-the-cherry-on-top-&quot;&gt;TypeScript Integration (The Cherry on Top)&lt;/h3&gt;
&lt;p&gt;AJV plays beautifully with TypeScript:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import { JSONSchemaType } from 'ajv';

// Define your TypeScript interface
interface User {
  id: string;
  name: string;
  age: number;
  email?: string;
}

// Create a typed schema
const userSchema: JSONSchemaType&amp;lt;User&amp;gt; = {
  type: &quot;object&quot;,
  properties: {
    id: { type: &quot;string&quot; },
    name: { type: &quot;string&quot; },
    age: { type: &quot;number&quot; },
    email: { type: &quot;string&quot;, nullable: true }
  },
  required: [&quot;id&quot;, &quot;name&quot;, &quot;age&quot;],
  additionalProperties: false
};

// Compile with type safety
const validateUser = ajv.compile(userSchema);

// TypeScript knows the validated data structure!
function processUser(userData: unknown): User | null {
  if (validateUser(userData)) {
    // userData is now typed as User!
    return userData;
  }
  return null;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;common-gotchas-learn-from-my-mistakes-&quot;&gt;Common Gotchas (Learn From My Mistakes)&lt;/h3&gt;
&lt;h4 id=&quot;1-format-validation-needs-ajv-formats&quot;&gt;1. Format Validation Needs ajv-formats&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;// This won't work without ajv-formats
const schema = {
  type: &quot;string&quot;,
  format: &quot;email&quot; // Silently ignored!
};

// Fix it
import addFormats from 'ajv-formats';
addFormats(ajv);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;2-regexp-patterns-need-double-escaping&quot;&gt;2. RegExp Patterns Need Double Escaping&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;// ❌ Wrong
pattern: &quot;^\d{3}-\d{3}-\d{4}$&quot;

// ✅ Correct
pattern: &quot;^\\d{3}-\\d{3}-\\d{4}$&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;3-additionalproperties-vs-properties&quot;&gt;3. additionalProperties vs Properties&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;// This allows ANY additional properties
const schema1 = {
  type: &quot;object&quot;,
  properties: { name: { type: &quot;string&quot; } }
  // additionalProperties defaults to true
};

// This blocks additional properties
const schema2 = {
  type: &quot;object&quot;,
  properties: { name: { type: &quot;string&quot; } },
  additionalProperties: false
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;wrapping-up-part-2&quot;&gt;Wrapping Up Part 2&lt;/h3&gt;
&lt;p&gt;You now have the power to implement robust, fast, and maintainable validation in your applications! AJV transforms JSON Schema from a simple concept into a validation powerhouse.&lt;/p&gt;
&lt;h3 id=&quot;coming-up-in-part-3-&quot;&gt;Coming Up in Part 3...&lt;/h3&gt;
&lt;p&gt;In the final part of our series, we'll explore real-world applications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Building API validation middleware&lt;/li&gt;
&lt;li&gt;Data pipeline validation patterns&lt;/li&gt;
&lt;li&gt;HAL (Hypermedia) schemas for self-describing APIs&lt;/li&gt;
&lt;li&gt;Testing strategies for schema validation&lt;/li&gt;
&lt;li&gt;Production ready tips&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Ready to put this knowledge to work in production? Part 3 is where the magic happens!&lt;/em&gt; &lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Series Navigation:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/5669/json-schema-your-datas-new-best-friend&quot;&gt;Part 1: Understanding the Basics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 2: Implementation with AJV&lt;/strong&gt; ← You are here  &lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/6206/json-schema-in-the-wild-real-world-applications-hal&quot;&gt;Part 3: Real-World Applications &amp;amp; HAL&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/5928/json-schema-with-ajv-implementation-deep-dive</guid>
<pubDate>Fri, 03 Oct 2025 04:08:27 +0000</pubDate>
</item>
<item>
<title>The Complete JSON &amp; Data Toolkit</title>
<link>https://coderlegion.com/5799/the-complete-json-data-toolkit</link>
<description>&lt;p&gt;**&lt;/p&gt;
&lt;h3 id=&quot;formatj-son-online-a-powerful-json-toolkit-that-now-supports-files-up-to-100-mb&quot;&gt;FormatJ SON Online: A Powerful JSON Toolkit That now Supports Files Up to 100 MB&lt;/h3&gt;
&lt;p&gt;**&lt;br&gt;
Dealing with large JSON files can be a headache — many online tools crash or refuse files past a small size limit. That’s why I built FormatJSONOnline.com, a toolbox that not only formats, validates, and converts JSON, but now supports files up to 100 MB. Here’s what makes it stand out, and how you can get full value from it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Key Features &amp;amp; Highlights&lt;/strong&gt;&lt;br&gt;
✅ Format &amp;amp; Beautify JSON&lt;/p&gt;
&lt;p&gt;Clean up minified or messy JSON into a readable structure.&lt;/p&gt;
&lt;p&gt; Live Validation&lt;/p&gt;
&lt;p&gt;Catch syntax errors instantly — missing commas, mismatched braces, and more.&lt;/p&gt;
&lt;p&gt; Minify JSON&lt;/p&gt;
&lt;p&gt;Remove unnecessary whitespace to compress JSON for performance.&lt;/p&gt;
&lt;p&gt; Conversion Tools&lt;/p&gt;
&lt;p&gt;JSON → Excel / JSON → CSV / JSON → XML&lt;/p&gt;
&lt;p&gt;Excel → JSON &amp;amp; CSV → JSON&lt;/p&gt;
&lt;p&gt; AI JSON Generator + Mock API&lt;/p&gt;
&lt;p&gt;Generate JSON from plain English prompts, edit it, and publish it as a live mock REST endpoint.&lt;/p&gt;
&lt;p&gt; Support Up to 100 MB&lt;/p&gt;
&lt;p&gt;Unlike many tools with strict size limits, FormatJSONOnline handles files up to 100 MB, making it robust for real-world API responses and large datasets.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt; Additional Utilities&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;SQL Formatter&lt;/p&gt;
&lt;p&gt;JSON Compare / Schema Validator / Path Finder / Dummy JSON Generator&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How to Use It (Step-by-Step)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Upload or Paste JSON / Prompt Description – Either bring in your own JSON or describe what you want in plain English.&lt;/p&gt;
&lt;p&gt;Edit &amp;amp; Validate – Fix any errors, beautify, or minify as needed.&lt;/p&gt;
&lt;p&gt;Convert or Publish – Convert JSON to other formats or publish it as a mock API endpoint.&lt;/p&gt;
&lt;p&gt;Why Support for 100 MB Matters&lt;/p&gt;
&lt;p&gt;Many online JSON tools cap at a few MB, which means you have to split, reduce, or only format parts of large responses. Supporting 100 MB means:&lt;/p&gt;
&lt;p&gt;You can format full API responses in one go.&lt;/p&gt;
&lt;p&gt;No need to chunk your JSON manually.&lt;/p&gt;
&lt;p&gt;Better for real production debugging and logging tasks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Try It Out&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Give it a spin here: &lt;a rel=&quot;nofollow&quot; href=&quot;https://formatjsononline.com&quot;&gt;https://formatjsononline.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Use the conversion pages:&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;http://formatjsononline.com/json-to-excel&quot;&gt;JSON to Excel&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;http://formatjsononline.com/excel-to-json&quot;&gt;Excel to JSON&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://formatjsononline.com/json-to-csharp&quot;&gt;JSON to C#&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://formatjsononline.com/json-to-pdf&quot;&gt;JSON to PDF&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://formatjsononline.com/&quot;&gt;Format JSON Online&lt;/a&gt; aims to be the one–stop JSON toolkit for developers. With support up to 100 MB, AI JSON generation, mock API creation, and robust conversions, it eliminates the need to bounce between multiple small utilities.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/5799/the-complete-json-data-toolkit</guid>
<pubDate>Tue, 30 Sep 2025 06:59:51 +0000</pubDate>
</item>
<item>
<title>JSON Schema: Your Data's New Best Friend ️</title>
<link>https://coderlegion.com/5669/json-schema-your-datas-new-best-friend</link>
<description>&lt;p&gt;&lt;em&gt;Part 1 of 3: Understanding the Basics&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Ever had that moment where your API returns &lt;code&gt;{ age: &quot;twenty-five&quot; }&lt;/code&gt; instead of &lt;code&gt;{ age: 25 }&lt;/code&gt; and your whole app breaks? Yeah, we've all been there. Let's talk about JSON Schema – your new best friend in the fight against unpredictable data.&lt;/p&gt;
&lt;h3 id=&quot;what-the-heck-is-json-schema-anyway-&quot;&gt;What the Heck is JSON Schema Anyway?&lt;/h3&gt;
&lt;p&gt;JSON Schema is basically a contract for your JSON data. Think of it like TypeScript's type definitions, but for runtime validation. It tells your data: &quot;Hey, you better look exactly like this, or we're gonna have problems.&quot;&lt;/p&gt;
&lt;p&gt;The concept emerged around 2009-2010 when developers got tired of writing custom validation logic for every single API endpoint. Someone smart said, &quot;What if we could describe our data structure using... more JSON?&quot; And boom – JSON Schema was born.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// Instead of this mess:
if (!data.name || typeof data.name !== 'string') {
  throw new Error('Name is required and must be a string');
}
if (!data.age || typeof data.age !== 'number' || data.age &amp;lt; 0) {
  throw new Error('Age must be a positive number');
}
// ... 50 more lines of validation

// You get this beauty:
const schema = {
  type: &quot;object&quot;,
  properties: {
    name: { type: &quot;string&quot; },
    age: { type: &quot;number&quot;, minimum: 0 }
  },
  required: [&quot;name&quot;, &quot;age&quot;]
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;the-anatomy-of-a-json-schema&quot;&gt;The Anatomy of a JSON Schema&lt;/h3&gt;
&lt;p&gt;Let's break down what makes a schema tick:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const userSchema = {
  // Root type - what kind of data are we expecting?
  type: &quot;object&quot;,
  
  // Define the shape of our object
  properties: {
    name: { 
      type: &quot;string&quot;, 
      minLength: 1,
      maxLength: 100 
    },
    age: { 
      type: &quot;number&quot;, 
      minimum: 0, 
      maximum: 150 
    },
    email: { 
      type: &quot;string&quot;, 
      format: &quot;email&quot;  // Built-in format validation!
    },
    hobbies: {
      type: &quot;array&quot;,
      items: { type: &quot;string&quot; },
      maxItems: 10,
      uniqueItems: true
    },
    isActive: { 
      type: &quot;boolean&quot; 
    }
  },
  
  // Which fields are mandatory?
  required: [&quot;name&quot;, &quot;age&quot;],
  
  // Should we allow extra properties?
  additionalProperties: false
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;common-schema-patterns-you-ll-love&quot;&gt;Common Schema Patterns You'll Love&lt;/h3&gt;
&lt;h4 id=&quot;1-enums-because-options-are-limited-&quot;&gt;1. Enums (Because Options Are Limited)&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const statusSchema = {
  type: &quot;object&quot;,
  properties: {
    status: { 
      enum: [&quot;active&quot;, &quot;inactive&quot;, &quot;pending&quot;, &quot;banned&quot;] 
    },
    priority: {
      type: &quot;string&quot;,
      enum: [&quot;low&quot;, &quot;medium&quot;, &quot;high&quot;, &quot;urgent&quot;]
    }
  }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;2-nested-objects-objects-all-the-way-down-&quot;&gt;2. Nested Objects (Objects All the Way Down)&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const addressSchema = {
  type: &quot;object&quot;,
  properties: {
    street: { type: &quot;string&quot; },
    city: { type: &quot;string&quot; },
    country: { type: &quot;string&quot; },
    zipCode: { 
      type: &quot;string&quot;, 
      pattern: &quot;^[1-9][0-9]{5}$&quot; // India ZIP code pattern
    }
  },
  required: [&quot;street&quot;, &quot;city&quot;, &quot;country&quot;]
};

const personSchema = {
  type: &quot;object&quot;,
  properties: {
    name: { type: &quot;string&quot; },
    homeAddress: addressSchema,  // Nested schema
    workAddress: addressSchema   // Reused!
  }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;3-arrays-with-specific-item-types&quot;&gt;3. Arrays with Specific Item Types&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const orderSchema = {
  type: &quot;object&quot;,
  properties: {
    orderId: { type: &quot;string&quot; },
    items: {
      type: &quot;array&quot;,
      minItems: 1,
      items: {
        type: &quot;object&quot;,
        properties: {
          productId: { type: &quot;string&quot; },
          quantity: { type: &quot;number&quot;, minimum: 1 },
          price: { type: &quot;number&quot;, minimum: 0 }
        },
        required: [&quot;productId&quot;, &quot;quantity&quot;, &quot;price&quot;]
      }
    }
  }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;yaml-schema-json-schema-s-chill-cousin&quot;&gt;YAML Schema: JSON Schema's Chill Cousin&lt;/h3&gt;
&lt;p&gt;Here's a cool secret – JSON Schema works perfectly with YAML too! Since YAML is essentially JSON's more readable sibling, you can use the same schema definitions.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;# Your YAML data
name: &quot;Alice&quot;
age: 30
email: &quot;*Emails are not allowed*&quot;
hobbies:
  - &quot;coding&quot;
  - &quot;coffee&quot;
  - &quot;cats&quot;

# Same schema validates both JSON and YAML!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The validation logic doesn't care if your data started as JSON or YAML – it all becomes the same object structure in memory anyway. This means you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use JSON Schema to validate YAML config files&lt;/li&gt;
&lt;li&gt;Accept both JSON and YAML in your APIs&lt;/li&gt;
&lt;li&gt;Keep one schema definition for multiple data formats&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;why-should-you-care-&quot;&gt;Why Should You Care?&lt;/h3&gt;
&lt;h4 id=&quot;1-catch-errors-early&quot;&gt;1. Catch Errors Early&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// This will blow up at runtime somewhere deep in your code
const user = { name: &quot;Bob&quot;, age: &quot;thirty-five&quot; };
calculateInsurance(user.age * 1.2); // NaN strikes again!

// This gets caught immediately with schema validation
// Error: /age: should be number
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;2-self-documenting-apis&quot;&gt;2. Self-Documenting APIs&lt;/h4&gt;
&lt;p&gt;Your schema IS your documentation. No more outdated API docs that say &lt;code&gt;age&lt;/code&gt; is a number when it's actually a string.&lt;/p&gt;
&lt;h4 id=&quot;3-frontend-backend-agreement&quot;&gt;3. Frontend/Backend Agreement&lt;/h4&gt;
&lt;p&gt;When both sides validate against the same schema, miscommunication becomes impossible. It's like having a peace treaty for your data structures.&lt;/p&gt;
&lt;h4 id=&quot;4-better-error-messages&quot;&gt;4. Better Error Messages&lt;/h4&gt;
&lt;p&gt;Instead of &quot;Something went wrong,&quot; you get &quot;Property 'email' should match format 'email'.&quot; Your users (and your future self) will thank you.&lt;/p&gt;
&lt;h3 id=&quot;coming-up-next-&quot;&gt;Coming Up Next...&lt;/h3&gt;
&lt;p&gt;In &lt;strong&gt;Part 2&lt;/strong&gt;, we'll dive deep into implementing JSON Schema validation with AJV, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Setting up AJV in your project&lt;/li&gt;
&lt;li&gt;Advanced validation features&lt;/li&gt;
&lt;li&gt;Custom keywords and error messages&lt;/li&gt;
&lt;li&gt;Performance optimization tips&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Ready to never debug mysterious data issues at 2 AM again? Let's make it happen!&lt;/em&gt; &lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Series Navigation:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Part 1: Understanding the Basics&lt;/strong&gt; ← You are here&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/5928/json-schema-with-ajv-implementation-deep-dive&quot;&gt;Part 2: Implementation with AJV&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/6206/json-schema-in-the-wild-real-world-applications-hal&quot;&gt;Part 3: Real-World Applications &amp;amp; HAL&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/5669/json-schema-your-datas-new-best-friend</guid>
<pubDate>Sat, 27 Sep 2025 11:49:08 +0000</pubDate>
</item>
</channel>
</rss>