jsonjsonschemaajv

Using complex schema with AJV


I'd like to reuse object properties in Ajv schema, but it claims that some part cannot be resolved:

const Ajv = require("ajv@8.11.0").default

const ajv = new Ajv({
  strictSchema: false,
  loadSchema: async (uri) => {
    console.log(uri);
    switch (uri) {
        case "main":
            return {
                entity: { $ref: "entity" },
                request: { $ref: "request" }
            }
        case "entity":
            return {
              type: "object",
              properties: {
                id: {type: "string"}
              },
            }
        case "request":
            return {
              type: "object",
              properties: {
                id: {$ref: "main#/entity/properties/id"}
              },
            }
    }
  },
})

const validate = await ajv.compileAsync({
    $ref: "main#/request"
})

validate({
 id: "123"
})

produces error: Error: AnySchema main is loaded but main#/entity/properties/id cannot be resolved

while replacing main#/entity/properties/id with entity#/properties/id makes it work. What I am missing?

Live code: https://runkit.com/desofto/64a40e034daec80008a495b5


Solution

  • Your main schema is precisely this:

    {
      entity: { $ref: "entity" },
      request: { $ref: "request" }
    }
    

    The JSON Pointer #/entity/properties/id doesn't resolve to a location within main.

    It looks like you're expecting the JSON Pointer to resolve the { $ref: "entity" } at main#/entity, but JSON Pointers don't resolve references.


    The code you posted may just be an example to illustrate your point, but given that entity#/properties/id is just

    { "type": "string" }
    

    it may just make more sense to repeat that in your request object rather than trying to reference into entity to get it. Typically references don't extract subschemas that way. They can, but it's not advised to do so.

    If you'd really like to reference it, I suggest you extract that out into a new schema and reference it from both places.

    Another option is to compose all of these into a single schema and make use of $defs to house any reusable pieces.