Implementing a JSON Schema Validator from Scratch - Week 3
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:
- Recursive architecture
- 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.
-
Public entry point –
validate
The function exposed to users. -
Schema traversal –
validateSchema
Controls the flow, determines which part of the schema to visit, and retrieves the appropriate handler from the keyword handler object. -
Handler signature
(schema: any, instance: any, context: ValidationContext) => voidschema– the current sub‑schema.instance– the data being validated.context– tracks errors, instance and schema locations, evaluated items/properties, and may expose utility functions.
-
Recursive delegation
After a handler finishes its work, it callsvalidateSchemaon any child schemas, passing control back to the traversal logic. -
Completion
Steps 2‑4 repeat until validation finishes. Thevalidatefunction then uses the accumulatedValidationContextto 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.