惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

H
Help Net Security
T
ThreatConnect
SecWiki News
SecWiki News
F
Future of Privacy Forum
AWS News Blog
AWS News Blog
C
Cisco Blogs
A
Arctic Wolf
Vercel News
Vercel News
The GitHub Blog
The GitHub Blog
Scott Helme
Scott Helme
V
V2EX
博客园 - 叶小钗
阮一峰的网络日志
阮一峰的网络日志
K
Kaspersky official blog
G
Google Developers Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
P
Privacy International News Feed
C
Cyber Attacks, Cyber Crime and Cyber Security
N
News | PayPal Newsroom
Schneier on Security
Schneier on Security
NISL@THU
NISL@THU
Microsoft Azure Blog
Microsoft Azure Blog
量子位
The Hacker News
The Hacker News
Stack Overflow Blog
Stack Overflow Blog
Security Latest
Security Latest
M
Microsoft Research Blog - Microsoft Research
Google Online Security Blog
Google Online Security Blog
博客园_首页
C
CXSECURITY Database RSS Feed - CXSecurity.com
I
InfoQ
Google DeepMind News
Google DeepMind News
Y
Y Combinator Blog
The Cloudflare Blog
Microsoft Security Blog
Microsoft Security Blog
Martin Fowler
Martin Fowler
Cisco Talos Blog
Cisco Talos Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Troy Hunt's Blog
F
Fox-IT International blog
S
Security @ Cisco Blogs
博客园 - 司徒正美
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
C
Comments on: Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
L
LINUX DO - 最新话题
GbyAI
GbyAI
Project Zero
Project Zero
腾讯CDC
T
Tailwind CSS Blog

DEV Community

