DEV Community

Mahima Thacker
Mahima Thacker

Posted on

Trying Aontu After Using JSON Schema, Zod, and Pydantic

I use Zod and Pydantic often, so when I started reading about Aontu, I had a basic question: why would I learn another language for defining data?
JSON Schema can define the structure of JSON data and validate it.

So I tried Aontu locally to understand it better.

Starting with a simple config

I created a small service config:

service: close({
  name: string
  host: *localhost | string
  port: *8080 | integer
  tags: [&: string]
})

service: {
  name: api
  port: 3000
  tags: [public http]
}
Enter fullscreen mode Exit fullscreen mode

Aontu returned:

{
  "service": {
    "host": "localhost",
    "name": "api",
    "port": 3000,
    "tags": ["public", "http"]
  }
}

Enter fullscreen mode Exit fullscreen mode

Here, host has a default value of localhost. port must be an integer and has a default value of 8080.

I gave port a value of 3000, so Aontu used that value.

If I change it to something that does not follow the rule, Aontu gives an error.
We already do this with tools like Zod.

For example, I can do this with Zod:

const Service = z.object({
  name: z.string(),
  host: z.string().default("localhost"),
  port: z.number().int().default(8080)
})
Enter fullscreen mode Exit fullscreen mode

So validation alone is not a reason for me to use Aontu.

Rules and data can stay together

This part was more useful to me.

In Aontu, I can write:

deploy: replicas: integer & min(2)
deploy: replicas: 6
Enter fullscreen mode Exit fullscreen mode

The first line gives a rule and second line gives the actual value.
Aontu combines both.

The final value is 6, but the rule that it must be an integer and at least 2 still applies.
If I write:

deploy: replicas: 1

Enter fullscreen mode Exit fullscreen mode

Aontu rejects it.

Aontu calls this unification.

With normal JSON, I cannot do this directly.

I could write something like:

{
  "rules": {
    "replicas": "integer"
  },
  "data": {
    "replicas": 6
  }
}
Enter fullscreen mode Exit fullscreen mode

But JSON does not understand what "rules" means. I still need to write code that reads those rules and applies them.

With JSON Schema, we can define the rules properly, but we normally keep the schema and the actual data in separate files.

For example:

schema.json
config.json

Aontu can keep rules, defaults, and values in the same model.
I liked this part.

Finding where a value came from

I also tried this command:

npx aontu model why "$.service.port" config.aon

Enter fullscreen mode Exit fullscreen mode

It tells you which definitions were used to get the final value.
In my small test, this is not very important because I already know where 3000 came from.

In a production project, it can be more useful.

A project may have:
base config
development config
production config
generated config

A value may come from one file and then get changed somewhere else.

If the final value is:

replicas = 6

Enter fullscreen mode Exit fullscreen mode

I may want to know where 6 came from and which rules were applied to it.
model why can help with that.

Checking rules between services

Aontu can also define relations between different parts of a system.
This is different from checking one JSON object.

For example, imagine these service dependencies:

api -> auth
auth -> payment
payment -> api

Now we have a cycle.

The api service depends on auth, auth depends on payment, and payment connects back to api.

Aontu can define rules for these relations and check for cycles.

We can do the same thing in TypeScript, but we need to load the services, create the graph, detect cycles, and then handle the error.

That is fine if the project has only one or two such rules.

If there are many rules between services, keeping them in the model can save some custom code.

Base config and production config

Another useful case is config for different environments.

For example, a base config can say:

replicas = 2
logLevel = info
Enter fullscreen mode Exit fullscreen mode

Production can change only the value it needs:

replicas = 6

Enter fullscreen mode Exit fullscreen mode

The other values and rules still come from the base config.

This is useful because the production config does not need to repeat everything.

Aontu also checks that the new values still follow the rules from the model.

Again, we can build this with normal config files and code. Aontu gives one system for defining the values, rules, and how they are combined.

How this can help coding agents

A coding agent can change many files in a project.

The code it writes may compile, but that does not always mean the change follows all the rules of the system.

For example, an agent may add a new service dependency and create this:

api -> auth
auth -> payment
payment -> api

The individual files may still look correct.

Aontu can check the full model after the change and report that there is a cycle.

Aontu also has commands such as vet for checking models, and it can expose information through MCP.

This can give an agent a fixed set of rules to check after it changes
something.

I like this idea because I already prefer validation around AI actions instead of only telling the model what to do in a prompt.

Where I would use Aontu

I would not use Aontu for simple request validation.

If I am building a Next.js API and need to check:

{
  "email": "user@example.com",
  "age": 20
}
Enter fullscreen mode Exit fullscreen mode

I would use Zod.
For a FastAPI project, I would use Pydantic.

They are already part of the stack and are easy to understand.

I would look at Aontu when the project has more rules around the full system.

For example:

  • base and production configs
  • defaults coming from different places
  • rules between services
  • dependency cycles
  • several definitions that need to work together
  • coding agents that need to check the same rules

The syntax is still a concern for me.

Some Aontu expressions look different from what we are used to seeing in JSON, so they can take some time to understand.

My first question was whether JSON Schema already does enough.

For simple validation, I still think it does.

After trying Aontu, I found several things that JSON Schema alone does not handle in the same way. Unification can combine rules, defaults, and values. model why can show where a value came from. Relations can check rules between different entities. The same model can also be checked by CI or coding agents.

So in short, Aontu is useful for these features, while for a small validation problem, tools like Zod or Pydantic are enough.

Top comments (0)