Implementing a JSON Schema Validator from Scratch - Week 3

Published: (February 6, 2026 at 11:15 AM EST)
3 min read
Source: Dev.to

Source: Dev.to

Weekly Update – Foundations and Architecture

I was pretty busy this week, so I didn’t have time to do much, but I made some foundational decisions and ran into a few minor issues.

TypeScript Learning Curve

This is my first time using TypeScript. I expected it to be “just JavaScript with static types,” but I quickly discovered that I also need to predetermine the static structure of objects. Writing the foundational part of the project became cumbersome because every variable showed red squiggly lines until I defined its type.

Although this upfront effort felt annoying, I now see how it will improve the development experience later on.

Early‑Stage Challenges

Starting a new project is always hard:

  • There’s no existing code to attach to or extend.
  • The broad vision of the system can feel intimidating.
  • You constantly shift between a high‑level architectural view and a low‑level implementation view.
  • Early decisions have long‑term consequences, making later changes difficult.

After a bit of feeling lost, things clicked once I did more research (see the next section).

Architectural Decisions

Before reading the JSON Schema specifications, the maintainers advised me to focus on the system’s architecture so that adding support for new JSON Schema drafts would be straightforward.

Keyword Handler Objects

I decided to create keyword handler objects that map keyword names to functions implementing those keywords. Each supported draft gets its own handler object.

While implementing the draft 2020‑12 handler, I hit a roadblock: I wasn’t sure how the handler functions should be integrated with the rest of the system. Research revealed two common validator architectures:

  1. Recursive architecture
  2. Visitor architecture

The visitor architecture provided the missing pieces for my approach, so I adopted it.

Visitor‑Based Validator Architecture

Both architectures are recursive, but they differ in how recursion is applied.

  1. Public entry pointvalidate
    The function exposed to users.

  2. Schema traversalvalidateSchema
    Controls the flow, determines which part of the schema to visit, and retrieves the appropriate handler from the keyword handler object.

  3. Handler signature

    (schema: any, instance: any, context: ValidationContext) => void
    • schema – the current sub‑schema.
    • instance – the data being validated.
    • context – tracks errors, instance and schema locations, evaluated items/properties, and may expose utility functions.
  4. Recursive delegation
    After a handler finishes its work, it calls validateSchema on any child schemas, passing control back to the traversal logic.

  5. Completion
    Steps 2‑4 repeat until validation finishes. The validate function then uses the accumulated ValidationContext to produce the final validation result.

This design cleanly separates flow control from keyword implementation, making it easy to add, update, or remove keywords.

Implementation Notes

  • I have been using LLMs to assist with the implementation, but every piece of code in the repository has been either written by me or thoroughly reviewed.
  • The source code is available on GitHub: (replace with the actual URL).

End of week 3 update.

Back to Blog

Related posts

Read more »