What I Learned Exploring AI-Generated 3D: A Hands-On Tour of Meshy, Tripo, and Three.js Day 15 - Software Composition Analysis(SCA) Contributing Upstream Instead of Forking: My grape-swagger-rails Story Behind The Badge: How We Built 2,000 Hackable Badges For Temporal Replay Access Control Doesn't Scale Linearly -- Part 3 33x faster than Rust: Why I stopped waiting for my compiler and built my own. I Built My First Production AWS Project as a Career Changer Why Detecting PII Matters More Than Ever Python Tasks How I Started My Cybersecurity Journey as an SQA Engineer 🔐 Why "fancy fonts" in Discord and Instagram bios turn into boxes ☁️ GKE private cluster setup — common mistakes and how to avoid them I Thought a Username Didn’t Matter… Until I Saw How Much People Care About It Claude for Small Business: 382K Day-One Buyer's Guide I Built a Diagnostic Toolkit for PyTorch Because I Was Tired of Guessing Why Models Fail How I Built an AI-Powered Incident RCA Platform with LangGraph and RAG The Paywall Was a Painted Door Sonnet hallucinated. My agent stored it as fact. How React-Style Time-Slicing Keeps UIs Responsive 这个 Princeton 开源项目让 AI 自己修 Bug,19K Stars 但 90% 的人只用了 1% 功能 🔥 SWE-agent's 5 Hidden Uses Nobody Told You About 🔥 Decompiling Serial Number U-36: Python TERCOM Reconstruction, Cryptographic Logistical Forensics, and Swarm Consensus Fault Tolerance Microservices Patterns You Cannot Outrun a Wave I Fired My Entire Node.js Stack — Rust Rebuilt It in 3 Weeks (The Ugly Truth) BoxAgnts Introduction (2) — AI Agent Toolbox Cursor 3 ships parallel AI agents. Here is the multi-agent workflow that actually works. Prisma-7 A Complete Beginners Guide (With Free Cloud Database!) Akses HDD Rumah dari Laptop Kantor Pakai Tailscale + SMB (Tanpa VPN Ribet) Content Pipeline in MonoGame: Why I Don't Use It Debug Log #1 — The Pipeline That Looked Broken Data Structures in JavaScript: When to Use What (2026) BGP Route Flap Damping: A Solution or a New Problem? First look at AWS DevOps Agent The Next Big “Cult App” Probably Isn’t Another Social Media Platform From Template to Production-Shaped: An AI-Native Dev Flow for Go Side Projects Idempotency Keys: The API Pattern That Saves You From Duplicate Payments and Phantom Records Everyone's Building Jarvis. Nobody's Even Close. The Moment the Jaeger Tracer Exhausted Itself and What We Switched To How to Fix Tool-Use Loops in Autonomous Coding Agents Months of self-testing: Citations shine, other features remain unproven. Claude Code for Canary Deployments: How I Ship to 1% of Users Before Breaking Everything Your recurring scraper is re-downloading data that didn't change. Here's the 15-line fix (conditional GET) 20 Years of GPUs in Numbers: How FLOPS & TDP Grew, and Who Led the NVIDIA vs AMD Race (open dataset, 13.5k GPUs) Espressif Reveals CoreBoard and Korvo Dev Kits for ESP32-S31 Composable Abstraction Layer: o pattern que faltava entre Pinia e seus componentes Vue Your GitHub Actions Logs Are Leaking LLM Keys and Your SIEM Isn't Catching It Solving Complex Logic with Claude and Research Papers Building TheEpicBook: A Deep Dive into a Node.js Monolithic Web Application Haber yazilimi, haber scripti, haber sistemi: ayni urun, uc ayri arama niyeti Predicting Blood Glucose Fluctuations: Building a Transformer-based CGM Forecaster with PyTorch & InfluxDB Pre-task hooks: the one-line wire-up that gives your Hono agent shared memory Concurrent writes to a shared agent memory: what we shipped, what we punted on Building a Production Serverless URL Shortener on AWS — 21 Articles, Every Test Run for Real My CKA Cheat Sheet: Commands, Aliases, and Documentation Tricks I Used During the Exam Frontend Engineering Beyond Pixels: The Architecture of Digital Accessibility VLA or IL? A Controlled Dataset for Testing Whether Finetuning Turns Your VLA into a Fancy Imitation Learner Fabric AI Functions Turn GenAI Into a Data Pipeline Step Proximate vs Ultimate: The Bug Is Never Just the Bug The Treasure Hunt Engine That Broke Before the Traffic Did Reset Windows Update: The Definitive MSP Guide to RWU Your Resume Was Never Built for This AI Writes 46% of Code Now: What Snap's Layoffs Mean for Developers in 2026 From Chatbot to Agent — Tool Calling with NVIDIA NIM Fatigue and Fracture Mechanics: Why Parts Break Below Their Yield Strength I built a token-level debugger for comparing two LLMs VCP-Virtual Private Cloud Embedding sing-box in an iOS messenger to bypass Russian DPI (no VPN) Microsoft Copilot just exfiltrated a company's files. The attack was one email. Here's the mechanism. RAG 시스템 실전 구축 (v42) copilot cloud agent is becoming an automation api Cx Dev Log — 2026-04-23 Why Tesla Is Becoming the AI Enterprise Case Study Every Leader Should Understand ORA-00214 오류 원인과 해결 방법 완벽 가이드 SpecAgnt v2.0: The Agent Lifecycle Framework for AI-Native Engineering Optimizing Signal Latency and Weight Allocations in Algorithmic Pipelines SSH Under the Hood: Protocols, Mechanisms, and the Full Technical Story دليل بوابات الدفع للتاجر العربي في 2026 (وكيف تختار المناسبة لمتجرك) Cómo Mi Configuración de Docker Me Salvó de un Ataque de Supply Chain (Y Por Qué la Tuya Debería Hacerlo También) How My Docker Setup Saved Me From a Supply Chain Attack (And Why Yours Should Too) Astro: The epitome of SEO Technical Update I Gave My AI Agent the Ability to Research Before It Writes — Here’s What Changed Kubernetes sem Cloud Provider (Parte 2): Criando Operators em Go para automação e self-service de plataforma AI Memory Needs an Authority Policy, Not Just More Context You've done tutorial after tutorial. Your GitHub is still empty. (Free 1‑page PDF, no signup) TypeScript 7.0: The Go Compiler That Makes TS 10x Faster Connecting Wallets the Right Way: wagmi v2 and EIP-6963 The 5-Layer Architecture Every Production Multi-Agent System Needs (And Why Most Skip Layers 4 and 5) CSS Scroll-Driven Animations: No JavaScript Required Vite 8 + Rolldown: Rust-Powered Builds That Are 10–30x Faster Core Architectural Components of Azure My Skills How I Use AI as a Senior Engineer Construí um motor ATS determinístico porque estava cansado de adivinhar por que meu currículo era rejeitado SCS-Lab1 — CloudTrail: Trail + S3 + KMS + Log Validation LuisCore MCP server — daily syndication · 2026-05-25 Cursor vs JetBrains Rider for C#/.NET in 2026: which to pay for I built a local-first movie recommender with Corrective-RAG (cited explanations, hybrid retrieval, runs entirely on Ollama) Scaling to 1 Million Users : Load Balancing & Caching Strategies
JSON Schema in 10 Minutes — Validation, Types & Real Examples
Anh Quân Ngu · 2026-05-26 · via DEV Community

Two years ago I shipped a webhook handler without input validation. A partner started sending us a slightly malformed payload (an extra field, one missing required field) and our worker silently processed garbage into the database for three days before anyone noticed. By the time I traced it, we had 12,000 corrupt rows and a very awkward customer call.

I learned JSON Schema the next week. This post is the cheat sheet I wish someone had handed me on day one — the keywords I actually use, the gotchas that bit me again later, and the honest comparison with OpenAPI and TypeScript types.

The seven types you'll use

Every JSON value is one of seven types: string, number, integer, boolean, object, array, or null. The integer type is a JSON Schema convenience (raw JSON only has number) but the schema layer enforces "no decimal places." A minimal schema:

{
  "type": "string"
}

Enter fullscreen mode Exit fullscreen mode

That validates any string and rejects everything else. You can also accept a union:

{
  "type": ["string", "null"]
}

Enter fullscreen mode Exit fullscreen mode

Useful for optional fields you want to keep present in the payload rather than omitting. Before I write more than a one-line schema I usually paste a sample payload into a JSON formatter to see the actual shape pretty-printed. Type errors almost always come from misreading the structure.

Objects, required, and the additionalProperties trap

Most real validation work happens on objects. The three keywords you use every day are properties, required, and additionalProperties:

{
  "type": "object",
  "properties": {
    "email":    { "type": "string" },
    "age":      { "type": "integer" },
    "verified": { "type": "boolean" }
  },
  "required": ["email"],
  "additionalProperties": false
}

Enter fullscreen mode Exit fullscreen mode

Three gotchas to internalize. First, properties describes each field but does NOT make any of them required. Without the required array, every property is optional. Second, required is a separate list of property names that must be present (presence only, you still need type to validate the value). Third, additionalProperties: false rejects any property not listed. Without this line, the schema accepts arbitrary extra fields silently. This was the bug that hit me — the partner was sending email_address instead of email, and without additionalProperties: false my schema accepted it as "no email + an unknown field."

Set additionalProperties: false by default. Remove it only when you genuinely want a free-form object. For maps with arbitrary keys but a known value type, use it as a schema instead of a boolean:

{
  "type": "object",
  "additionalProperties": { "type": "number" }
}

Enter fullscreen mode Exit fullscreen mode

That validates any object where every value is a number. Perfect for price lookup tables, feature-flag percentages, or anything keyed dynamically.

String validation: minLength, pattern, format, enum

Real string validation goes beyond "is it a string." The keywords that earn their keep:

  • minLength / maxLength, integer bounds on UTF-16 code units (not bytes, not graphemes)
  • pattern, ECMA-262 regex the string must match somewhere (use ^...$ anchors for a full match)
  • format, named formats like email, uri, date, date-time, uuid, ipv4, ipv6
  • enum, a fixed list of allowed values (works for any type)
  • const, a single allowed value (equivalent to a one-item enum)

A practical username field:

{
  "type": "string",
  "minLength": 3,
  "maxLength": 20,
  "pattern": "^[a-zA-Z0-9_]+$"
}

Enter fullscreen mode Exit fullscreen mode

One gotcha that cost me a day: format is informational by default in older drafts. You must enable format assertion in your validator. Ajv requires ajv-formats. Python jsonschema needs format_checker. Without it, "format": "email" documents intent but does not actually reject invalid emails. See the JSON Schema spec for format for the full list and the assertion behavior per draft.

Number validation: minimum, maximum, multipleOf

For numbers and integers, the validation keywords are arithmetic:

  • minimum / maximum, inclusive bounds
  • exclusiveMinimum / exclusiveMaximum, exclusive bounds (in Draft 2020-12 these take a number, in older drafts they took a boolean)
  • multipleOf, the value must be a multiple of this number

Validating a percentage that must be 0 to 100 in 0.01 increments:

{
  "type": "number",
  "minimum": 0,
  "maximum": 100,
  "multipleOf": 0.01
}

Enter fullscreen mode Exit fullscreen mode

multipleOf has a floating-point trap I keep getting wrong. 0.1 is not exactly representable in IEEE 754, so { "multipleOf": 0.1 } will sometimes reject values you expect to pass. For money, I now store and validate as integer cents ({ "type": "integer", "minimum": 0 }). It is the same precision argument behind storing prices in the smallest currency unit everywhere else in the stack.

Array validation: items, minItems, uniqueItems

For arrays the workhorses are items (schema applied to every element), minItems / maxItems (length bounds), and uniqueItems (rejects duplicates by deep equality). A list of unique tags:

{
  "type": "array",
  "items": { "type": "string", "minLength": 1 },
  "minItems": 1,
  "maxItems": 10,
  "uniqueItems": true
}

Enter fullscreen mode Exit fullscreen mode

For positional tuples where each index has a different schema, use prefixItems in Draft 2020-12 or items as an array in older drafts. A coordinate pair where index 0 is longitude and index 1 is latitude:

{
  "type": "array",
  "prefixItems": [
    { "type": "number", "minimum": -180, "maximum": 180 },
    { "type": "number", "minimum":  -90, "maximum":  90 }
  ],
  "items": false
}

Enter fullscreen mode Exit fullscreen mode

The trailing "items": false rejects any extra elements beyond the two declared positions. The array equivalent of additionalProperties: false.

Schema composition: $ref, allOf, oneOf, anyOf

Once your schemas grow past a single page, you will want to break them up and combine them. JSON Schema has four composition keywords:

  • $ref, reuse another schema by JSON Pointer (e.g., "#/$defs/address" or an external URL)
  • allOf, data must validate against every subschema (intersection / mixin)
  • anyOf, data must validate against at least one (union, OK if multiple match)
  • oneOf, data must validate against exactly one (XOR, rejects if zero or multiple match)

A reusable address schema referenced from two parents:

{
  "$defs": {
    "address": {
      "type": "object",
      "properties": {
        "street":  { "type": "string" },
        "city":    { "type": "string" },
        "country": { "type": "string", "minLength": 2, "maxLength": 2 }
      },
      "required": ["street", "city", "country"]
    }
  },
  "type": "object",
  "properties": {
    "shipping": { "$ref": "#/$defs/address" },
    "billing":  { "$ref": "#/$defs/address" }
  }
}

Enter fullscreen mode Exit fullscreen mode

For discriminated unions (event types, message kinds), oneOf with a const discriminator is the standard pattern:

{
  "oneOf": [
    { "type": "object", "properties": { "kind": { "const": "email" },
        "to": { "type": "string", "format": "email" } }, "required": ["kind", "to"] },
    { "type": "object", "properties": { "kind": { "const": "sms" },
        "phone": { "type": "string", "pattern": "^\\+[1-9]\\d{1,14}$" } }, "required": ["kind", "phone"] }
  ]
}

Enter fullscreen mode Exit fullscreen mode

A real signup schema

Putting every keyword together, here is roughly the schema I now use for a signup endpoint:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "SignupRequest",
  "type": "object",
  "properties": {
    "email":     { "type": "string", "format": "email", "maxLength": 254 },
    "password":  { "type": "string", "minLength": 12, "maxLength": 128 },
    "username":  { "type": "string", "pattern": "^[a-zA-Z0-9_]{3,20}$" },
    "age":       { "type": "integer", "minimum": 13, "maximum": 120 },
    "country":   { "type": "string", "enum": ["US", "UK", "CA", "AU"] },
    "newsletter":{ "type": "boolean", "default": false },
    "referrals": { "type": "array", "items": { "type": "string", "format": "email" },
                   "maxItems": 5, "uniqueItems": true }
  },
  "required": ["email", "password", "username", "age", "country"],
  "additionalProperties": false
}

Enter fullscreen mode Exit fullscreen mode

It enforces the email format with the 254-char maximum from RFC 5321, a 12-character minimum password from NIST SP 800-63B, a regex-validated username, an integer age within plausible bounds, a closed enum of supported countries, an optional boolean with a documented default, and an optional referral list capped at 5 unique emails. The trailing additionalProperties: false is the line that would have saved me three days and 12,000 rows two years ago.

Tooling: Ajv (Node) and jsonschema (Python)

Declare which draft you target with the $schema keyword at the root. The two production-grade validators I reach for:

  • Ajv for Node.js and browser, the fastest JS validator, supports Draft 2020-12. Install ajv and ajv-formats together if you use format. Compile schemas once at startup with const validate = ajv.compile(schema), then call validate(data) on every request. This is 10 to 100 times faster than recompiling per call.
  • jsonschema for Python, the reference Python validator. Use Draft202012Validator(schema).validate(data) or iterate .iter_errors(data) to surface all errors at once instead of failing on the first.

For quick iteration without writing code, I usually paste the schema and a sample payload into a JSON formatter to confirm both parse, then run them through a browser-based validator. When debugging an unexpected failure, a diff checker helps me compare a failing payload against a known-good payload to spot the offending field.

JSON Schema vs OpenAPI vs TypeScript

These three describe data shapes but solve different problems:

  • TypeScript types are compile-time only. They vanish at runtime, so a malformed API payload will silently corrupt your program if you trust the type without validating. Great for developer ergonomics, useless for runtime safety.
  • JSON Schema is runtime validation that works in any language. Use it at API boundaries, for config files, for database documents, and for any cross-language data contract. A single schema can drive validation in your Node frontend, Python backend, and Go worker without rewriting.
  • OpenAPI (formerly Swagger) wraps JSON Schema inside an API description. It adds endpoints, methods, status codes, authentication, examples, and tooling for client SDK generation. Use it when you are describing an HTTP API and want documentation, client codegen, and validation in one document.

The stack I default to now: write the JSON Schema as source of truth, generate TypeScript types from it with json-schema-to-typescript, and embed the same schema inside an OpenAPI spec for HTTP routes. One source, three outputs, no drift.

The mistakes I kept making

1. Forgetting additionalProperties: false

The original bug. Without it, any extra field passes validation. A client typo like { "emial": "x@y.com" } validates as "no email present plus an unknown field" instead of the clean error you want. Add it by default.

2. Confusing required with type

Listing a property under properties does NOT make it required. You must also add it to the required array. Conversely, required only checks presence. A wrong-type field still fails, but on the type check, not the required check.

3. Using format without enabling assertion

In Ajv you must require('ajv-formats')(ajv). In Python jsonschema pass format_checker=FormatChecker(). Without this, format: email is metadata only and accepts any string. I burned half a day on this one.

4. oneOf where anyOf is correct

oneOf rejects data that matches more than one subschema. If your subschemas overlap (a value that is both a positive integer and a multiple of 5), oneOf rejects. Use it only for genuinely disjoint cases like discriminated unions.

5. multipleOf with floats

IEEE 754 cannot exactly represent 0.1. { "multipleOf": 0.1 } will reject values you expect to pass. Use integer units (cents, basis points) instead.

6. Recompiling schemas on every request

Ajv's compile() is expensive. The compiled validator is fast. Compile once at module load, store the function, reuse it.

Closing thought

JSON Schema looks verbose at first. Often the schema is longer than the data. That is the point. Every constraint you encode is one bug you cannot ship. Start with your top three API endpoints, then your config files, then your cross-service messages. Within a sprint you will catch at least one bug that would have made it to production.

If you want a sandbox, try the JSON Schema Reference Tutorial and an online validator like jsonschemavalidator.net. And if you ever debug a pattern validation that is misbehaving, a regex tester is faster than guessing.


Originally published at calculators.im.